mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-01 20:32:27 +00:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53e784094a | ||
|
|
0e92e48376 | ||
|
|
4c9943ac38 | ||
|
|
4bd0fd145d | ||
|
|
01ae82eb28 | ||
|
|
e21eae0933 | ||
|
|
2f047c50dc | ||
|
|
e397e1a80c | ||
|
|
b09e86352f | ||
|
|
8687b69167 | ||
|
|
6c5468d904 | ||
|
|
d6b0f8d4c5 | ||
|
|
a8cd4ff12c | ||
|
|
8509efc8af | ||
|
|
23a0053dad | ||
|
|
5849980092 | ||
|
|
e81fc2b254 | ||
|
|
2ad869a036 | ||
|
|
7a8c84e990 | ||
|
|
0de01e93dd | ||
|
|
6fa93e5b44 | ||
|
|
2144ec1e3f | ||
|
|
68d2f60ace | ||
|
|
3d671ae71f | ||
|
|
8ed47f9d99 | ||
|
|
b50f858556 | ||
|
|
5de1a74429 | ||
|
|
2063ad467d | ||
|
|
679acbae16 | ||
|
|
a5b706a99e | ||
|
|
542e61357e | ||
|
|
3743602c67 | ||
|
|
ee651840bf | ||
|
|
0765c60d77 | ||
|
|
7fa17322a1 | ||
|
|
cfa3047330 | ||
|
|
9e033deb7b | ||
|
|
f6c914f6f0 | ||
|
|
06ff02c2a5 | ||
|
|
63fd263890 | ||
|
|
94f3d4b279 | ||
|
|
fdc96044ad | ||
|
|
91487ffc94 | ||
|
|
7a57dcc08a | ||
|
|
913a54713d | ||
|
|
a7015b0d1a |
@@ -3,6 +3,7 @@ build/*
|
||||
|
||||
# Third-party source code which we (1) do not want to modify or (2) try to
|
||||
# modify as little as possible.
|
||||
flow-typed/*
|
||||
libs/*
|
||||
|
||||
# ESLint will by default ignore its own configuration file. However, there does
|
||||
|
||||
@@ -4,7 +4,10 @@ module.exports = {
|
||||
'commonjs': true,
|
||||
'es6': true
|
||||
},
|
||||
'extends': 'eslint:recommended',
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
'plugin:flowtype/recommended'
|
||||
],
|
||||
'globals': {
|
||||
// The globals that (1) are accessed but not defined within many of our
|
||||
// files, (2) are certainly defined, and (3) we would like to use
|
||||
@@ -12,12 +15,16 @@ module.exports = {
|
||||
// files.
|
||||
'__filename': false
|
||||
},
|
||||
'parser': 'babel-eslint',
|
||||
'parserOptions': {
|
||||
'ecmaFeatures': {
|
||||
'experimentalObjectRestSpread': true
|
||||
},
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'plugins': [
|
||||
'flowtype'
|
||||
],
|
||||
'rules': {
|
||||
'new-cap': [
|
||||
'error',
|
||||
|
||||
50
.flowconfig
Normal file
50
.flowconfig
Normal file
@@ -0,0 +1,50 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
.*/Libraries/react-native/ReactNative.js
|
||||
|
||||
; Ignore packages in node_modules which we (i.e. the jitsi-meet project) have
|
||||
; seen to cause errors and we have chosen not to fix.
|
||||
.*/node_modules/babel-core/.*
|
||||
.*/node_modules/bower/.*
|
||||
.*/node_modules/jsonlint/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow
|
||||
flow/
|
||||
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
experimental.strict_type_args=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'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-7]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-7]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[version]
|
||||
^0.37.0
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -47,6 +47,7 @@ local.properties
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
#
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
# The following do not need to be checked because they do not represent JS
|
||||
# source code.
|
||||
build/
|
||||
debian/
|
||||
libs/
|
||||
node_modules/
|
||||
|
||||
# The following are checked by ESLint which supersedes JSHint.
|
||||
flow-typed/
|
||||
react/
|
||||
|
||||
analytics.js
|
||||
webpack.config.babel.js
|
||||
|
||||
15
README.md
15
README.md
@@ -1,5 +1,5 @@
|
||||
Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
====
|
||||
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, scalable video conferences. You can see [Jitsi Meet in action](http://youtu.be/7vFUVClsNh0) here at the session #482 of the VoIP Users Conference.
|
||||
|
||||
You can also try it out yourself at https://meet.jit.si .
|
||||
@@ -16,6 +16,7 @@ For other systems, or if you wish to install all components manually, see the [d
|
||||
|
||||
You can download Debian/Ubuntu binaries:
|
||||
* [stable](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianStableRepository))
|
||||
* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianTestingRepository))
|
||||
* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianNightlyRepository))
|
||||
|
||||
## Building the sources
|
||||
@@ -34,7 +35,7 @@ To build the Jitsi Meet application, just type
|
||||
make
|
||||
```
|
||||
|
||||
## Working with the library sources(lib-jitsi-meet).
|
||||
## Working with the library sources (lib-jitsi-meet)
|
||||
|
||||
By default the library is build from its git repository sources. The default dependency path in package.json is :
|
||||
```json
|
||||
@@ -81,6 +82,14 @@ npm unlink lib-jitsi-meet
|
||||
npm install
|
||||
```
|
||||
|
||||
## Embedding in external applications
|
||||
|
||||
Jitsi Meet provides a very flexible way of embedding it in external applications by using the [Jitsi Meet API](doc/api.md).
|
||||
|
||||
## Mobile app
|
||||
Jitsi Meet is also available as a React Native application for Android and iOS.
|
||||
Instructions on how to build it can be found [here](doc/mobile.md).
|
||||
|
||||
## Discuss
|
||||
Please use the [Jitsi dev mailing list](http://lists.jitsi.org/pipermail/dev/) to discuss feature requests before opening an issue on Github.
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ if (project.hasProperty('JITSI_SIGNING')
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-immersive')
|
||||
compile project(':react-native-keep-awake')
|
||||
compile project(':react-native-vector-icons')
|
||||
compile project(':react-native-webrtc')
|
||||
|
||||
@@ -38,11 +38,19 @@
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:host="beta.hipchat.me" android:scheme="https" />
|
||||
<data android:host="beta.meet.jit.si" android:scheme="https" />
|
||||
<data android:host="chaos.hipchat.me" android:scheme="https" />
|
||||
<data android:host="enso.me" android:scheme="https" />
|
||||
<data android:host="hipchat.me" android:scheme="https" />
|
||||
<data android:host="meet.jit.si" android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="org.jitsi.meet" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
@@ -16,7 +16,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean getUseDeveloperSupport() {
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.oblador.vectoricons.VectorIconsPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new org.jitsi.meet.audiomode.AudioModePackage()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
rootProject.name = 'jitsi-meet-react'
|
||||
|
||||
include ':app'
|
||||
include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
|
||||
include ':react-native-vector-icons'
|
||||
|
||||
@@ -10,7 +10,7 @@ import Recorder from './modules/recorder/Recorder';
|
||||
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
|
||||
import {reportError} from './modules/util/helpers';
|
||||
import { reload, reportError } from './modules/util/helpers';
|
||||
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
@@ -203,10 +203,8 @@ function maybeRedirectToWelcomePage(options) {
|
||||
// save whether current user is guest or not, before navigating
|
||||
// to close page
|
||||
window.sessionStorage.setItem('guest', APP.tokenData.isGuest);
|
||||
if (options.feedbackSubmitted)
|
||||
window.location.pathname = "close.html";
|
||||
else
|
||||
window.location.pathname = "close2.html";
|
||||
assignWindowLocationPathname(
|
||||
options.feedbackSubmitted ? "close.html" : "close2.html");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -219,11 +217,42 @@ function maybeRedirectToWelcomePage(options) {
|
||||
if (config.enableWelcomePage) {
|
||||
setTimeout(() => {
|
||||
APP.settings.setWelcomePageEnabled(true);
|
||||
window.location.pathname = "/";
|
||||
assignWindowLocationPathname('./');
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a specific pathname to window.location.pathname taking into account
|
||||
* the context root of the Web app.
|
||||
*
|
||||
* @param {string} pathname - The pathname to assign to
|
||||
* window.location.pathname. If the specified pathname is relative, the context
|
||||
* root of the Web app will be prepended to the specified pathname before
|
||||
* assigning it to window.location.pathname.
|
||||
* @return {void}
|
||||
*/
|
||||
function assignWindowLocationPathname(pathname) {
|
||||
const windowLocation = window.location;
|
||||
|
||||
if (!pathname.startsWith('/')) {
|
||||
// XXX To support a deployment in a sub-directory, assume that the room
|
||||
// (name) is the last non-directory component of the path (name).
|
||||
let contextRoot = windowLocation.pathname;
|
||||
|
||||
contextRoot
|
||||
= contextRoot.substring(0, contextRoot.lastIndexOf('/') + 1);
|
||||
|
||||
// A pathname equal to ./ specifies the current directory. It will be
|
||||
// fine but pointless to include it because contextRoot is the current
|
||||
// directory.
|
||||
pathname.startsWith('./') && (pathname = pathname.substring(2));
|
||||
pathname = contextRoot + pathname;
|
||||
}
|
||||
|
||||
windowLocation.pathname = pathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create local tracks of specified types.
|
||||
* @param {Object} options
|
||||
@@ -323,7 +352,7 @@ class ConferenceConnector {
|
||||
case ConferenceErrors.NOT_ALLOWED_ERROR:
|
||||
{
|
||||
// let's show some auth not allowed page
|
||||
window.location.pathname = "authError.html";
|
||||
assignWindowLocationPathname('authError.html');
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -388,7 +417,7 @@ class ConferenceConnector {
|
||||
APP.UI.notifyMaxUsersLimitReached();
|
||||
break;
|
||||
case ConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS:
|
||||
window.location.reload();
|
||||
reload();
|
||||
break;
|
||||
default:
|
||||
this._handleConferenceFailed(err, ...params);
|
||||
@@ -1445,7 +1474,7 @@ export default {
|
||||
APP.UI.addListener(UIEvents.LOGOUT, () => {
|
||||
AuthHandler.logout(room).then(url => {
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
UIUtil.redirect(url);
|
||||
} else {
|
||||
this.hangup(true);
|
||||
}
|
||||
|
||||
@@ -78,5 +78,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// edit their profile.
|
||||
enableUserRolesBasedOnToken: false,
|
||||
// Suspending video might cause problems with audio playback. Disabling until these are fixed.
|
||||
disableSuspendVideo: true
|
||||
disableSuspendVideo: true,
|
||||
// disables or enables RTX (RFC 4588).
|
||||
disableRtx: true
|
||||
};
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-mic-camera-combined:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.icon-feedback:before {
|
||||
content: "\e91d";
|
||||
}
|
||||
|
||||
212
doc/api.md
212
doc/api.md
@@ -1,17 +1,17 @@
|
||||
Jitsi Meet API
|
||||
============
|
||||
# Jitsi Meet API
|
||||
|
||||
You can use Jitsi Meet API to embed Jitsi Meet in to your application.
|
||||
You can use the Jitsi Meet API to embed Jitsi Meet in to your application.
|
||||
|
||||
Installation
|
||||
==========
|
||||
## Installation
|
||||
|
||||
To embed Jitsi Meet in your application you need to add the Jitsi Meet API library:
|
||||
|
||||
To embed Jitsi Meet in your application you need to add Jitsi Meet API library
|
||||
```javascript
|
||||
<script src="https://meet.jit.si/external_api.js"></script>
|
||||
```
|
||||
|
||||
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object
|
||||
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
var domain = "meet.jit.si";
|
||||
@@ -21,169 +21,174 @@ The next step for embedding Jitsi Meet is to create the Jitsi Meet API object
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height);
|
||||
</script>
|
||||
```
|
||||
You can paste that lines in your html code where you want to be placed the Jitsi Meet conference
|
||||
or you can specify the parent HTML element for the Jitsi Meet conference in the JitsiMeetExternalAPI
|
||||
constructor.
|
||||
|
||||
You can use the above lines to indicate where exactly you want the Jitsi Meet conference to be placed in your HTML code,
|
||||
or you can specify the parent HTML element for the Jitsi Meet conference in the `JitsiMeetExternalAPI`
|
||||
constructor:
|
||||
|
||||
```javascript
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement);
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement);
|
||||
```
|
||||
If you don't specify room the user will enter in new conference with random room name.
|
||||
|
||||
You can overwrite options set in config.js and interface_config.js. For example, to enable the film-strip-only interface mode and disable simulcast, you can use:
|
||||
If you don't specify the room the user will enter in new conference with a random room name.
|
||||
|
||||
You can overwrite options set in [config.js]() and [interface_config.js](). For example, to enable the film-strip-only interface mode and disable simulcast, you can use:
|
||||
|
||||
```javascript
|
||||
var configOverwrite = {enableSimulcast: false};
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, configOverwrite, interfaceConfigOverwrite);
|
||||
var configOverwrite = {disableSimulcast: true};
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, configOverwrite, interfaceConfigOverwrite);
|
||||
```
|
||||
|
||||
Controlling embedded Jitsi Meet Conference
|
||||
=========
|
||||
## Controlling the embedded Jitsi Meet Conference
|
||||
|
||||
You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object.
|
||||
You can control the embedded Jitsi Meet conference using the `JitsiMeetExternalAPI` object by using `executeCommand`:
|
||||
|
||||
You can send command to Jitsi Meet conference using ```executeCommand```.
|
||||
```
|
||||
```javascript
|
||||
api.executeCommand(command, ...arguments)
|
||||
```
|
||||
The ```command``` parameter is String object with the name of the command.
|
||||
Currently we support the following commands:
|
||||
|
||||
The `command` parameter is String object with the name of the command. The following commands are currently supported:
|
||||
|
||||
* **displayName** - sets the display name of the local participant. This command requires one argument -
|
||||
the new display name to be set
|
||||
```
|
||||
* **displayName** - Sets the display name of the local participant. This command requires one argument - the new display name to be set.
|
||||
```javascript
|
||||
api.executeCommand('displayName', 'New Nickname');
|
||||
```
|
||||
* **toggleAudio** - mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```
|
||||
|
||||
* **toggleAudio** - Mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleAudio')
|
||||
```
|
||||
* **toggleVideo** - mutes / unmutes the video for the local participant. No arguments are required.
|
||||
```
|
||||
|
||||
* **toggleVideo** - Mutes / unmutes the video for the local participant. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleVideo')
|
||||
```
|
||||
* **toggleFilmStrip** - hides / shows the film strip. No arguments are required.
|
||||
```
|
||||
api.executeCommand('filmStrip')
|
||||
```
|
||||
* **toggleChat** - hides / shows the chat. No arguments are required.
|
||||
|
||||
* **toggleFilmStrip** - Hides / shows the film strip. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleFilmStrip')
|
||||
```
|
||||
|
||||
* **toggleChat** - Hides / shows the chat. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleChat')
|
||||
```
|
||||
* **toggleContactList** - hides / shows the contact list. No arguments are required.
|
||||
```
|
||||
|
||||
* **toggleContactList** - Hides / shows the contact list. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleContactList')
|
||||
```
|
||||
|
||||
* **toggleShareScreen** - starts / stops the screen sharing. No arguments are required.
|
||||
```
|
||||
* **toggleShareScreen** - Starts / stops screen sharing. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleShareScreen')
|
||||
```
|
||||
|
||||
* **hangup** - Hangups the call. No arguments are required.
|
||||
```
|
||||
```javascript
|
||||
api.executeCommand('hangup')
|
||||
```
|
||||
|
||||
* **email** - Hangups the call. No arguments are required.
|
||||
```
|
||||
* **email** - Changes the local email address. This command requires one argument - the new email address to be set.
|
||||
```javascript
|
||||
api.executeCommand('email', 'example@example.com')
|
||||
```
|
||||
|
||||
* **avatarUrl** - Hangups the call. No arguments are required.
|
||||
```
|
||||
api.executeCommand('avatarUrl', 'avatarUrl')
|
||||
* **avatarUrl** - Changes the local avatar URL. This command requires one argument - the new avatar URL to be set.
|
||||
```javascript
|
||||
api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647')
|
||||
```
|
||||
|
||||
You can also execute multiple commands using the method ```executeCommands```.
|
||||
```
|
||||
You can also execute multiple commands using the `executeCommands` method:
|
||||
```javascript
|
||||
api.executeCommands(commands)
|
||||
```
|
||||
The ```commands``` parameter is object with keys the names of the commands and values the arguments for the
|
||||
commands.
|
||||
|
||||
```
|
||||
The `commands` parameter is an object with the names of the commands as keys and the arguments for the commands asvalues:
|
||||
```javascript
|
||||
api.executeCommands({displayName: ['nickname'], toggleAudio: []});
|
||||
```
|
||||
|
||||
You can add event listeners to the embedded Jitsi Meet using ```addEventListener``` method.
|
||||
```
|
||||
You can add event listeners to the embedded Jitsi Meet using the `addEventListener` method.
|
||||
```javascript
|
||||
api.addEventListener(event, listener)
|
||||
```
|
||||
The ```event``` parameter is String object with the name of the event.
|
||||
The ```listener``` paramenter is Function object with one argument that will be notified when the event occurs
|
||||
with data related to the event.
|
||||
|
||||
Currently we support the following events:
|
||||
The `event` parameter is a String object with the name of the event.
|
||||
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
|
||||
|
||||
* **incomingMessage** - event notifications about incoming
|
||||
messages. The listener will receive object with the following structure:
|
||||
```
|
||||
The following events are currently supported:
|
||||
|
||||
* **incomingMessage** - Event notifications about incoming
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"from": from,//JID of the user that sent the message
|
||||
"nick": nick,//the nickname of the user that sent the message
|
||||
"message": txt//the text of the message
|
||||
"from": from, // JID of the user that sent the message
|
||||
"nick": nick, // the nickname of the user that sent the message
|
||||
"message": txt // the text of the message
|
||||
}
|
||||
```
|
||||
* **outgoingMessage** - event notifications about outgoing
|
||||
messages. The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **outgoingMessage** - Event notifications about outgoing
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"message": txt//the text of the message
|
||||
"message": txt // the text of the message
|
||||
}
|
||||
```
|
||||
|
||||
* **displayNameChanged** - event notifications about display name
|
||||
change. The listener will receive object with the following structure:
|
||||
```
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
jid: jid,//the JID of the participant that changed his display name
|
||||
displayname: displayName //the new display name
|
||||
"jid": jid, // the JID of the participant that changed his display name
|
||||
"displayname": displayName // the new display name
|
||||
}
|
||||
```
|
||||
* **participantJoined** - event notifications about new participant.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **participantJoined** - event notifications about new participants who join the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
jid: jid //the jid of the participant
|
||||
"jid": jid // the JID of the participant
|
||||
}
|
||||
```
|
||||
* **participantLeft** - event notifications about participant that left room.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
jid: jid //the jid of the participant
|
||||
"jid": jid // the JID of the participant
|
||||
}
|
||||
```
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
roomName: room //the room name of the conference
|
||||
"roomName": room // the room name of the conference
|
||||
}
|
||||
```
|
||||
* **videoConferenceLeft** - event notifications fired when the local user has left the video conference.
|
||||
The listener will receive object with the following structure:
|
||||
```
|
||||
|
||||
* **videoConferenceLeft** - event notifications fired when the local user has left the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
roomName: room //the room name of the conference
|
||||
"roomName": room // the room name of the conference
|
||||
}
|
||||
```
|
||||
|
||||
* **readyToClose** - event notification fired when Jitsi Meet is ready to be closed (hangup operations are completed).
|
||||
|
||||
You can also add multiple event listeners by using ```addEventListeners```.
|
||||
You can also add multiple event listeners by using `addEventListeners`.
|
||||
This method requires one argument of type Object. The object argument must
|
||||
have keys with the names of the events and values the listeners of the events.
|
||||
have the names of the events as keys and the listeners of the events as values.
|
||||
|
||||
```
|
||||
```javascript
|
||||
function incomingMessageListener(object)
|
||||
{
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
function outgoingMessageListener(object)
|
||||
{
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
api.addEventListeners({
|
||||
@@ -191,25 +196,28 @@ api.addEventListeners({
|
||||
outgoingMessage: outgoingMessageListener})
|
||||
```
|
||||
|
||||
If you want to remove a listener you can use ```removeEventListener``` method with argument the name of the event.
|
||||
```
|
||||
If you want to remove a listener you can use `removeEventListener` method with argument the name of the event.
|
||||
|
||||
```javascript
|
||||
api.removeEventListener("incomingMessage");
|
||||
```
|
||||
|
||||
If you want to remove more than one event you can use ```removeEventListeners``` method with argument
|
||||
array with the names of the events.
|
||||
```
|
||||
If you want to remove more than one event you can use `removeEventListeners` method with an Array with the names of the events as an argument.
|
||||
```javascript
|
||||
api.removeEventListeners(["incomingMessage", "outgoingMessageListener"]);
|
||||
```
|
||||
|
||||
You can get the number of participants in the conference with the following code:
|
||||
```
|
||||
You can get the number of participants in the conference with the following API function:
|
||||
```javascript
|
||||
var numberOfParticipants = api.getNumberOfParticipants();
|
||||
```
|
||||
|
||||
You can remove the embedded Jitsi Meet Conference with the following code:
|
||||
```
|
||||
You can remove the embedded Jitsi Meet Conference with the following API function:
|
||||
```javascript
|
||||
api.dispose()
|
||||
```
|
||||
|
||||
It is a good practice to remove the conference before the page is unloaded.
|
||||
NOTE: It's a good practice to remove the conference before the page is unloaded.
|
||||
|
||||
[config.js]: https://github.com/jitsi/jitsi-meet/blob/master/config.js
|
||||
[interface_config.js]: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js
|
||||
|
||||
94
doc/mobile.md
Normal file
94
doc/mobile.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Jitsi Meet mobile apps
|
||||
|
||||
Jitsi Meet can also be built as a standalone mobile application for
|
||||
iOS and Android. It uses the [React Native]() framework.
|
||||
|
||||
First make sure the [React Native dependencies]() are installed.
|
||||
|
||||
**NOTE**: This document assumes the app is being built on a macOS system.
|
||||
|
||||
**NOTE**: The app must be built for an actual device since the simulators don't
|
||||
work properly with the native plugins we require.
|
||||
|
||||
|
||||
## iOS
|
||||
|
||||
1. Install some extra dependencies
|
||||
|
||||
- Install ios-deploy globally (in case you want to use the React Native CLI
|
||||
to deploy the app to the device)
|
||||
|
||||
```bash
|
||||
npm install -g ios-deploy
|
||||
```
|
||||
|
||||
2. Build the app
|
||||
|
||||
There are 2 ways to build the app: using the CLI or using Xcode.
|
||||
|
||||
Using the CLI:
|
||||
|
||||
```bash
|
||||
react-native run-ios --device
|
||||
```
|
||||
|
||||
When the app is launched from the CLI the output can be checked with the
|
||||
following command:
|
||||
|
||||
```bash
|
||||
react-native log-ios
|
||||
```
|
||||
|
||||
Using Xcode
|
||||
|
||||
- Open **ios/jitsi-meet-react.xcworkspace** in Xcode. Make sure it's the
|
||||
workspace file!
|
||||
|
||||
- Select your device from the top bar and hit the "play" button.
|
||||
|
||||
When the app is launched from Xcode the Debug console will show the output
|
||||
logs the application creates.
|
||||
|
||||
|
||||
3. Other remarks
|
||||
|
||||
It's likely you'll need to change the bundle ID for deploying to a device
|
||||
because the default bundle ID points to the application signed by Atlassian.
|
||||
|
||||
This can be changed in the "General" tab. Under "Identity" set
|
||||
"Bundle Identifier" to a different value, and adjust the "Team" in the
|
||||
"Signing" section to match your own.
|
||||
|
||||
|
||||
## Android
|
||||
|
||||
The [React Native dependencies]() page has very detailed information on how to
|
||||
setup [Android Studio]() and the required components for getting the necessary
|
||||
build environment. Make sure you follow it closely.
|
||||
|
||||
1. Building the app
|
||||
|
||||
The app can be built using the CLI utility as follows:
|
||||
|
||||
```bash
|
||||
react-native run-android
|
||||
```
|
||||
|
||||
It will be launched on the connected Android device.
|
||||
|
||||
|
||||
## Debugging
|
||||
|
||||
The official documentation on [debugging]() is quite extensive, it is the
|
||||
preferred method for debugging.
|
||||
|
||||
**NOTE**: When using Chrome Developer Tools for debugging the JavaScript code
|
||||
is being interpreted by Chrome's V8 engine, instead of JSCore which
|
||||
React Native uses. It's important to keep this in mind due to potential
|
||||
differences in supported JavaScript features.
|
||||
|
||||
|
||||
[Android Studio]: https://developer.android.com/studio/index.html
|
||||
[debugging]: https://facebook.github.io/react-native/docs/debugging.html
|
||||
[React Native]: https://facebook.github.io/react-native/
|
||||
[React Native dependencies]: https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies
|
||||
6
flow-typed/npm/flow-bin_v0.x.x.js
vendored
Normal file
6
flow-typed/npm/flow-bin_v0.x.x.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583
|
||||
// flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x
|
||||
|
||||
declare module "flow-bin" {
|
||||
declare module.exports: string;
|
||||
}
|
||||
89
flow-typed/npm/react-redux_v5.x.x.js
vendored
Normal file
89
flow-typed/npm/react-redux_v5.x.x.js
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// flow-typed signature: 0ed284c5a2e97a9e3c0e87af3dedc09d
|
||||
// flow-typed version: bdf1e66252/react-redux_v5.x.x/flow_>=v0.30.x
|
||||
|
||||
import type { Dispatch, Store } from 'redux'
|
||||
|
||||
declare module 'react-redux' {
|
||||
|
||||
/*
|
||||
|
||||
S = State
|
||||
A = Action
|
||||
OP = OwnProps
|
||||
SP = StateProps
|
||||
DP = DispatchProps
|
||||
|
||||
*/
|
||||
|
||||
declare type MapStateToProps<S, OP: Object, SP: Object> = (state: S, ownProps: OP) => SP | MapStateToProps<S, OP, SP>;
|
||||
|
||||
declare type MapDispatchToProps<A, OP: Object, DP: Object> = ((dispatch: Dispatch<A>, ownProps: OP) => DP) | DP;
|
||||
|
||||
declare type MergeProps<SP, DP: Object, OP: Object, P: Object> = (stateProps: SP, dispatchProps: DP, ownProps: OP) => P;
|
||||
|
||||
declare type StatelessComponent<P> = (props: P) => ?React$Element<any>;
|
||||
|
||||
declare class ConnectedComponent<OP, P, Def, St> extends React$Component<void, OP, void> {
|
||||
static WrappedComponent: Class<React$Component<Def, P, St>>;
|
||||
getWrappedInstance(): React$Component<Def, P, St>;
|
||||
static defaultProps: void;
|
||||
props: OP;
|
||||
state: void;
|
||||
}
|
||||
|
||||
declare type ConnectedComponentClass<OP, P, Def, St> = Class<ConnectedComponent<OP, P, Def, St>>;
|
||||
|
||||
declare type Connector<OP, P> = {
|
||||
(component: StatelessComponent<P>): ConnectedComponentClass<OP, P, void, void>;
|
||||
<Def, St>(component: Class<React$Component<Def, P, St>>): ConnectedComponentClass<OP, P, Def, St>;
|
||||
};
|
||||
|
||||
declare class Provider<S, A> extends React$Component<void, { store: Store<S, A>, children?: any }, void> { }
|
||||
|
||||
declare type ConnectOptions = {
|
||||
pure?: boolean,
|
||||
withRef?: boolean
|
||||
};
|
||||
|
||||
declare type Null = null | void;
|
||||
|
||||
declare function connect<A, OP>(
|
||||
...rest: Array<void> // <= workaround for https://github.com/facebook/flow/issues/2360
|
||||
): Connector<OP, $Supertype<{ dispatch: Dispatch<A> } & OP>>;
|
||||
|
||||
declare function connect<A, OP>(
|
||||
mapStateToProps: Null,
|
||||
mapDispatchToProps: Null,
|
||||
mergeProps: Null,
|
||||
options: ConnectOptions
|
||||
): Connector<OP, $Supertype<{ dispatch: Dispatch<A> } & OP>>;
|
||||
|
||||
declare function connect<S, A, OP, SP>(
|
||||
mapStateToProps: MapStateToProps<S, OP, SP>,
|
||||
mapDispatchToProps: Null,
|
||||
mergeProps: Null,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, $Supertype<SP & { dispatch: Dispatch<A> } & OP>>;
|
||||
|
||||
declare function connect<A, OP, DP>(
|
||||
mapStateToProps: Null,
|
||||
mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
|
||||
mergeProps: Null,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, $Supertype<DP & OP>>;
|
||||
|
||||
declare function connect<S, A, OP, SP, DP>(
|
||||
mapStateToProps: MapStateToProps<S, OP, SP>,
|
||||
mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
|
||||
mergeProps: Null,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, $Supertype<SP & DP & OP>>;
|
||||
|
||||
declare function connect<S, A, OP, SP, DP, P>(
|
||||
mapStateToProps: MapStateToProps<S, OP, SP>,
|
||||
mapDispatchToProps: MapDispatchToProps<A, OP, DP>,
|
||||
mergeProps: MergeProps<SP, DP, OP, P>,
|
||||
options?: ConnectOptions
|
||||
): Connector<OP, P>;
|
||||
|
||||
}
|
||||
56
flow-typed/npm/redux_v3.x.x.js
vendored
Normal file
56
flow-typed/npm/redux_v3.x.x.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// flow-typed signature: ba132c96664f1a05288f3eb2272a3c35
|
||||
// flow-typed version: c4bbd91cfc/redux_v3.x.x/flow_>=v0.33.x
|
||||
|
||||
declare module 'redux' {
|
||||
|
||||
/*
|
||||
|
||||
S = State
|
||||
A = Action
|
||||
|
||||
*/
|
||||
|
||||
declare type Dispatch<A: { type: $Subtype<string> }> = (action: A) => A;
|
||||
|
||||
declare type MiddlewareAPI<S, A> = {
|
||||
dispatch: Dispatch<A>;
|
||||
getState(): S;
|
||||
};
|
||||
|
||||
declare type Store<S, A> = {
|
||||
// rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages)
|
||||
dispatch: Dispatch<A>;
|
||||
getState(): S;
|
||||
subscribe(listener: () => void): () => void;
|
||||
replaceReducer(nextReducer: Reducer<S, A>): void
|
||||
};
|
||||
|
||||
declare type Reducer<S, A> = (state: S, action: A) => S;
|
||||
|
||||
declare type Middleware<S, A> =
|
||||
(api: MiddlewareAPI<S, A>) =>
|
||||
(next: Dispatch<A>) => Dispatch<A>;
|
||||
|
||||
declare type StoreCreator<S, A> = {
|
||||
(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
(reducer: Reducer<S, A>, preloadedState: S, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
};
|
||||
|
||||
declare type StoreEnhancer<S, A> = (next: StoreCreator<S, A>) => StoreCreator<S, A>;
|
||||
|
||||
declare function createStore<S, A>(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
declare function createStore<S, A>(reducer: Reducer<S, A>, preloadedState: S, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
|
||||
|
||||
declare function applyMiddleware<S, A>(...middlewares: Array<Middleware<S, A>>): StoreEnhancer<S, A>;
|
||||
|
||||
declare type ActionCreator<A, B> = (...args: Array<B>) => A;
|
||||
declare type ActionCreators<K, A> = { [key: K]: ActionCreator<A, any> };
|
||||
|
||||
declare function bindActionCreators<A, C: ActionCreator<A, any>>(actionCreator: C, dispatch: Dispatch<A>): C;
|
||||
declare function bindActionCreators<A, K, C: ActionCreators<K, A>>(actionCreators: C, dispatch: Dispatch<A>): C;
|
||||
|
||||
declare function combineReducers<O: Object, A>(reducers: O): Reducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
|
||||
|
||||
declare function compose<S, A>(...fns: Array<StoreEnhancer<S, A>>): Function;
|
||||
|
||||
}
|
||||
BIN
fonts/jitsi.eot
BIN
fonts/jitsi.eot
Binary file not shown.
@@ -14,6 +14,7 @@
|
||||
<glyph unicode="" glyph-name="connection-lost" horiz-adv-x="1414" d="M0 299.153h196.337v-187.951h-196.337v187.951zM271.842 480.372h196.337v-369.169h-196.337v369.169zM543.656 661.562h196.337v-550.36h-196.337v550.36zM815.47 842.766v-731.564h119.56c-14.589 33.025-23.125 71.503-23.232 111.943 0.132 86.42 38.697 163.851 99.656 216.468l0.348 403.153h-196.332zM1087.292 1024v-533.672c28.874 10.572 62.222 16.73 97.009 16.825 35.717-0.129 69.823-6.614 101.322-18.371l-1.999 535.218h-196.332zM1192.868 439.852c-0.009 0-0.020 0-0.031 0-122.247 0-221.351-98.447-221.372-219.896 0-0.007 0-0.014 0-0.021 0-121.467 99.111-219.935 221.372-219.935 0.011 0 0.021 0 0.032 0 122.248 0.014 221.345 98.477 221.345 219.935 0 0.007 0 0.013 0 0.020-0.021 121.441-99.11 219.883-221.345 219.897zM1194.706 372.607c87.601-0.006 158.614-69.787 158.614-155.866 0-0.006 0-0.012 0-0.019-0.022-86.062-71.026-155.822-158.614-155.828-87.588 0.006-158.593 69.766-158.615 155.826 0 0.007 0 0.014 0 0.020 0 86.079 71.013 155.86 158.613 155.866zM1286.795 355.682l48.348-52.528-236.375-217.567-48.348 52.528 236.375 217.567z" />
|
||||
<glyph unicode="" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
|
||||
<glyph unicode="" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-8 198-100 198-212 0-118-96-214-214-214h-554c-142 0-256 114-256 256 0 132 100 240 228 254 54 102 160 174 284 174 156 0 284-110 314-258z" />
|
||||
<glyph unicode="" glyph-name="mic-camera-combined" d="M756.704 628.138l267.296 202.213v-635.075l-267.296 202.213v-191.923c0-12.085-11.296-21.863-25.216-21.863h-706.272c-13.92 0-25.216 9.777-25.216 21.863v612.25c0 12.085 11.296 21.863 25.216 21.863h706.272c13.92 0 25.216-9.777 25.216-21.863v-189.679zM371.338 376.228c47.817 0 86.529 40.232 86.529 89.811v184.835c0 49.651-38.713 89.883-86.529 89.883-47.788 0-86.515-40.232-86.515-89.883v-184.835c0-49.579 38.756-89.811 86.515-89.811v0zM356.754 314.070v-32.78h33.718v33.412c73.858 9.606 131.235 73.73 131.235 151.351v88.232h-30.636v-88.232c0-67.57-53.696-122.534-119.734-122.534-66.024 0-119.691 54.964-119.691 122.534v88.232h-30.636v-88.232c0-79.215 59.674-144.502 135.744-151.969v-0.014z" />
|
||||
<glyph unicode="" glyph-name="kick" d="M512 810l284-426h-568zM214 298h596v-84h-596v84z" />
|
||||
<glyph unicode="" glyph-name="hangup" d="M512 640c-68 0-134-10-196-30v-132c0-16-10-34-24-40-42-20-80-46-114-78-8-8-18-12-30-12s-22 4-30 12l-106 106c-8 8-12 18-12 30s4 22 12 30c130 124 306 200 500 200s370-76 500-200c8-8 12-18 12-30s-4-22-12-30l-106-106c-8-8-18-12-30-12s-22 4-30 12c-34 32-72 58-114 78-14 6-24 20-24 38v132c-62 20-128 32-196 32z" />
|
||||
<glyph unicode="" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.ttf
Binary file not shown.
BIN
fonts/jitsi.woff
BIN
fonts/jitsi.woff
Binary file not shown.
@@ -1,6 +1,35 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M756.704 395.862l267.296-202.213v635.075l-267.296-202.213v191.923c0 12.085-11.296 21.863-25.216 21.863h-706.272c-13.92 0-25.216-9.777-25.216-21.863v-612.25c0-12.085 11.296-21.863 25.216-21.863h706.272c13.92 0 25.216 9.777 25.216 21.863v189.679zM371.338 647.772c47.817 0 86.529-40.232 86.529-89.811v-184.835c0-49.651-38.713-89.883-86.529-89.883-47.788 0-86.515 40.232-86.515 89.883v184.835c0 49.579 38.756 89.811 86.515 89.811v0zM356.754 709.93v32.78h33.718v-33.412c73.858-9.606 131.235-73.73 131.235-151.351v-88.232h-30.636v88.232c0 67.57-53.696 122.534-119.734 122.534-66.024 0-119.691-54.964-119.691-122.534v-88.232h-30.636v88.232c0 79.215 59.674 144.502 135.744 151.969v0.014z"
|
||||
],
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"Combined Shape"
|
||||
]
|
||||
},
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"properties": {
|
||||
"order": 109,
|
||||
"id": 0,
|
||||
"name": "mic-camera-combined",
|
||||
"prevSize": 32,
|
||||
"code": 59651
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -26,8 +55,8 @@
|
||||
"prevSize": 32,
|
||||
"code": 59677
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
@@ -55,8 +84,8 @@
|
||||
"prevSize": 32,
|
||||
"code": 59676
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 1
|
||||
},
|
||||
{
|
||||
@@ -81,9 +110,9 @@
|
||||
"code": 59649,
|
||||
"name": "avatar"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 12
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -107,9 +136,9 @@
|
||||
"code": 59653,
|
||||
"name": "hangup"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 123
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 3
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -133,9 +162,9 @@
|
||||
"code": 59654,
|
||||
"name": "chat"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 149
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -159,9 +188,9 @@
|
||||
"code": 59650,
|
||||
"name": "download"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 165
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 5
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -185,9 +214,9 @@
|
||||
"code": 59655,
|
||||
"name": "edit"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 186
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -211,9 +240,9 @@
|
||||
"code": 59656,
|
||||
"name": "share-doc"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 207
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 7
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -237,9 +266,9 @@
|
||||
"code": 59657,
|
||||
"name": "telephone"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 216
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 8
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -263,9 +292,9 @@
|
||||
"code": 59652,
|
||||
"name": "kick"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 243
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 9
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -289,9 +318,9 @@
|
||||
"code": 59679,
|
||||
"name": "menu-up"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 257
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 10
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -315,9 +344,9 @@
|
||||
"code": 59680,
|
||||
"name": "menu-down"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 258
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 11
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -341,9 +370,9 @@
|
||||
"code": 59659,
|
||||
"name": "full-screen"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 351
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -367,9 +396,9 @@
|
||||
"code": 59660,
|
||||
"name": "exit-full-screen"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 352
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -393,9 +422,9 @@
|
||||
"code": 59658,
|
||||
"name": "star-full"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 364
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 14
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -419,9 +448,9 @@
|
||||
"code": 59661,
|
||||
"name": "security"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 474
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 15
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -445,9 +474,9 @@
|
||||
"code": 59662,
|
||||
"name": "security-locked"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 475
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 16
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -471,9 +500,9 @@
|
||||
"code": 59663,
|
||||
"name": "reload"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 483
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 17
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -497,9 +526,9 @@
|
||||
"code": 59664,
|
||||
"name": "microphone"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 493
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 18
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -523,9 +552,9 @@
|
||||
"code": 59665,
|
||||
"name": "mic-empty"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 494
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 19
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -549,9 +578,9 @@
|
||||
"code": 59666,
|
||||
"name": "mic-disabled"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 495
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 20
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -575,9 +604,9 @@
|
||||
"code": 59678,
|
||||
"name": "raised-hand"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 540
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 21
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -601,9 +630,9 @@
|
||||
"code": 59675,
|
||||
"name": "contactList"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 550
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -627,9 +656,9 @@
|
||||
"code": 59667,
|
||||
"name": "link"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 560
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 23
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -653,9 +682,9 @@
|
||||
"code": 59668,
|
||||
"name": "shared-video"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 591
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 24
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -679,9 +708,9 @@
|
||||
"code": 59669,
|
||||
"name": "settings"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 666
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 25
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -705,9 +734,9 @@
|
||||
"code": 59670,
|
||||
"name": "star"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 718
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 26
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -731,9 +760,9 @@
|
||||
"code": 59681,
|
||||
"name": "switch-camera"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 742
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 27
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -757,9 +786,9 @@
|
||||
"code": 59671,
|
||||
"name": "share-desktop"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 784
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 28
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -783,9 +812,9 @@
|
||||
"code": 59672,
|
||||
"name": "camera"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 799
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 29
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -809,9 +838,9 @@
|
||||
"code": 59673,
|
||||
"name": "camera-disabled"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 800
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 30
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -835,9 +864,9 @@
|
||||
"code": 59674,
|
||||
"name": "volume"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 822
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 31
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -885,7 +914,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
"iconIdx": 32
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -956,7 +985,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
"iconIdx": 33
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -983,7 +1012,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
"iconIdx": 34
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1011,7 +1040,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
"iconIdx": 35
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1039,7 +1068,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
"iconIdx": 36
|
||||
}
|
||||
],
|
||||
"height": 1024,
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBundleURLProvider.h"
|
||||
#import "RCTLinkingManager.h"
|
||||
#import "RCTRootView.h"
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
/**
|
||||
* A <tt>RCTFatalHandler</tt> implementation which swallows JavaScript errors.
|
||||
|
||||
@@ -20,6 +20,19 @@
|
||||
<string>1.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>org.jitsi.meet</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>org.jitsi.meet</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:beta.meet.jit.si</string>
|
||||
<string>applinks:beta.hipchat.me</string>
|
||||
<string>applinks:chaos.hipchat.me</string>
|
||||
<string>applinks:enso.me</string>
|
||||
<string>applinks:hipchat.me</string>
|
||||
<string>applinks:meet.jit.si</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
6
modules/API/external/external_api.js
vendored
6
modules/API/external/external_api.js
vendored
@@ -88,8 +88,10 @@ function changeParticipantNumber(APIInstance, number) {
|
||||
* @param width width of the iframe
|
||||
* @param height height of the iframe
|
||||
* @param parent_node the node that will contain the iframe
|
||||
* @param filmStripOnly if the value is true only the small videos will be
|
||||
* visible.
|
||||
* @param configOverwrite object containing configuration options defined in
|
||||
* config.js to be overridden.
|
||||
* @param interfaceConfigOverwrite object containing configuration options
|
||||
* defined in interface_config.js to be overridden.
|
||||
* @param noSsl if the value is true https won't be used
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
@@ -54,7 +54,7 @@ const IndicatorFontSizes = {
|
||||
/**
|
||||
* Returns the available video width.
|
||||
*/
|
||||
getAvailableVideoWidth: function () {
|
||||
getAvailableVideoWidth() {
|
||||
return window.innerWidth;
|
||||
},
|
||||
|
||||
@@ -70,7 +70,7 @@ const IndicatorFontSizes = {
|
||||
*
|
||||
* @param el the element
|
||||
*/
|
||||
getTextWidth: function (el) {
|
||||
getTextWidth(el) {
|
||||
return (el.clientWidth + 1);
|
||||
},
|
||||
|
||||
@@ -79,7 +79,7 @@ const IndicatorFontSizes = {
|
||||
*
|
||||
* @param el the element
|
||||
*/
|
||||
getTextHeight: function (el) {
|
||||
getTextHeight(el) {
|
||||
return (el.clientHeight + 1);
|
||||
},
|
||||
|
||||
@@ -88,14 +88,14 @@ const IndicatorFontSizes = {
|
||||
*
|
||||
* @param id the identifier of the audio element.
|
||||
*/
|
||||
playSoundNotification: function (id) {
|
||||
playSoundNotification(id) {
|
||||
document.getElementById(id).play();
|
||||
},
|
||||
|
||||
/**
|
||||
* Escapes the given text.
|
||||
*/
|
||||
escapeHtml: function (unsafeText) {
|
||||
escapeHtml(unsafeText) {
|
||||
return $('<div/>').text(unsafeText).html();
|
||||
},
|
||||
|
||||
@@ -105,11 +105,11 @@ const IndicatorFontSizes = {
|
||||
* @param {string} safe string which contains escaped html
|
||||
* @returns {string} unescaped html string.
|
||||
*/
|
||||
unescapeHtml: function (safe) {
|
||||
unescapeHtml(safe) {
|
||||
return $('<div />').html(safe).text();
|
||||
},
|
||||
|
||||
imageToGrayScale: function (canvas) {
|
||||
imageToGrayScale(canvas) {
|
||||
var context = canvas.getContext('2d');
|
||||
var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
var pixels = imgData.data;
|
||||
@@ -156,7 +156,7 @@ const IndicatorFontSizes = {
|
||||
* @param key the tooltip data-i18n key
|
||||
* @param position the position of the tooltip in relation to the element
|
||||
*/
|
||||
setTooltip: function (element, key, position) {
|
||||
setTooltip(element, key, position) {
|
||||
if (element !== null) {
|
||||
element.setAttribute('data-tooltip', TOOLTIP_POSITIONS[position]);
|
||||
element.setAttribute('data-i18n', '[content]' + key);
|
||||
@@ -170,7 +170,7 @@ const IndicatorFontSizes = {
|
||||
*
|
||||
* @param element the element to remove the tooltip from
|
||||
*/
|
||||
removeTooltip: function (element) {
|
||||
removeTooltip(element) {
|
||||
element.removeAttribute('data-tooltip', '');
|
||||
element.removeAttribute('data-i18n','');
|
||||
element.removeAttribute('content','');
|
||||
@@ -183,7 +183,7 @@ const IndicatorFontSizes = {
|
||||
* @returns {string|*}
|
||||
* @private
|
||||
*/
|
||||
_getTooltipText: function (element) {
|
||||
_getTooltipText(element) {
|
||||
let title = element.getAttribute('content');
|
||||
let shortcut = element.getAttribute('shortcut');
|
||||
if(shortcut) {
|
||||
@@ -198,7 +198,7 @@ const IndicatorFontSizes = {
|
||||
* @param container the container to which new child element will be added
|
||||
* @param newChild the new element that will be inserted into the container
|
||||
*/
|
||||
prependChild: function (container, newChild) {
|
||||
prependChild(container, newChild) {
|
||||
var firstChild = container.childNodes[0];
|
||||
if (firstChild) {
|
||||
container.insertBefore(newChild, firstChild);
|
||||
@@ -214,7 +214,7 @@ const IndicatorFontSizes = {
|
||||
* @returns {boolean} {true} to indicate that the given toolbar button
|
||||
* is enabled, {false} - otherwise
|
||||
*/
|
||||
isButtonEnabled: function (name) {
|
||||
isButtonEnabled(name) {
|
||||
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1
|
||||
|| interfaceConfig.MAIN_TOOLBAR_BUTTONS.indexOf(name) !== -1;
|
||||
},
|
||||
@@ -226,7 +226,7 @@ const IndicatorFontSizes = {
|
||||
* @returns {boolean} {true} to indicate that the given setting section
|
||||
* is enabled, {false} - otherwise
|
||||
*/
|
||||
isSettingEnabled: function (name) {
|
||||
isSettingEnabled(name) {
|
||||
return interfaceConfig.SETTINGS_SECTIONS.indexOf(name) !== -1;
|
||||
},
|
||||
|
||||
@@ -235,7 +235,7 @@ const IndicatorFontSizes = {
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isAuthenticationEnabled: function() {
|
||||
isAuthenticationEnabled() {
|
||||
return interfaceConfig.AUTHENTICATION_ENABLE;
|
||||
},
|
||||
|
||||
@@ -303,7 +303,7 @@ const IndicatorFontSizes = {
|
||||
}
|
||||
},
|
||||
|
||||
hideDisabledButtons: function (mappings) {
|
||||
hideDisabledButtons(mappings) {
|
||||
var selector = Object.keys(mappings)
|
||||
.map(function (buttonName) {
|
||||
return UIUtil.isButtonEnabled(buttonName)
|
||||
@@ -313,7 +313,7 @@ const IndicatorFontSizes = {
|
||||
$(selector).hide();
|
||||
},
|
||||
|
||||
redirect (url) {
|
||||
redirect(url) {
|
||||
window.location.href = url;
|
||||
},
|
||||
|
||||
@@ -368,7 +368,7 @@ const IndicatorFontSizes = {
|
||||
* @param {Object} attrs object with properties
|
||||
* @returns {String} string of html element attributes
|
||||
*/
|
||||
attrsToString: function (attrs) {
|
||||
attrsToString(attrs) {
|
||||
return Object.keys(attrs).map(
|
||||
key => ` ${key}="${attrs[key]}"`
|
||||
).join(' ');
|
||||
|
||||
@@ -23,6 +23,8 @@ export default class LargeVideoManager {
|
||||
this.eventEmitter = emitter;
|
||||
|
||||
this.state = VIDEO_CONTAINER_TYPE;
|
||||
// FIXME: We are passing resizeContainer as parameter which is calling
|
||||
// Container.resize. Probably there's better way to implement this.
|
||||
this.videoContainer = new VideoContainer(
|
||||
() => this.resizeContainer(VIDEO_CONTAINER_TYPE), emitter);
|
||||
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
|
||||
|
||||
@@ -164,12 +164,20 @@ export class VideoContainer extends LargeContainer {
|
||||
return getStreamOwnerId(this.stream);
|
||||
}
|
||||
|
||||
constructor (onPlay, emitter) {
|
||||
/**
|
||||
* Creates new VideoContainer instance.
|
||||
* @param resizeContainer {Function} function that takes care of the size
|
||||
* of the video container.
|
||||
* @param emitter {EventEmitter} the event emitter that will be used by
|
||||
* this instance.
|
||||
*/
|
||||
constructor (resizeContainer, emitter) {
|
||||
super();
|
||||
this.stream = null;
|
||||
this.videoType = null;
|
||||
this.localFlipX = true;
|
||||
this.emitter = emitter;
|
||||
this.resizeContainer = resizeContainer;
|
||||
|
||||
this.isVisible = false;
|
||||
|
||||
@@ -199,8 +207,8 @@ export class VideoContainer extends LargeContainer {
|
||||
this.avatarHeight = $("#dominantSpeakerAvatar").height();
|
||||
|
||||
var onPlayCallback = function (event) {
|
||||
if (typeof onPlay === 'function') {
|
||||
onPlay(event);
|
||||
if (typeof resizeContainer === 'function') {
|
||||
resizeContainer(event);
|
||||
}
|
||||
this.wasVideoRendered = true;
|
||||
}.bind(this);
|
||||
@@ -336,8 +344,14 @@ export class VideoContainer extends LargeContainer {
|
||||
* @param {string} videoType video type
|
||||
*/
|
||||
setStream (stream, videoType) {
|
||||
|
||||
if (this.stream === stream) {
|
||||
// Handles the use case for the remote participants when the
|
||||
// videoType is received with delay after turning on/off the
|
||||
// desktop sharing.
|
||||
if(this.videoType !== videoType) {
|
||||
this.videoType = videoType;
|
||||
this.resizeContainer();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
// The stream has changed, so the image will be lost on detach
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import { redirect } from '../util/helpers';
|
||||
import { replace } from '../util/helpers';
|
||||
|
||||
/**
|
||||
* The modules stores information about the URL used to start the conference and
|
||||
@@ -68,6 +68,6 @@ export default class ConferenceUrl {
|
||||
*/
|
||||
reload() {
|
||||
logger.info("Reloading the conference using URL: " + this.originalURL);
|
||||
redirect(this.originalURL);
|
||||
replace(this.originalURL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Create deferred object.
|
||||
*
|
||||
* @returns {{promise, resolve, reject}}
|
||||
*/
|
||||
export function createDeferred () {
|
||||
let deferred = {};
|
||||
export function createDeferred() {
|
||||
const deferred = {};
|
||||
|
||||
deferred.promise = new Promise(function (resolve, reject) {
|
||||
deferred.promise = new Promise((resolve, reject) => {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
@@ -18,63 +19,58 @@ export function createDeferred () {
|
||||
/**
|
||||
* Reload page.
|
||||
*/
|
||||
export function reload () {
|
||||
export function reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to new URL.
|
||||
* Redirects to a specific new URL by replacing the current location (in the
|
||||
* history).
|
||||
*
|
||||
* @param {string} url the URL pointing to the location where the user should
|
||||
* be redirected to.
|
||||
*/
|
||||
export function redirect (url) {
|
||||
export function replace(url) {
|
||||
window.location.replace(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the error and reports it to the global error handler.
|
||||
*
|
||||
* @param e {Error} the error
|
||||
* @param msg {string} [optional] the message printed in addition to the error
|
||||
*/
|
||||
export function reportError (e, msg = "") {
|
||||
export function reportError(e, msg = "") {
|
||||
logger.error(msg, e);
|
||||
if(window.onerror)
|
||||
window.onerror(msg, null, null,
|
||||
null, e);
|
||||
window.onerror && window.onerror(msg, null, null, null, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a debounced function that delays invoking func until after wait
|
||||
* milliseconds have elapsed since the last time the debounced
|
||||
* function was invoked
|
||||
* milliseconds have elapsed since the last time the debounced function was
|
||||
* invoked.
|
||||
*
|
||||
* @param fn
|
||||
* @param wait
|
||||
* @param options
|
||||
* @returns {function(...[*])}
|
||||
*/
|
||||
export function debounce(fn, wait = 0, options = {}) {
|
||||
let leading = options.leading || false;
|
||||
let trailing = true;
|
||||
let isCalled = false;
|
||||
|
||||
if (typeof options.trailing !== 'undefined') {
|
||||
trailing = options.trailing;
|
||||
}
|
||||
const leading = options.leading || false;
|
||||
const trailing
|
||||
= (typeof options.trailing === 'undefined') || options.trailing;
|
||||
let called = false;
|
||||
|
||||
return (...args) => {
|
||||
if(!isCalled) {
|
||||
if (leading) {
|
||||
fn(...args);
|
||||
}
|
||||
if (!called) {
|
||||
leading && fn(...args);
|
||||
|
||||
setTimeout(() => {
|
||||
isCalled = false;
|
||||
if (trailing) {
|
||||
fn(...args);
|
||||
}
|
||||
called = false;
|
||||
trailing && fn(...args);
|
||||
}, wait);
|
||||
|
||||
isCalled = true;
|
||||
called = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
12
package.json
12
package.json
@@ -34,7 +34,8 @@
|
||||
"postis": "^2.2.0",
|
||||
"react": "15.4.2",
|
||||
"react-dom": "15.4.2",
|
||||
"react-native": "0.40.0",
|
||||
"react-native": "0.41.2",
|
||||
"react-native-immersive": "0.0.4",
|
||||
"react-native-keep-awake": "^2.0.2",
|
||||
"react-native-prompt": "^1.0.0",
|
||||
"react-native-vector-icons": "^4.0.0",
|
||||
@@ -59,12 +60,15 @@
|
||||
"babel-preset-stage-1": "^6.16.0",
|
||||
"clean-css": "^3.0.0",
|
||||
"css-loader": "*",
|
||||
"eslint": "^3.14.1",
|
||||
"eslint": "^3.15.0",
|
||||
"eslint-plugin-flowtype": "^2.30.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsdoc": "*",
|
||||
"eslint-plugin-react": "*",
|
||||
"eslint-plugin-react-native": "^2.2.1",
|
||||
"expose-loader": "*",
|
||||
"file-loader": "*",
|
||||
"file-loader": "^0.10.0",
|
||||
"flow-bin": "^0.37.0",
|
||||
"haste-resolver-webpack-plugin": "^0.2.2",
|
||||
"imports-loader": "*",
|
||||
"jshint": "2.9.4",
|
||||
@@ -78,7 +82,7 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "jshint . && eslint .",
|
||||
"lint": "jshint . && eslint . && flow",
|
||||
"validate": "npm ls"
|
||||
},
|
||||
"pre-commit": [
|
||||
|
||||
13
prosody-plugins/mod_muc_allowners.lua
Normal file
13
prosody-plugins/mod_muc_allowners.lua
Normal file
@@ -0,0 +1,13 @@
|
||||
-- Copyright (c) 2015 &yet <https://andyet.com>
|
||||
-- https://github.com/otalk/mod_muc_allowners/blob/9a86266a25ed32ade150742cc79f5a1669765a8f/mod_muc_allowners.lua
|
||||
--
|
||||
-- Used under the terms of the MIT License
|
||||
-- https://github.com/otalk/mod_muc_allowners/blob/9a86266a25ed32ade150742cc79f5a1669765a8f/LICENSE
|
||||
|
||||
local muc_service = module:depends("muc");
|
||||
local room_mt = muc_service.room_mt;
|
||||
|
||||
|
||||
room_mt.get_affiliation = function (room, jid)
|
||||
return "owner";
|
||||
end
|
||||
21
prosody-plugins/muc_owner_allow_kick.patch
Normal file
21
prosody-plugins/muc_owner_allow_kick.patch
Normal file
@@ -0,0 +1,21 @@
|
||||
--- muc.lib.lua 2016-10-26 18:26:53.432377291 +0000
|
||||
+++ muc.lib.lua 2016-10-26 18:41:40.754426072 +0000
|
||||
@@ -1256,15 +1256,16 @@
|
||||
if actor == true then
|
||||
actor = nil -- So we can pass it safely to 'publicise_occupant_status' below
|
||||
else
|
||||
+ local actor_affiliation = self:get_affiliation(actor);
|
||||
+
|
||||
-- Can't do anything to other owners or admins
|
||||
local occupant_affiliation = self:get_affiliation(occupant.bare_jid);
|
||||
- if occupant_affiliation == "owner" or occupant_affiliation == "admin" then
|
||||
+ if (occupant_affiliation == "owner" and actor_affiliation ~= "owner") or (occupant_affiliation == "admin" and actor_affiliation ~= "admin" and actor_affiliation ~= "owner") then
|
||||
return nil, "cancel", "not-allowed";
|
||||
end
|
||||
|
||||
-- If you are trying to give or take moderator role you need to be an owner or admin
|
||||
if occupant.role == "moderator" or role == "moderator" then
|
||||
- local actor_affiliation = self:get_affiliation(actor);
|
||||
if actor_affiliation ~= "owner" and actor_affiliation ~= "admin" then
|
||||
return nil, "cancel", "not-allowed";
|
||||
end
|
||||
@@ -1,12 +1,16 @@
|
||||
module.exports = {
|
||||
'extends': '../.eslintrc.js',
|
||||
'parser': 'babel-eslint',
|
||||
'parserOptions': {
|
||||
'ecmaFeatures': {
|
||||
'jsx': true
|
||||
}
|
||||
},
|
||||
'plugins': [
|
||||
|
||||
// ESLint's rule no-duplicate-imports does not understand Flow's import
|
||||
// type. Fortunately, eslint-plugin-import understands Flow's import
|
||||
// type.
|
||||
'import',
|
||||
'jsdoc',
|
||||
'react',
|
||||
'react-native'
|
||||
@@ -274,7 +278,6 @@ module.exports = {
|
||||
'no-confusing-arrow': 2,
|
||||
'no-const-assign': 2,
|
||||
'no-dupe-class-members': 2,
|
||||
'no-duplicate-imports': 2,
|
||||
'no-new-symbol': 2,
|
||||
'no-restricted-imports': 0,
|
||||
'no-this-before-super': 2,
|
||||
@@ -299,6 +302,8 @@ module.exports = {
|
||||
'template-curly-spacing': 2,
|
||||
'yield-star-spacing': 2,
|
||||
|
||||
'import/no-duplicates': 2,
|
||||
|
||||
// JsDoc plugin rules group. The following rules are in addition to
|
||||
// valid-jsdoc rule.
|
||||
'jsdoc/check-param-names': 0,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { loadConfig, setConfig } from '../base/lib-jitsi-meet';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
|
||||
import {
|
||||
_getRoomAndDomainFromUrlString,
|
||||
_getRouteToRender,
|
||||
_parseURIString,
|
||||
init
|
||||
} from './functions';
|
||||
import './reducer';
|
||||
@@ -34,7 +34,7 @@ export function appNavigate(urlOrRoom) {
|
||||
const state = getState();
|
||||
const oldDomain = getDomain(state);
|
||||
|
||||
const { domain, room } = _getRoomAndDomainFromUrlString(urlOrRoom);
|
||||
const { domain, room } = _parseURIString(urlOrRoom);
|
||||
|
||||
// TODO Kostiantyn Tsaregradskyi: We should probably detect if user is
|
||||
// currently in a conference and ask her if she wants to close the
|
||||
|
||||
@@ -38,7 +38,7 @@ export class AbstractApp extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new App instance.
|
||||
* Initializes a new AbstractApp instance.
|
||||
*
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the new instance is to be initialized.
|
||||
@@ -334,13 +334,7 @@ export class AbstractApp extends Component {
|
||||
// (2) A replace function would be provided to the Route in case it
|
||||
// chose to redirect to another path.
|
||||
this._onRouteEnter(route, nextState, pathname => {
|
||||
// FIXME In order to minimize the modifications related to the
|
||||
// removal of react-router, the Web implementation is provided
|
||||
// bellow because the replace function is used on Web only at the
|
||||
// time of this writing. Provide a platform-agnostic implementation.
|
||||
// It should likely find the best Route matching the specified
|
||||
// pathname and navigate to it.
|
||||
window.location.pathname = pathname;
|
||||
this._openURL(pathname);
|
||||
|
||||
// Do not proceed with the route because it chose to redirect to
|
||||
// another path.
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Linking } from 'react-native';
|
||||
|
||||
import { Platform } from '../../base/react';
|
||||
import '../../audio-mode';
|
||||
import '../../full-screen';
|
||||
import '../../wake-lock';
|
||||
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
|
||||
@@ -14,6 +14,27 @@ export class App extends AbstractApp {
|
||||
*/
|
||||
static propTypes = AbstractApp.propTypes
|
||||
|
||||
/**
|
||||
* Initializes a new App instance.
|
||||
*
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the new instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
|
||||
/**
|
||||
* The context root of window.location i.e. this Web App.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
windowLocationContextRoot: this._getWindowLocationContextRoot()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits the app before component will mount.
|
||||
*
|
||||
@@ -35,6 +56,22 @@ export class App extends AbstractApp {
|
||||
return window.location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context root of this Web App from window.location.
|
||||
*
|
||||
* @private
|
||||
* @returns {string} The context root of window.location i.e. this Web App.
|
||||
*/
|
||||
_getWindowLocationContextRoot() {
|
||||
const pathname = this._getWindowLocation().pathname;
|
||||
const contextRootEndIndex = pathname.lastIndexOf('/');
|
||||
|
||||
return (
|
||||
contextRootEndIndex === -1
|
||||
? '/'
|
||||
: pathname.substring(0, contextRootEndIndex + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to a specific Route (via platform-specific means).
|
||||
*
|
||||
@@ -53,6 +90,7 @@ export class App extends AbstractApp {
|
||||
= path.replace(
|
||||
/:room/g,
|
||||
store.getState()['features/base/conference'].room);
|
||||
path = this._routePath2WindowLocationPathname(path);
|
||||
|
||||
// Navigate to the specified Route.
|
||||
const windowLocation = this._getWindowLocation();
|
||||
@@ -69,4 +107,22 @@ export class App extends AbstractApp {
|
||||
windowLocation.pathname = path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a specific Route path to a window.location.pathname.
|
||||
*
|
||||
* @param {string} path - A Route path to be converted to/represeted as a
|
||||
* window.location.pathname.
|
||||
* @private
|
||||
* @returns {string} A window.location.pathname-compatible representation of
|
||||
* the specified Route path.
|
||||
*/
|
||||
_routePath2WindowLocationPathname(path) {
|
||||
let pathname = this.state.windowLocationContextRoot;
|
||||
|
||||
pathname.endsWith('/') || (pathname += '/');
|
||||
pathname += path.startsWith('/') ? path.substring(1) : path;
|
||||
|
||||
return pathname;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,134 @@ import { RouteRegistry } from '../base/react';
|
||||
import { Conference } from '../conference';
|
||||
import { WelcomePage } from '../welcome';
|
||||
|
||||
/**
|
||||
* The RegExp pattern of the authority of a URI.
|
||||
*
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
const _URI_AUTHORITY_PATTERN = '(//[^/?#]+)';
|
||||
|
||||
/**
|
||||
* The RegExp pattern of the path of a URI.
|
||||
*
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
const _URI_PATH_PATTERN = '([^?#]*)';
|
||||
|
||||
/**
|
||||
* The RegExp patther of the protocol of a URI.
|
||||
*
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
const _URI_PROTOCOL_PATTERN = '([a-z][a-z0-9\\.\\+-]*:)';
|
||||
|
||||
/**
|
||||
* Fixes the hier-part of a specific URI (string) so that the URI is well-known.
|
||||
* For example, certain Jitsi Meet deployments are not conventional but it is
|
||||
* possible to translate their URLs into conventional.
|
||||
*
|
||||
* @param {string} uri - The URI (string) to fix the hier-part of.
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
function _fixURIStringHierPart(uri) {
|
||||
// Rewrite the specified URL in order to handle special cases such as
|
||||
// hipchat.com and enso.me which do not follow the common pattern of most
|
||||
// Jitsi Meet deployments.
|
||||
|
||||
// hipchat.com
|
||||
let regex
|
||||
= new RegExp(
|
||||
`^${_URI_PROTOCOL_PATTERN}//hipchat\\.com/video/call/`,
|
||||
'gi');
|
||||
let match = regex.exec(uri);
|
||||
|
||||
if (!match) {
|
||||
// enso.me
|
||||
regex
|
||||
= new RegExp(
|
||||
`^${_URI_PROTOCOL_PATTERN}//enso\\.me/(?:call|meeting)/`,
|
||||
'gi');
|
||||
match = regex.exec(uri);
|
||||
}
|
||||
if (match) {
|
||||
/* eslint-disable no-param-reassign, prefer-template */
|
||||
|
||||
uri
|
||||
= match[1] /* protocol */
|
||||
+ '//enso.hipchat.me/'
|
||||
+ uri.substring(regex.lastIndex); /* room (name) */
|
||||
|
||||
/* eslint-enable no-param-reassign, prefer-template */
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the scheme part of a specific URI (string) so that it contains a
|
||||
* well-known scheme such as HTTP(S). For example, the mobile app implements an
|
||||
* app-specific URI scheme in addition to Universal Links. The app-specific
|
||||
* scheme may precede or replace the well-known scheme. In such a case, dealing
|
||||
* with the app-specific scheme only complicates the logic and it is simpler to
|
||||
* get rid of it (by translating the app-specific scheme into a well-known
|
||||
* scheme).
|
||||
*
|
||||
* @param {string} uri - The URI (string) to fix the scheme of.
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
function _fixURIStringScheme(uri) {
|
||||
const regex = new RegExp(`^${_URI_PROTOCOL_PATTERN}+`, 'gi');
|
||||
const match = regex.exec(uri);
|
||||
|
||||
if (match) {
|
||||
// As an implementation convenience, pick up the last scheme and make
|
||||
// sure that it is a well-known one.
|
||||
let protocol = match[match.length - 1].toLowerCase();
|
||||
|
||||
if (protocol !== 'http:' && protocol !== 'https:') {
|
||||
protocol = 'https:';
|
||||
}
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
uri = uri.substring(regex.lastIndex);
|
||||
if (uri.startsWith('//')) {
|
||||
// The specified URL was not a room name only, it contained an
|
||||
// authority.
|
||||
uri = protocol + uri;
|
||||
}
|
||||
|
||||
/* eslint-enable no-param-reassign */
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets room name and domain from URL object.
|
||||
*
|
||||
* @param {URL} url - URL object.
|
||||
* @private
|
||||
* @returns {{
|
||||
* domain: (string|undefined),
|
||||
* room: (string|undefined)
|
||||
* }}
|
||||
* domain: (string|undefined),
|
||||
* room: (string|undefined)
|
||||
* }}
|
||||
*/
|
||||
function _getRoomAndDomainFromUrlObject(url) {
|
||||
function _getRoomAndDomainFromURLObject(url) {
|
||||
let domain;
|
||||
let room;
|
||||
|
||||
if (url) {
|
||||
domain = url.hostname;
|
||||
room = url.pathname.substr(1);
|
||||
|
||||
// The room (name) is the last component of pathname.
|
||||
room = url.pathname;
|
||||
room = room.substring(room.lastIndexOf('/') + 1);
|
||||
|
||||
// Convert empty string to undefined to simplify checks.
|
||||
if (room === '') {
|
||||
@@ -36,44 +147,6 @@ function _getRoomAndDomainFromUrlObject(url) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets conference room name and connection domain from URL.
|
||||
*
|
||||
* @param {(string|undefined)} url - URL.
|
||||
* @returns {{
|
||||
* domain: (string|undefined),
|
||||
* room: (string|undefined)
|
||||
* }}
|
||||
*/
|
||||
export function _getRoomAndDomainFromUrlString(url) {
|
||||
// Rewrite the specified URL in order to handle special cases such as
|
||||
// hipchat.com and enso.me which do not follow the common pattern of most
|
||||
// Jitsi Meet deployments.
|
||||
if (typeof url === 'string') {
|
||||
// hipchat.com
|
||||
let regex = /^(https?):\/\/hipchat.com\/video\/call\//gi;
|
||||
let match = regex.exec(url);
|
||||
|
||||
if (!match) {
|
||||
// enso.me
|
||||
regex = /^(https?):\/\/enso\.me\/(?:call|meeting)\//gi;
|
||||
match = regex.exec(url);
|
||||
}
|
||||
if (match && match.length > 1) {
|
||||
/* eslint-disable no-param-reassign, prefer-template */
|
||||
|
||||
url
|
||||
= match[1] /* URL protocol */
|
||||
+ '://enso.hipchat.me/'
|
||||
+ url.substring(regex.lastIndex);
|
||||
|
||||
/* eslint-enable no-param-reassign, prefer-template */
|
||||
}
|
||||
}
|
||||
|
||||
return _getRoomAndDomainFromUrlObject(_urlStringToObject(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
* store.
|
||||
@@ -94,24 +167,74 @@ export function _getRouteToRender(stateOrGetState) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string into a URL (object).
|
||||
* Parses a specific URI which (supposedly) references a Jitsi Meet resource
|
||||
* (location).
|
||||
*
|
||||
* @param {(string|undefined)} url - The URL to parse.
|
||||
* @private
|
||||
* @returns {URL}
|
||||
* @param {(string|undefined)} uri - The URI to parse which (supposedly)
|
||||
* references a Jitsi Meet resource (location).
|
||||
* @returns {{
|
||||
* domain: (string|undefined),
|
||||
* room: (string|undefined)
|
||||
* }}
|
||||
*/
|
||||
function _urlStringToObject(url) {
|
||||
let urlObj;
|
||||
export function _parseURIString(uri) {
|
||||
let obj;
|
||||
|
||||
if (url) {
|
||||
try {
|
||||
urlObj = new URL(url);
|
||||
} catch (ex) {
|
||||
// The return value will signal the failure & the logged exception
|
||||
// will provide the details to the developers.
|
||||
console.log(`${url} seems to be not a valid URL, but it's OK`, ex);
|
||||
if (typeof uri === 'string') {
|
||||
let str = uri;
|
||||
|
||||
str = _fixURIStringScheme(str);
|
||||
str = _fixURIStringHierPart(str);
|
||||
|
||||
obj = {};
|
||||
|
||||
let regex;
|
||||
let match;
|
||||
|
||||
// protocol
|
||||
regex = new RegExp(`^${_URI_PROTOCOL_PATTERN}`, 'gi');
|
||||
match = regex.exec(str);
|
||||
if (match) {
|
||||
obj.protocol = match[1].toLowerCase();
|
||||
str = str.substring(regex.lastIndex);
|
||||
}
|
||||
|
||||
// authority
|
||||
regex = new RegExp(`^${_URI_AUTHORITY_PATTERN}`, 'gi');
|
||||
match = regex.exec(str);
|
||||
if (match) {
|
||||
let authority = match[1].substring(/* // */ 2);
|
||||
|
||||
str = str.substring(regex.lastIndex);
|
||||
|
||||
// userinfo
|
||||
const userinfoEndIndex = authority.indexOf('@');
|
||||
|
||||
if (userinfoEndIndex !== -1) {
|
||||
authority = authority.substring(userinfoEndIndex + 1);
|
||||
}
|
||||
|
||||
obj.host = authority;
|
||||
|
||||
// port
|
||||
const portBeginIndex = authority.lastIndexOf(':');
|
||||
|
||||
if (portBeginIndex !== -1) {
|
||||
obj.port = authority.substring(portBeginIndex + 1);
|
||||
authority = authority.substring(0, portBeginIndex);
|
||||
}
|
||||
|
||||
obj.hostname = authority;
|
||||
}
|
||||
|
||||
// pathname
|
||||
regex = new RegExp(`^${_URI_PATH_PATTERN}`, 'gi');
|
||||
match = regex.exec(str);
|
||||
if (match) {
|
||||
obj.pathname = match[1] || '/';
|
||||
str = str.substring(regex.lastIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return urlObj;
|
||||
return _getRoomAndDomainFromURLObject(obj);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
|
||||
|
||||
const Logger = require('jitsi-meet-logger');
|
||||
|
||||
export { _getRoomAndDomainFromUrlString } from './functions.native';
|
||||
export { _parseURIString } from './functions.native';
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { APP_WILL_MOUNT } from '../app';
|
||||
@@ -49,7 +51,9 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
if (mode !== null) {
|
||||
AudioMode.setMode(mode)
|
||||
.catch(err =>
|
||||
console.error(`Failed to set audio mode ${mode}: ${err}`));
|
||||
console.error(
|
||||
`Failed to set audio mode ${String(mode)}: `
|
||||
+ `${err}`));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { conferenceWillLeave } from '../conference';
|
||||
import JitsiMeetJS from '../lib-jitsi-meet';
|
||||
|
||||
@@ -14,10 +18,10 @@ const JitsiConnectionEvents = JitsiMeetJS.events.connection;
|
||||
/**
|
||||
* Opens new connection.
|
||||
*
|
||||
* @returns {Promise<JitsiConnection>}
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function connect() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const state = getState();
|
||||
const connectionOptions
|
||||
= state['features/base/connection'].connectionOptions;
|
||||
@@ -57,7 +61,7 @@ export function connect() {
|
||||
* @param {string} message - Disconnect reason.
|
||||
* @returns {void}
|
||||
*/
|
||||
function connectionDisconnected(message) {
|
||||
function connectionDisconnected(message: string) {
|
||||
connection.removeEventListener(
|
||||
JitsiConnectionEvents.CONNECTION_DISCONNECTED,
|
||||
connectionDisconnected);
|
||||
@@ -110,7 +114,7 @@ export function connect() {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function disconnect() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const state = getState();
|
||||
const conference = state['features/base/conference'].conference;
|
||||
const connection = state['features/base/connection'].connection;
|
||||
@@ -148,7 +152,7 @@ export function disconnect() {
|
||||
* domain: string
|
||||
* }}
|
||||
*/
|
||||
export function setDomain(domain) {
|
||||
export function setDomain(domain: string) {
|
||||
return {
|
||||
type: SET_DOMAIN,
|
||||
domain
|
||||
@@ -167,7 +171,7 @@ export function setDomain(domain) {
|
||||
* message: string
|
||||
* }}
|
||||
*/
|
||||
function _connectionDisconnected(connection, message) {
|
||||
function _connectionDisconnected(connection, message: string) {
|
||||
return {
|
||||
type: CONNECTION_DISCONNECTED,
|
||||
connection,
|
||||
@@ -205,7 +209,7 @@ function _connectionEstablished(connection) {
|
||||
* error: string
|
||||
* }}
|
||||
*/
|
||||
function _connectionFailed(connection, error) {
|
||||
function _connectionFailed(connection, error: string) {
|
||||
return {
|
||||
type: CONNECTION_FAILED,
|
||||
connection,
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
/* global APP, JitsiMeetJS */
|
||||
/* @flow */
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import { SET_DOMAIN } from './actionTypes';
|
||||
import './reducer';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var JitsiMeetJS: Object;
|
||||
|
||||
const JitsiConferenceEvents = JitsiMeetJS.events.conference;
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
@@ -14,7 +19,7 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
* @returns {Promise<JitsiConnection>}
|
||||
*/
|
||||
export function connect() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const state = getState();
|
||||
|
||||
// XXX Lib-jitsi-meet does not accept uppercase letters.
|
||||
@@ -88,7 +93,7 @@ export function disconnect() {
|
||||
* domain: string
|
||||
* }}
|
||||
*/
|
||||
export function setDomain(domain) {
|
||||
export function setDomain(domain: string) {
|
||||
return {
|
||||
type: SET_DOMAIN,
|
||||
domain
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
/**
|
||||
* Returns current domain.
|
||||
*
|
||||
@@ -5,7 +7,7 @@
|
||||
* state.
|
||||
* @returns {(string|undefined)}
|
||||
*/
|
||||
export function getDomain(stateOrGetState) {
|
||||
export function getDomain(stateOrGetState: Function | Object) {
|
||||
const state
|
||||
= typeof stateOrGetState === 'function'
|
||||
? stateOrGetState()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import { ReducerRegistry, setStateProperty } from '../redux';
|
||||
|
||||
import {
|
||||
@@ -9,20 +11,22 @@ import {
|
||||
/**
|
||||
* Reduces the Redux actions of the feature base/connection.
|
||||
*/
|
||||
ReducerRegistry.register('features/base/connection', (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case CONNECTION_DISCONNECTED:
|
||||
return _connectionDisconnected(state, action);
|
||||
ReducerRegistry.register(
|
||||
'features/base/connection',
|
||||
(state: Object = {}, action: Object) => {
|
||||
switch (action.type) {
|
||||
case CONNECTION_DISCONNECTED:
|
||||
return _connectionDisconnected(state, action);
|
||||
|
||||
case CONNECTION_ESTABLISHED:
|
||||
return _connectionEstablished(state, action);
|
||||
case CONNECTION_ESTABLISHED:
|
||||
return _connectionEstablished(state, action);
|
||||
|
||||
case SET_DOMAIN:
|
||||
return _setDomain(state, action);
|
||||
}
|
||||
case SET_DOMAIN:
|
||||
return _setDomain(state, action);
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
return state;
|
||||
});
|
||||
|
||||
/**
|
||||
* Reduces a specific Redux action CONNECTION_DISCONNECTED of the feature
|
||||
@@ -34,7 +38,7 @@ ReducerRegistry.register('features/base/connection', (state = {}, action) => {
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _connectionDisconnected(state, action) {
|
||||
function _connectionDisconnected(state: Object, action: Object) {
|
||||
if (state.connection === action.connection) {
|
||||
return setStateProperty(state, 'connection', undefined);
|
||||
}
|
||||
@@ -52,7 +56,7 @@ function _connectionDisconnected(state, action) {
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _connectionEstablished(state, action) {
|
||||
function _connectionEstablished(state: Object, action: Object) {
|
||||
return setStateProperty(state, 'connection', action.connection);
|
||||
}
|
||||
|
||||
@@ -65,7 +69,7 @@ function _connectionEstablished(state, action) {
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _constructConnectionOptions(domain) {
|
||||
function _constructConnectionOptions(domain: string) {
|
||||
// FIXME The HTTPS scheme for the BOSH URL works with meet.jit.si on both
|
||||
// mobile & Web. It also works with beta.meet.jit.si on Web. Unfortunately,
|
||||
// it doesn't work with beta.meet.jit.si on mobile. Temporarily, use the
|
||||
@@ -89,7 +93,7 @@ function _constructConnectionOptions(domain) {
|
||||
boshProtocol || (boshProtocol = 'https:');
|
||||
|
||||
return {
|
||||
bosh: `${boshProtocol}//${domain}/http-bind`,
|
||||
bosh: `${String(boshProtocol)}//${domain}/http-bind`,
|
||||
hosts: {
|
||||
domain,
|
||||
focus: `focus.${domain}`,
|
||||
@@ -107,7 +111,7 @@ function _constructConnectionOptions(domain) {
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setDomain(state, action) {
|
||||
function _setDomain(state: Object, action: Object) {
|
||||
return {
|
||||
...state,
|
||||
connectionOptions: {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
SET_AUDIO_MUTED,
|
||||
SET_CAMERA_FACING_MODE,
|
||||
@@ -17,7 +21,7 @@ import './reducer';
|
||||
* muted: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setAudioMuted(muted) {
|
||||
export function setAudioMuted(muted: boolean) {
|
||||
return {
|
||||
type: SET_AUDIO_MUTED,
|
||||
muted
|
||||
@@ -33,7 +37,7 @@ export function setAudioMuted(muted) {
|
||||
* cameraFacingMode: CAMERA_FACING_MODE
|
||||
* }}
|
||||
*/
|
||||
export function setCameraFacingMode(cameraFacingMode) {
|
||||
export function setCameraFacingMode(cameraFacingMode: CAMERA_FACING_MODE) {
|
||||
return {
|
||||
type: SET_CAMERA_FACING_MODE,
|
||||
cameraFacingMode
|
||||
@@ -50,7 +54,7 @@ export function setCameraFacingMode(cameraFacingMode) {
|
||||
* muted: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setVideoMuted(muted) {
|
||||
export function setVideoMuted(muted: boolean) {
|
||||
return {
|
||||
type: SET_VIDEO_MUTED,
|
||||
muted
|
||||
@@ -63,7 +67,7 @@ export function setVideoMuted(muted) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleAudioMuted() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const muted = getState()['features/base/media'].audio.muted;
|
||||
|
||||
return dispatch(setAudioMuted(!muted));
|
||||
@@ -76,7 +80,7 @@ export function toggleAudioMuted() {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleCameraFacingMode() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
let cameraFacingMode
|
||||
= getState()['features/base/media'].video.facingMode;
|
||||
|
||||
@@ -95,7 +99,7 @@ export function toggleCameraFacingMode() {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleVideoMuted() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const muted = getState()['features/base/media'].video.muted;
|
||||
|
||||
return dispatch(setVideoMuted(!muted));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import { CONFERENCE_LEFT } from '../conference';
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
import { setTrackMuted, TRACK_ADDED } from '../tracks';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
// Re-export react-native's Platform because we want to provide a minimal
|
||||
// equivalent on Web.
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
const userAgent = navigator.userAgent;
|
||||
let OS;
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import { Component } from 'react';
|
||||
|
||||
/**
|
||||
* Object describing application route.
|
||||
*
|
||||
@@ -5,12 +9,18 @@
|
||||
* @property {Component} component - React Component constructor.
|
||||
* @property {string} path - URL route, required for web routing.
|
||||
*/
|
||||
type Route = {
|
||||
component: Class<Component<*>>, // eslint-disable-line no-undef
|
||||
path: string
|
||||
};
|
||||
|
||||
/**
|
||||
* A registry for Navigator routes, allowing features to register themselves
|
||||
* without needing to create additional inter-feature dependencies.
|
||||
*/
|
||||
class RouteRegistry {
|
||||
_elements: Array<Route>;
|
||||
|
||||
/**
|
||||
* Initializes a new RouteRegistry instance.
|
||||
*/
|
||||
@@ -19,8 +29,9 @@ class RouteRegistry {
|
||||
* The set of registered routes.
|
||||
*
|
||||
* @private
|
||||
* @type {Route[]}
|
||||
*/
|
||||
this._elements = new Set();
|
||||
this._elements = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +43,7 @@ class RouteRegistry {
|
||||
* @returns {boolean} True if the specified a and b describe one and the
|
||||
* same abstract route; otherwise, false.
|
||||
*/
|
||||
areRoutesEqual(a, b) {
|
||||
areRoutesEqual(a: Route, b: Route) {
|
||||
if (a === b) { // reflexive
|
||||
return true;
|
||||
}
|
||||
@@ -43,9 +54,12 @@ class RouteRegistry {
|
||||
return !a;
|
||||
}
|
||||
|
||||
const aKeys = Object.keys(a);
|
||||
const bKeys = Object.keys(b);
|
||||
|
||||
return (
|
||||
Object.keys(a).every(key => a[key] === b[key])
|
||||
&& /* symmetric */ this.areRoutesEqual(b, a));
|
||||
aKeys.length === bKeys.length /* symmetric */
|
||||
&& aKeys.every(key => a[key] === b[key]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,21 +71,25 @@ class RouteRegistry {
|
||||
// We use the destructuring operator to 'clone' the route object to
|
||||
// prevent modifications from outside (e.g. React Native's Navigator
|
||||
// extends it with additional properties).
|
||||
return [ ...this._elements ].map(r => {
|
||||
return this._elements.map(r => {
|
||||
return { ...r };
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
/**
|
||||
* Returns registered route by name if any.
|
||||
*
|
||||
* @param {Object} component - The React Component (class) of the route to
|
||||
* retrieve.
|
||||
* @param {Component} component - The React Component (class) of the route
|
||||
* to retrieve.
|
||||
* @returns {Route|null}
|
||||
*/
|
||||
getRouteByComponent(component) {
|
||||
const route
|
||||
= [ ...this._elements ].find(r => r.component === component);
|
||||
getRouteByComponent(component: Class<Component<*>>) {
|
||||
|
||||
/* eslint-enable no-undef */
|
||||
|
||||
const route = this._elements.find(r => r.component === component);
|
||||
|
||||
// We use destructuring operator to 'clone' route object to prevent
|
||||
// modifications from outside (e.g. React Native's Navigator extends
|
||||
@@ -85,12 +103,13 @@ class RouteRegistry {
|
||||
* @param {Route} route - Route definition object.
|
||||
* @returns {void}
|
||||
*/
|
||||
register(route) {
|
||||
if (this._elements.has(route)) {
|
||||
throw new Error(`Route ${route.component} is registered already!`);
|
||||
register(route: Route) {
|
||||
if (this._elements.includes(route)) {
|
||||
throw new Error(
|
||||
`Route ${String(route.component)} is registered already!`);
|
||||
}
|
||||
|
||||
this._elements.add(route);
|
||||
this._elements.push(route);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
/* global APP, interfaceConfig */
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* The CSS style of the element with CSS class <tt>rightwatermark</tt>.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const RIGHT_WATERMARK_STYLE = {
|
||||
const _RIGHT_WATERMARK_STYLE = {
|
||||
backgroundImage: 'url(images/rightwatermark.png)'
|
||||
};
|
||||
|
||||
@@ -14,13 +19,22 @@ const RIGHT_WATERMARK_STYLE = {
|
||||
* etc.
|
||||
*/
|
||||
export class Watermarks extends Component {
|
||||
state = {
|
||||
brandWatermarkLink: String,
|
||||
jitsiWatermarkLink: String,
|
||||
showBrandWatermark: Boolean,
|
||||
showJitsiWatermark: Boolean,
|
||||
showJitsiWatermarkForGuests: Boolean,
|
||||
showPoweredBy: Boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new Watermarks instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
|
||||
let showBrandWatermark;
|
||||
@@ -87,7 +101,7 @@ export class Watermarks extends Component {
|
||||
target = '_new'>
|
||||
<div
|
||||
className = 'watermark rightwatermark'
|
||||
style = { RIGHT_WATERMARK_STYLE } />
|
||||
style = { _RIGHT_WATERMARK_STYLE } />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
/* eslint-disable flowtype/space-before-type-colon */
|
||||
|
||||
/**
|
||||
* Prevents further propagation of the events to be handler by a specific event
|
||||
* handler/listener in the capturing and bubbling phases.
|
||||
@@ -7,8 +11,12 @@
|
||||
* @returns {Function} An event handler/listener to be used in place of the
|
||||
* specified eventHandler in order to stop the events from propagating.
|
||||
*/
|
||||
export function stopEventPropagation(eventHandler) {
|
||||
return ev => {
|
||||
export function stopEventPropagation<T>(eventHandler: (ev: Event) => T)
|
||||
: (ev: Event) => T {
|
||||
|
||||
/* eslint-enable flowtype/space-before-type-colon */
|
||||
|
||||
return (ev: Event): T => {
|
||||
const r = eventHandler(ev);
|
||||
|
||||
// React Native does not propagate the press event so, for the sake of
|
||||
|
||||
@@ -1,33 +1,45 @@
|
||||
/* @flow */
|
||||
|
||||
import { applyMiddleware } from 'redux';
|
||||
import type { Middleware } from 'redux';
|
||||
|
||||
/**
|
||||
* A registry for Redux middleware, allowing features to register their
|
||||
* middleware without needing to create additional inter-feature dependencies.
|
||||
*/
|
||||
class MiddlewareRegistry {
|
||||
_elements: Array<Middleware<*, *>>;
|
||||
|
||||
/**
|
||||
* Creates a MiddlewareRegistry instance.
|
||||
*/
|
||||
constructor() {
|
||||
/**
|
||||
* The set of registered middleware.
|
||||
*
|
||||
* @private
|
||||
* @type {Middleware[]}
|
||||
*/
|
||||
this._elements = new Set();
|
||||
this._elements = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all registered middleware into a store enhancer.
|
||||
* (@link http://redux.js.org/docs/api/applyMiddleware.html).
|
||||
*
|
||||
* @param {Function[]} additional - Any additional middleware that need to
|
||||
* @param {Middleware[]} additional - Any additional middleware that need to
|
||||
* be included (such as middleware from third-party modules).
|
||||
* @returns {Function}
|
||||
* @returns {Middleware}
|
||||
*/
|
||||
applyMiddleware(...additional) {
|
||||
return applyMiddleware(
|
||||
applyMiddleware(...additional: Array<Middleware<*, *>>) {
|
||||
// XXX The explicit definition of the local variable middlewares is to
|
||||
// satisfy flow.
|
||||
const middlewares = [
|
||||
...this._elements,
|
||||
...additional
|
||||
);
|
||||
];
|
||||
|
||||
return applyMiddleware(...middlewares);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,11 +47,11 @@ class MiddlewareRegistry {
|
||||
*
|
||||
* The method is to be invoked only before {@link #applyMiddleware()}.
|
||||
*
|
||||
* @param {Function} middleware - A Redux middleware.
|
||||
* @param {Middleware} middleware - A Redux middleware.
|
||||
* @returns {void}
|
||||
*/
|
||||
register(middleware) {
|
||||
this._elements.add(middleware);
|
||||
register(middleware: Middleware<*, *>) {
|
||||
this._elements.push(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
/* @flow */
|
||||
|
||||
import { combineReducers } from 'redux';
|
||||
import type { Reducer } from 'redux';
|
||||
|
||||
/**
|
||||
* The type of the dictionary/map which associates a reducer (function) with the
|
||||
* name of he Redux state property managed by the reducer.
|
||||
*/
|
||||
declare type NameReducerMap<S, A> = { [name: string]: Reducer<S, A> };
|
||||
|
||||
/**
|
||||
* A registry for Redux reducers, allowing features to register themselves
|
||||
* without needing to create additional inter-feature dependencies.
|
||||
*/
|
||||
class ReducerRegistry {
|
||||
_elements: NameReducerMap<*, *>;
|
||||
|
||||
/**
|
||||
* Creates a ReducerRegistry instance.
|
||||
*/
|
||||
@@ -12,6 +23,9 @@ class ReducerRegistry {
|
||||
/**
|
||||
* The set of registered reducers, keyed based on the field each reducer
|
||||
* will manage.
|
||||
*
|
||||
* @private
|
||||
* @type {NameReducerMap}
|
||||
*/
|
||||
this._elements = {};
|
||||
}
|
||||
@@ -23,7 +37,7 @@ class ReducerRegistry {
|
||||
* included (such as reducers from third-party modules).
|
||||
* @returns {Function}
|
||||
*/
|
||||
combineReducers(additional = {}) {
|
||||
combineReducers(additional: NameReducerMap<*, *> = {}) {
|
||||
return combineReducers({
|
||||
...this._elements,
|
||||
...additional
|
||||
@@ -37,10 +51,10 @@ class ReducerRegistry {
|
||||
*
|
||||
* @param {string} name - The field in the state object that will be managed
|
||||
* by the provided reducer.
|
||||
* @param {Function} reducer - A Redux reducer.
|
||||
* @param {Reducer} reducer - A Redux reducer.
|
||||
* @returns {void}
|
||||
*/
|
||||
register(name, reducer) {
|
||||
register(name: string, reducer: Reducer<*, *>) {
|
||||
this._elements[name] = reducer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import { LIB_DISPOSED, LIB_INITIALIZED } from '../lib-jitsi-meet';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
@@ -67,7 +69,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @returns {Track} The local <tt>Track</tt> associated with the specified
|
||||
* <tt>mediaType</tt> in the specified <tt>store</tt>.
|
||||
*/
|
||||
function _getLocalTrack(store, mediaType) {
|
||||
function _getLocalTrack(store, mediaType: MEDIA_TYPE) {
|
||||
return getLocalTrack(store.getState()['features/base/tracks'], mediaType);
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ function _getLocalTrack(store, mediaType) {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _setMuted(store, action, mediaType) {
|
||||
function _setMuted(store, action, mediaType: MEDIA_TYPE) {
|
||||
const localTrack = _getLocalTrack(store, mediaType);
|
||||
|
||||
localTrack && setTrackMuted(localTrack.jitsiTrack, action.muted);
|
||||
|
||||
@@ -5,11 +5,11 @@ import { UnsupportedMobileBrowser } from '../../unsupported-browser';
|
||||
* Array of rules defining whether we should intercept component to render
|
||||
* or not.
|
||||
*
|
||||
* @type {Array<Function>}
|
||||
* @param {Object} state - Redux state object.
|
||||
* @private
|
||||
* @returns {ReactElement|void}
|
||||
* @type {Function[]}
|
||||
*/
|
||||
const RULES = [
|
||||
const _RULES = [
|
||||
|
||||
/**
|
||||
* This rule describes case when user opens application using mobile
|
||||
@@ -46,7 +46,7 @@ export function interceptComponent(stateOrGetState, currentComponent) {
|
||||
? stateOrGetState()
|
||||
: stateOrGetState;
|
||||
|
||||
for (const rule of RULES) {
|
||||
for (const rule of _RULES) {
|
||||
result = rule(state);
|
||||
if (result) {
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
/**
|
||||
* Alphanumeric characters.
|
||||
* @const
|
||||
@@ -18,7 +20,7 @@ const HEX_DIGITS = '0123456789abcdef';
|
||||
* @returns {string} A string of random alphanumeric characters with the
|
||||
* specified length.
|
||||
*/
|
||||
export function randomAlphanumString(length) {
|
||||
export function randomAlphanumString(length: number) {
|
||||
return _randomString(length, ALPHANUM);
|
||||
}
|
||||
|
||||
@@ -28,7 +30,7 @@ export function randomAlphanumString(length) {
|
||||
* @param {Array|string} arr - Source.
|
||||
* @returns {Array|string} Array element or string character.
|
||||
*/
|
||||
export function randomElement(arr) {
|
||||
export function randomElement(arr: [any] | string) {
|
||||
return arr[randomInt(0, arr.length - 1)];
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ export function randomHexDigit() {
|
||||
* @returns {string} A string of random hexadecimal digits with the specified
|
||||
* length.
|
||||
*/
|
||||
export function randomHexString(length) {
|
||||
export function randomHexString(length: number) {
|
||||
return _randomString(length, HEX_DIGITS);
|
||||
}
|
||||
|
||||
@@ -59,7 +61,7 @@ export function randomHexString(length) {
|
||||
* @param {number} max - The maximum value for the generated number.
|
||||
* @returns {number} Random int number.
|
||||
*/
|
||||
export function randomInt(min, max) {
|
||||
export function randomInt(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import { randomElement } from './randomUtil';
|
||||
|
||||
/*
|
||||
@@ -150,7 +152,7 @@ const _CONJUNCTION_ = [
|
||||
* Maps a string (category name) to the array of words from that category.
|
||||
* @const
|
||||
*/
|
||||
const CATEGORIES = {
|
||||
const CATEGORIES: { [key: string]: Array<string> } = {
|
||||
_ADJECTIVE_,
|
||||
_ADVERB_,
|
||||
_PLURALNOUN_,
|
||||
|
||||
@@ -13,11 +13,14 @@ import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* The timeout in milliseconds after which the toolbar will be hidden.
|
||||
*
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
const TOOLBAR_TIMEOUT_MS = 5000;
|
||||
const _TOOLBAR_TIMEOUT_MS = 5000;
|
||||
|
||||
/**
|
||||
* The conference page of the application.
|
||||
* The conference page of the mobile (i.e. React Native) application.
|
||||
*/
|
||||
class Conference extends Component {
|
||||
/**
|
||||
@@ -220,7 +223,7 @@ class Conference extends Component {
|
||||
this._clearToolbarTimeout();
|
||||
if (toolbarVisible) {
|
||||
this._toolbarTimeout
|
||||
= setTimeout(this._onClick, TOOLBAR_TIMEOUT_MS);
|
||||
= setTimeout(this._onClick, _TOOLBAR_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,22 @@ import { connect as reactReduxConnect } from 'react-redux';
|
||||
|
||||
import { connect, disconnect } from '../../base/connection';
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { FeedbackButton } from '../../feedback';
|
||||
|
||||
/**
|
||||
* For legacy reasons, inline style for display none.
|
||||
* @type {{display: string}}
|
||||
*
|
||||
* @private
|
||||
* @type {{
|
||||
* display: string
|
||||
* }}
|
||||
*/
|
||||
const DISPLAY_NONE_STYLE = {
|
||||
const _DISPLAY_NONE_STYLE = {
|
||||
display: 'none'
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React Component which renders initial conference layout
|
||||
* The conference page of the Web application.
|
||||
*/
|
||||
class Conference extends Component {
|
||||
|
||||
@@ -67,7 +72,7 @@ class Conference extends Component {
|
||||
<div
|
||||
className = 'notice'
|
||||
id = 'notice'
|
||||
style = { DISPLAY_NONE_STYLE }>
|
||||
style = { _DISPLAY_NONE_STYLE }>
|
||||
<span
|
||||
className = 'noticeText'
|
||||
id = 'noticeText' />
|
||||
@@ -83,9 +88,9 @@ class Conference extends Component {
|
||||
className = 'toolbar'
|
||||
id = 'extendedToolbar'>
|
||||
<div id = 'extendedToolbarButtons' />
|
||||
<a
|
||||
className = 'button icon-feedback'
|
||||
id = 'feedbackButton' />
|
||||
|
||||
<FeedbackButton />
|
||||
|
||||
<div id = 'sideToolbarContainer' />
|
||||
</div>
|
||||
<div id = 'videospace'>
|
||||
@@ -134,16 +139,14 @@ class Conference extends Component {
|
||||
<span
|
||||
className = 'videocontainer'
|
||||
id = 'localVideoContainer'>
|
||||
<div
|
||||
className = 'videocontainer__background' />
|
||||
<div className = 'videocontainer__background' />
|
||||
<span id = 'localVideoWrapper' />
|
||||
<audio
|
||||
autoPlay = { true }
|
||||
id = 'localAudio'
|
||||
muted = { true } />
|
||||
<div className = 'videocontainer__toolbar' />
|
||||
<div
|
||||
className = 'videocontainer__toptoolbar' />
|
||||
<div className = 'videocontainer__toptoolbar' />
|
||||
<div
|
||||
className
|
||||
= 'videocontainer__hoverOverlay' />
|
||||
|
||||
@@ -40,7 +40,8 @@ function _initConference() {
|
||||
* Promise wrapper on obtain config method. When HttpConfigFetch will be moved
|
||||
* to React app it's better to use load config instead.
|
||||
*
|
||||
* @param {string} location - URL of the domain.
|
||||
* @param {string} location - URL of the domain from which the config is to be
|
||||
* obtained.
|
||||
* @param {string} room - Room name.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
|
||||
47
react/features/feedback/components/FeedbackButton.web.js
Normal file
47
react/features/feedback/components/FeedbackButton.web.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
declare var config: Object;
|
||||
|
||||
/**
|
||||
* Implements a Web/React Component which renders a feedback button.
|
||||
*/
|
||||
export class FeedbackButton extends Component {
|
||||
state = {
|
||||
callStatsID: String
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new FeedbackButton instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
callStatsID: config.callStatsID
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
// If callstats.io-support is not configured, skip rendering.
|
||||
if (!this.state.callStatsID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className = 'button icon-feedback'
|
||||
id = 'feedbackButton' />
|
||||
);
|
||||
}
|
||||
}
|
||||
1
react/features/feedback/components/index.js
Normal file
1
react/features/feedback/components/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './FeedbackButton';
|
||||
1
react/features/feedback/index.js
Normal file
1
react/features/feedback/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './components';
|
||||
1
react/features/full-screen/index.js
Normal file
1
react/features/full-screen/index.js
Normal file
@@ -0,0 +1 @@
|
||||
import './middleware';
|
||||
76
react/features/full-screen/middleware.js
Normal file
76
react/features/full-screen/middleware.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/* @flow */
|
||||
|
||||
import { StatusBar } from 'react-native';
|
||||
import { Immersive } from 'react-native-immersive';
|
||||
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_LEFT,
|
||||
CONFERENCE_WILL_JOIN
|
||||
} from '../base/conference';
|
||||
import { Platform } from '../base/react';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
/**
|
||||
* Middleware that captures conference actions and activates or deactivates the
|
||||
* full screen mode. On iOS it hides the status bar, and on Android it uses the
|
||||
* immersive mode:
|
||||
* https://developer.android.com/training/system-ui/immersive.html
|
||||
* In immersive mode the status and navigation bars are hidden and thus the
|
||||
* entire screen will be covered by our application.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
let fullScreen;
|
||||
|
||||
switch (action.type) {
|
||||
case CONFERENCE_WILL_JOIN: {
|
||||
const conference = store.getState()['features/base/conference'];
|
||||
|
||||
fullScreen = !conference.audioOnly;
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_FAILED:
|
||||
case CONFERENCE_LEFT:
|
||||
fullScreen = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
fullScreen = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fullScreen !== null) {
|
||||
_setFullScreen(fullScreen)
|
||||
.catch(err =>
|
||||
console.warn(`Failed to set full screen mode: ${err}`));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Activates/deactivates the full screen mode. On iOS it will hide the status
|
||||
* bar, and on Android it will turn immersive mode on.
|
||||
*
|
||||
* @param {boolean} fullScreen - True to set full screen mode, false to
|
||||
* deactivate it.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _setFullScreen(fullScreen: boolean) {
|
||||
// XXX The React Native module Immersive is only implemented on Android and
|
||||
// throws on other platforms.
|
||||
if (Platform.OS === 'android') {
|
||||
return fullScreen ? Immersive.on() : Immersive.off();
|
||||
}
|
||||
|
||||
// On platforms other than Android go with whatever React Native itself
|
||||
// supports.
|
||||
StatusBar.setHidden(fullScreen, 'slide');
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -6,8 +6,10 @@ import { Platform } from '../../base/react';
|
||||
/**
|
||||
* The map of platforms to URLs at which the mobile app for the associated
|
||||
* platform is available for download.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const URLS = {
|
||||
const _URLS = {
|
||||
android: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
|
||||
ios: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905'
|
||||
};
|
||||
@@ -19,7 +21,7 @@ const URLS = {
|
||||
*/
|
||||
class UnsupportedMobileBrowser extends Component {
|
||||
/**
|
||||
* Mobile browser page component's property types.
|
||||
* UnsupportedMobileBrowser component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
@@ -35,35 +37,32 @@ class UnsupportedMobileBrowser extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor of UnsupportedMobileBrowser component.
|
||||
* Initializes the text and URL of the `Start a conference` / `Join the
|
||||
* conversation` button which takes the user to the mobile app.
|
||||
*
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the new instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind methods
|
||||
this._onJoinClick = this._onJoinClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* React lifecycle method triggered before component will mount.
|
||||
*
|
||||
* @returns {void}
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentWillMount() {
|
||||
const joinButtonText
|
||||
const joinText
|
||||
= this.props._room ? 'Join the conversation' : 'Start a conference';
|
||||
|
||||
// If the user installed the app while this Component was displayed
|
||||
// (e.g. the user clicked the Download the App button), then we would
|
||||
// like to open the current URL in the mobile app. The only way to do it
|
||||
// appears to be a link with an app-specific scheme, not a Universal
|
||||
// Link.
|
||||
const joinURL = `org.jitsi.meet:${window.location.href}`;
|
||||
|
||||
this.setState({
|
||||
joinButtonText
|
||||
joinText,
|
||||
joinURL
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders component.
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
@@ -75,12 +74,12 @@ class UnsupportedMobileBrowser extends Component {
|
||||
<div className = { `${ns}__body` }>
|
||||
<img
|
||||
className = { `${ns}__logo` }
|
||||
src = '/images/logo-blue.svg' />
|
||||
src = 'images/logo-blue.svg' />
|
||||
<p className = { `${ns}__text` }>
|
||||
You need <strong>Jitsi Meet</strong> to join a
|
||||
conversation on your mobile
|
||||
</p>
|
||||
<a href = { URLS[Platform.OS] }>
|
||||
<a href = { _URLS[Platform.OS] }>
|
||||
<button className = { downloadButtonClassName }>
|
||||
Download the App
|
||||
</button>
|
||||
@@ -90,32 +89,47 @@ class UnsupportedMobileBrowser extends Component {
|
||||
<br />
|
||||
<strong>then</strong>
|
||||
</p>
|
||||
<button
|
||||
className = { `${ns}__button` }
|
||||
onClick = { this._onJoinClick }>
|
||||
{
|
||||
this.state.joinButtonText
|
||||
}
|
||||
</button>
|
||||
<a href = { this.state.joinURL }>
|
||||
<button className = { `${ns}__button` }>
|
||||
{
|
||||
this.state.joinText
|
||||
}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{
|
||||
this._renderStyle()
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks on the button that joins the local participant in a
|
||||
* conference.
|
||||
* Renders an HTML style element with CSS specific to
|
||||
* this UnsupportedMobileBrowser.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_onJoinClick() {
|
||||
// If the user installed the app while this Component was displayed
|
||||
// (e.g. the user clicked the Download the App button), then we would
|
||||
// like to open the current URL in the mobile app.
|
||||
|
||||
// TODO The only way to do it appears to be a link with an app-specific
|
||||
// scheme, not a Universal Link.
|
||||
_renderStyle() {
|
||||
// Temasys provide lib-jitsi-meet/modules/RTC/adapter.screenshare.js
|
||||
// which detects whether the browser supports WebRTC. If the browser
|
||||
// does not support WebRTC, it displays an alert in the form of a yellow
|
||||
// bar at the top of the page. The alert notifies the user that the
|
||||
// browser does not support WebRTC and, if Temasys provide a plugin for
|
||||
// the browser, the alert contains a button to initiate installing the
|
||||
// browser. When Temasys do not provide a plugin for the browser, we do
|
||||
// not want the alert on the unsupported-browser page because the
|
||||
// notification about the lack of WebRTC support is the whole point of
|
||||
// the unsupported-browser page.
|
||||
return (
|
||||
<style type = 'text/css'>
|
||||
{
|
||||
'iframe[name="adapterjs-alert"] { display: none; }'
|
||||
}
|
||||
</style>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,13 +76,18 @@ class WelcomePage extends AbstractWelcomePage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain name.
|
||||
* Returns the URL of this WelcomePage for display purposes. For
|
||||
* historic/legacy reasons, the return value is referred to as domain.
|
||||
*
|
||||
* @private
|
||||
* @returns {string} Domain name.
|
||||
* @returns {string} The URL of this WelcomePage for display purposes.
|
||||
*/
|
||||
_getDomain() {
|
||||
return `${window.location.protocol}//${window.location.host}/`;
|
||||
// As the returned URL is for display purposes, do not return the
|
||||
// userinfo, query and fragment URI parts.
|
||||
const wl = window.location;
|
||||
|
||||
return `${wl.protocol}//${wl.host}${wl.pathname}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
26
utils.js
26
utils.js
@@ -10,26 +10,22 @@
|
||||
* Builds and returns the room name.
|
||||
*/
|
||||
function getRoomName () { // eslint-disable-line no-unused-vars
|
||||
var getroomnode = config.getroomnode;
|
||||
var path = window.location.pathname;
|
||||
var roomName;
|
||||
|
||||
// determinde the room node from the url
|
||||
// TODO: just the roomnode or the whole bare jid?
|
||||
if (config.getroomnode && typeof config.getroomnode === 'function') {
|
||||
// Determine the room node from the URL.
|
||||
if (getroomnode && typeof getroomnode === 'function') {
|
||||
// custom function might be responsible for doing the pushstate
|
||||
roomName = config.getroomnode(path);
|
||||
roomName = getroomnode.call(config, path);
|
||||
} else {
|
||||
/* fall back to default strategy
|
||||
* this is making assumptions about how the URL->room mapping happens.
|
||||
* It currently assumes deployment at root, with a rewrite like the
|
||||
* following one (for nginx):
|
||||
location ~ ^/([a-zA-Z0-9]+)$ {
|
||||
rewrite ^/(.*)$ / break;
|
||||
}
|
||||
*/
|
||||
if (path.length > 1) {
|
||||
roomName = path.substr(1).toLowerCase();
|
||||
}
|
||||
// Fall back to the default strategy of making assumptions about how the
|
||||
// URL maps to the room (name). It currently assumes a deployment in
|
||||
// which the last non-directory component of the path (name) is the
|
||||
// room.
|
||||
roomName
|
||||
= path.substring(path.lastIndexOf('/') + 1).toLowerCase()
|
||||
|| undefined;
|
||||
}
|
||||
|
||||
return roomName;
|
||||
|
||||
@@ -33,6 +33,23 @@ if (minimize) {
|
||||
NODE_ENV: JSON.stringify('production')
|
||||
}
|
||||
}));
|
||||
|
||||
// While webpack will automatically insert UglifyJsPlugin when minimize is
|
||||
// true, the defaults of UglifyJsPlugin in webpack 1 and webpack 2 are
|
||||
// different. Explicitly state what we want even if we want defaults in
|
||||
// order to prepare for webpack 2.
|
||||
plugins.push(new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
// It is nice to see warnings from UglifyJsPlugin that something is
|
||||
// unused and, consequently, is removed. The default is false in
|
||||
// webpack 2.
|
||||
warnings: true
|
||||
},
|
||||
|
||||
// Use the source map to map error message locations to modules. The
|
||||
// default is false in webpack 2.
|
||||
sourceMap: true
|
||||
}));
|
||||
}
|
||||
|
||||
// The base Webpack configuration to bundle the JavaScript artifacts of
|
||||
@@ -56,7 +73,7 @@ var config = {
|
||||
// as well.
|
||||
|
||||
exclude: node_modules,
|
||||
loader: 'babel',
|
||||
loader: 'babel-loader',
|
||||
query: {
|
||||
// XXX The require.resolve bellow solves failures to locate the
|
||||
// presets when lib-jitsi-meet, for example, is npm linked in
|
||||
@@ -74,20 +91,20 @@ var config = {
|
||||
// to be available in such a form by multiple jitsi-meet
|
||||
// dependencies including AUI, lib-jitsi-meet.
|
||||
|
||||
loader: 'expose?$!expose?jQuery',
|
||||
loader: 'expose-loader?$!expose-loader?jQuery',
|
||||
test: /\/node_modules\/jquery\/.*\.js$/
|
||||
}, {
|
||||
// Disable AMD for the Strophe.js library or its imports will fail
|
||||
// at runtime.
|
||||
|
||||
loader: 'imports?define=>false&this=>window',
|
||||
loader: 'imports-loader?define=>false&this=>window',
|
||||
test: strophe
|
||||
}, {
|
||||
// Allow CSS to be imported into JavaScript.
|
||||
|
||||
loaders: [
|
||||
'style',
|
||||
'css'
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
],
|
||||
test: /\.css$/
|
||||
}, {
|
||||
@@ -95,7 +112,7 @@ var config = {
|
||||
// by CSS into the output path.
|
||||
|
||||
include: aui_css,
|
||||
loader: 'file',
|
||||
loader: 'file-loader',
|
||||
query: {
|
||||
context: aui_css,
|
||||
name: '[path][name].[ext]'
|
||||
@@ -104,7 +121,7 @@ var config = {
|
||||
}, {
|
||||
// Enable the import of JSON files.
|
||||
|
||||
loader: 'json',
|
||||
loader: 'json-loader',
|
||||
exclude: node_modules,
|
||||
test: /\.json$/
|
||||
} ],
|
||||
|
||||
Reference in New Issue
Block a user