Compare commits
1 Commits
1986
...
letsencryp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
029ca1753f |
19
.flowconfig
@@ -19,7 +19,6 @@
|
||||
.*/node_modules/babel-core/.*
|
||||
.*/node_modules/bower/.*
|
||||
.*/node_modules/jsonlint/.*
|
||||
.*/node_modules/styled-components/.*
|
||||
|
||||
[include]
|
||||
|
||||
@@ -35,11 +34,6 @@ module.system=haste
|
||||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
; FIXME: munge_underscores should be false but right now there are some errors
|
||||
; if we change the value to false
|
||||
; Treats class properties with underscore as private. Disabled because currently
|
||||
; for us "_" can mean protected too.
|
||||
; munge_underscores=false
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
@@ -51,21 +45,8 @@ suppress_type=$FixMe
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-8]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-8]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowDisableNextLine
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
; We (i.e. the jitsi-meet project) are using the haste module system on Web as
|
||||
; well, not only on React Native. Unfortunately, Flow does not support .web.js
|
||||
; by default. Override Flow's defaults to include .web.js as well. Technically,
|
||||
; we have .native.js as well so the choice of .web.js may lead to errors.
|
||||
; Practically though, it is a potential future problem that we do not have at
|
||||
; the time of this writing.
|
||||
module.file_ext=.web.js
|
||||
; Flow's defaults:
|
||||
module.file_ext=.js
|
||||
module.file_ext=.jsx
|
||||
module.file_ext=.json
|
||||
|
||||
[version]
|
||||
^0.38.0
|
||||
|
||||
@@ -8,9 +8,6 @@ node_modules/
|
||||
# The following are checked by ESLint with the maximum configuration which
|
||||
# supersedes JSHint.
|
||||
flow-typed/
|
||||
modules/API/
|
||||
modules/remotecontrol/
|
||||
modules/transport/
|
||||
react/
|
||||
|
||||
# The following are checked by ESLint with the minimum configuration which does
|
||||
|
||||
@@ -3,9 +3,13 @@ We would love to have your help. Before you start working however, please read
|
||||
and follow this short guide.
|
||||
|
||||
# Reporting Issues
|
||||
Provide as much information as possible. Mention the version of Jitsi Meet,
|
||||
Jicofo and JVB you are using, and explain (as detailed as you can) how the
|
||||
problem can be reproduced.
|
||||
Before you open an issue on GitHub, please discuss it on one of our
|
||||
[mailing lists](https://jitsi.org/Development/MailingLists) and wait for
|
||||
confirmation from one of the committers. Once you have that confirmation,
|
||||
please proceed to reporting the issue on GitHub, while providing as much
|
||||
information as possible. Mention the version of Jitsi Meet, Jicofo and JVB
|
||||
you are using, and explain (as detailed as you can) how the problem can
|
||||
be reproduced.
|
||||
|
||||
# Code contributions
|
||||
Found a bug and know how to fix it? Great! Please read on.
|
||||
|
||||
@@ -2,10 +2,3 @@
|
||||
* Notifies interested parties that hangup procedure will start.
|
||||
*/
|
||||
export const BEFORE_HANGUP = "conference.before_hangup";
|
||||
|
||||
/**
|
||||
* Notifies interested parties that desktop sharing enable/disable state is
|
||||
* changed.
|
||||
*/
|
||||
export const DESKTOP_SHARING_ENABLED_CHANGED
|
||||
= "conference.desktop_sharing_enabled_changed";
|
||||
|
||||
2
Makefile
@@ -27,8 +27,6 @@ deploy-appbundle:
|
||||
cp \
|
||||
$(BUILD_DIR)/app.bundle.min.js \
|
||||
$(BUILD_DIR)/app.bundle.min.map \
|
||||
$(BUILD_DIR)/do_external_connect.min.js \
|
||||
$(BUILD_DIR)/do_external_connect.min.map \
|
||||
$(BUILD_DIR)/external_api.min.js \
|
||||
$(BUILD_DIR)/external_api.min.map \
|
||||
$(OUTPUT_DIR)/analytics.js \
|
||||
|
||||
@@ -19,15 +19,14 @@ You can download Debian/Ubuntu binaries:
|
||||
* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianTestingRepository))
|
||||
* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/Main/InstallJitsiMeetDebianNightlyRepository))
|
||||
|
||||
You can download source archives (produced by ```make source-package```):
|
||||
* [source builds](https://download.jitsi.org/jitsi-meet/src/)
|
||||
|
||||
You can get our mobile versions from here:
|
||||
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||
|
||||
## Building the sources
|
||||
|
||||
Jitsi Meet uses [Browserify](http://browserify.org). If you want to make changes in the code you need to [install Browserify](http://browserify.org/#install). Browserify requires [nodejs](http://nodejs.org).
|
||||
|
||||
On Debian/Ubuntu systems, the required packages can be installed with:
|
||||
```
|
||||
sudo apt-get install npm nodejs-legacy
|
||||
@@ -100,6 +99,9 @@ Jitsi Meet provides a very flexible way of embedding it in external applications
|
||||
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.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Jitsi Meet started out as a sample conferencing application using Jitsi Videobridge. It was originally developed by then ESTOS' developer Philipp Hancke who then contributed it to the community where development continues with joint forces!
|
||||
|
||||
@@ -91,7 +91,7 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode Integer.parseInt("${version}")
|
||||
versionName "1.4.${version}"
|
||||
versionName "1.3.${version}"
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'x86'
|
||||
}
|
||||
|
||||
@@ -11,13 +11,12 @@
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<application
|
||||
|
||||
@@ -32,8 +32,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new org.jitsi.meet.audiomode.AudioModePackage(),
|
||||
new org.jitsi.meet.proximity.ProximityPackage()
|
||||
new org.jitsi.meet.audiomode.AudioModePackage()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -188,24 +188,13 @@ public class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean success;
|
||||
|
||||
try {
|
||||
success = updateAudioRoute(mode);
|
||||
} catch (Throwable e) {
|
||||
success = false;
|
||||
Log.e(
|
||||
TAG,
|
||||
"Failed to update audio route for mode: " + mode,
|
||||
e);
|
||||
}
|
||||
if (success) {
|
||||
if (updateAudioRoute(mode)) {
|
||||
AudioModeModule.this.mode = mode;
|
||||
promise.resolve(null);
|
||||
} else {
|
||||
promise.reject(
|
||||
"setMode",
|
||||
"Failed to set audio mode to " + mode);
|
||||
"Failed to set the requested audio mode");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
package org.jitsi.meet.proximity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
/**
|
||||
* Module implementing a simple API to enable a proximity sensor-controlled
|
||||
* wake lock. When the lock is held, if the proximity sensor detects a nearby
|
||||
* object it will dim the screen and disable touch controls. The functionality
|
||||
* is used with the conference audio-only mode.
|
||||
*/
|
||||
public class ProximityModule extends ReactContextBaseJavaModule {
|
||||
/**
|
||||
* React Native module name.
|
||||
*/
|
||||
private static final String MODULE_NAME = "Proximity";
|
||||
|
||||
/**
|
||||
* This type of wake lock (the one activated by the proximity sensor) has
|
||||
* been available for a while, but the constant was only exported in API
|
||||
* level 21 (Android Marshmallow) so make no assumptions and use its value
|
||||
* directly.
|
||||
*
|
||||
* TODO: Remove when we bump the API level to 21.
|
||||
*/
|
||||
private static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
|
||||
|
||||
/**
|
||||
* {@link WakeLock} instance.
|
||||
*/
|
||||
private final WakeLock wakeLock;
|
||||
|
||||
/**
|
||||
* Initializes a new module instance. There shall be a single instance of
|
||||
* this module throughout the lifetime of the application.
|
||||
*
|
||||
* @param reactContext The {@link ReactApplicationContext} where this module
|
||||
* is created.
|
||||
*/
|
||||
public ProximityModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
WakeLock wakeLock;
|
||||
PowerManager powerManager
|
||||
= (PowerManager)
|
||||
reactContext.getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
try {
|
||||
wakeLock
|
||||
= powerManager.newWakeLock(
|
||||
PROXIMITY_SCREEN_OFF_WAKE_LOCK,
|
||||
MODULE_NAME);
|
||||
} catch (Throwable ignored) {
|
||||
wakeLock = null;
|
||||
}
|
||||
|
||||
this.wakeLock = wakeLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this module to be used in the React Native bridge.
|
||||
*
|
||||
* @return The name of this module to be used in the React Native bridge.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires / releases the proximity sensor wake lock.
|
||||
*
|
||||
* @param enabled {@code true} to enable the proximity sensor; otherwise,
|
||||
* {@code false}.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setEnabled(final boolean enabled) {
|
||||
if (wakeLock == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (enabled) {
|
||||
if (!wakeLock.isHeld()) {
|
||||
wakeLock.acquire();
|
||||
}
|
||||
} else if (wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.jitsi.meet.proximity;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implements {@link ReactPackage} for {@link ProximityModule}.
|
||||
*/
|
||||
public class ProximityPackage implements ReactPackage {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return List of native modules to be exposed by React Native.
|
||||
*/
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new ProximityModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(
|
||||
ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
rootProject.name = 'jitsi-meet'
|
||||
rootProject.name = 'jitsi-meet-react'
|
||||
|
||||
include ':app'
|
||||
include ':react-native-background-timer'
|
||||
|
||||
3
app.js
@@ -1,5 +1,6 @@
|
||||
/* application specific logic */
|
||||
|
||||
import "babel-polyfill";
|
||||
import "jquery";
|
||||
import "jquery-contextmenu";
|
||||
import "jquery-ui";
|
||||
@@ -18,7 +19,7 @@ window.toastr = require("toastr");
|
||||
import UI from "./modules/UI/UI";
|
||||
import settings from "./modules/settings/Settings";
|
||||
import conference from './conference';
|
||||
import API from './modules/API';
|
||||
import API from './modules/API/API';
|
||||
|
||||
import translation from "./modules/translation/translation";
|
||||
import remoteControl from "./modules/remotecontrol/RemoteControl";
|
||||
|
||||
426
conference.js
@@ -2,6 +2,7 @@
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import {openConnection} from './connection';
|
||||
import Invite from './modules/UI/invite/Invite';
|
||||
import ContactList from './modules/UI/side_pannels/contactlist/ContactList';
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
@@ -19,28 +20,24 @@ import analytics from './modules/analytics/analytics';
|
||||
|
||||
import EventEmitter from "events";
|
||||
|
||||
import { getLocationContextRoot } from './react/features/app';
|
||||
import {
|
||||
AVATAR_ID_COMMAND,
|
||||
AVATAR_URL_COMMAND,
|
||||
conferenceFailed,
|
||||
conferenceJoined,
|
||||
conferenceLeft,
|
||||
EMAIL_COMMAND,
|
||||
lockStateChanged
|
||||
EMAIL_COMMAND
|
||||
} from './react/features/base/conference';
|
||||
import {
|
||||
updateDeviceList
|
||||
} from './react/features/base/devices';
|
||||
import {
|
||||
isFatalJitsiConnectionError
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import {
|
||||
localParticipantRoleChanged,
|
||||
changeParticipantAvatarID,
|
||||
changeParticipantAvatarURL,
|
||||
changeParticipantEmail,
|
||||
participantJoined,
|
||||
participantLeft,
|
||||
participantRoleChanged,
|
||||
participantUpdated
|
||||
participantRoleChanged
|
||||
} from './react/features/base/participants';
|
||||
import {
|
||||
showDesktopPicker
|
||||
@@ -49,7 +46,6 @@ import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
import { showDesktopSharingButton } from './react/features/toolbox';
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
@@ -64,10 +60,7 @@ const ConnectionQualityEvents = JitsiMeetJS.events.connectionQuality;
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
let room;
|
||||
let connection;
|
||||
let localAudio, localVideo;
|
||||
let initialAudioMutedState = false, initialVideoMutedState = false;
|
||||
let room, connection, localAudio, localVideo;
|
||||
|
||||
/**
|
||||
* Indicates whether extension external installation is in progress or not.
|
||||
@@ -180,11 +173,7 @@ function createInitialLocalTracksAndConnect(roomName) {
|
||||
* @param command the command
|
||||
* @param {string} value new value
|
||||
*/
|
||||
function sendData(command, value) {
|
||||
if(!room) {
|
||||
return;
|
||||
}
|
||||
|
||||
function sendData (command, value) {
|
||||
room.removeCommand(command);
|
||||
room.sendCommand(command, {value: value});
|
||||
}
|
||||
@@ -217,7 +206,7 @@ function _setupLocalParticipantProperties() {
|
||||
* @param {string} id user id
|
||||
* @returns {string?} user nickname or undefined if user is unknown.
|
||||
*/
|
||||
function getDisplayName(id) {
|
||||
function getDisplayName (id) {
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
return APP.settings.getDisplayName();
|
||||
}
|
||||
@@ -234,7 +223,7 @@ function getDisplayName(id) {
|
||||
* @param {boolean} userInteraction - indicates if this local audio mute was a
|
||||
* result of user interaction
|
||||
*/
|
||||
function muteLocalAudio(muted) {
|
||||
function muteLocalAudio (muted) {
|
||||
muteLocalMedia(localAudio, muted, 'Audio');
|
||||
}
|
||||
|
||||
@@ -254,7 +243,7 @@ function muteLocalMedia(localMedia, muted, localMediaTypeString) {
|
||||
* Mute or unmute local video stream if it exists.
|
||||
* @param {boolean} muted if video stream should be muted or unmuted.
|
||||
*/
|
||||
function muteLocalVideo(muted) {
|
||||
function muteLocalVideo (muted) {
|
||||
muteLocalMedia(localVideo, muted, 'Video');
|
||||
}
|
||||
|
||||
@@ -273,11 +262,9 @@ function muteLocalVideo(muted) {
|
||||
function maybeRedirectToWelcomePage(options) {
|
||||
// if close page is enabled redirect to it, without further action
|
||||
if (config.enableClosePage) {
|
||||
const { isGuest } = APP.store.getState()['features/jwt'];
|
||||
|
||||
// save whether current user is guest or not, before navigating
|
||||
// to close page
|
||||
window.sessionStorage.setItem('guest', isGuest);
|
||||
window.sessionStorage.setItem('guest', APP.tokenData.isGuest);
|
||||
assignWindowLocationPathname('static/'
|
||||
+ (options.feedbackSubmitted ? "close.html" : "close2.html"));
|
||||
return;
|
||||
@@ -311,11 +298,18 @@ 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 = getLocationContextRoot(windowLocation) + pathname;
|
||||
pathname = contextRoot + pathname;
|
||||
}
|
||||
|
||||
windowLocation.pathname = pathname;
|
||||
@@ -334,7 +328,7 @@ function assignWindowLocationPathname(pathname) {
|
||||
* for gUM permission prompt
|
||||
* @returns {Promise<JitsiLocalTrack[]>}
|
||||
*/
|
||||
function createLocalTracks(options, checkForPermissionPrompt) {
|
||||
function createLocalTracks (options, checkForPermissionPrompt) {
|
||||
options || (options = {});
|
||||
|
||||
return JitsiMeetJS
|
||||
@@ -367,10 +361,28 @@ function createLocalTracks(options, checkForPermissionPrompt) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the display name for the local user
|
||||
* @param nickname {string} the new display name
|
||||
*/
|
||||
function changeLocalDisplayName(nickname = '') {
|
||||
const formattedNickname
|
||||
= nickname.trim().substr(0, MAX_DISPLAY_NAME_LENGTH);
|
||||
|
||||
if (formattedNickname === APP.settings.getDisplayName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.settings.setDisplayName(formattedNickname);
|
||||
room.setDisplayName(formattedNickname);
|
||||
APP.UI.changeDisplayName(APP.conference.getMyUserId(), formattedNickname);
|
||||
}
|
||||
|
||||
class ConferenceConnector {
|
||||
constructor(resolve, reject) {
|
||||
constructor(resolve, reject, invite) {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
this._invite = invite;
|
||||
this.reconnectTimeout = null;
|
||||
room.on(ConferenceEvents.CONFERENCE_JOINED,
|
||||
this._handleConferenceJoined.bind(this));
|
||||
@@ -386,8 +398,12 @@ class ConferenceConnector {
|
||||
_onConferenceFailed(err, ...params) {
|
||||
APP.store.dispatch(conferenceFailed(room, err, ...params));
|
||||
logger.error('CONFERENCE FAILED:', err, ...params);
|
||||
APP.UI.hideRingOverlay();
|
||||
APP.UI.hideRingOverLay();
|
||||
switch (err) {
|
||||
// room is locked by the password
|
||||
case ConferenceErrors.PASSWORD_REQUIRED:
|
||||
APP.UI.emitEvent(UIEvents.PASSWORD_REQUIRED);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.CONNECTION_ERROR:
|
||||
{
|
||||
@@ -404,15 +420,15 @@ class ConferenceConnector {
|
||||
break;
|
||||
|
||||
// not enough rights to create conference
|
||||
case ConferenceErrors.AUTHENTICATION_REQUIRED: {
|
||||
// Schedule reconnect to check if someone else created the room.
|
||||
this.reconnectTimeout = setTimeout(() => room.join(), 5000);
|
||||
case ConferenceErrors.AUTHENTICATION_REQUIRED:
|
||||
// schedule reconnect to check if someone else created the room
|
||||
this.reconnectTimeout = setTimeout(function () {
|
||||
room.join();
|
||||
}, 5000);
|
||||
|
||||
const { password }
|
||||
= APP.store.getState()['features/base/conference'];
|
||||
|
||||
AuthHandler.requireAuth(room, password);
|
||||
}
|
||||
// notify user that auth is required
|
||||
AuthHandler.requireAuth(
|
||||
room, this._invite.getRoomLocker().password);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.RESERVATION_ERROR:
|
||||
@@ -579,12 +595,6 @@ export default {
|
||||
analytics.init();
|
||||
return createInitialLocalTracksAndConnect(options.roomName);
|
||||
}).then(([tracks, con]) => {
|
||||
tracks.forEach(track => {
|
||||
if((track.isAudioTrack() && initialAudioMutedState)
|
||||
|| (track.isVideoTrack() && initialVideoMutedState)) {
|
||||
track.mute();
|
||||
}
|
||||
});
|
||||
logger.log('initialized with %s local tracks', tracks.length);
|
||||
con.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
@@ -592,14 +602,8 @@ export default {
|
||||
APP.connection = connection = con;
|
||||
this.isDesktopSharingEnabled =
|
||||
JitsiMeetJS.isDesktopSharingEnabled();
|
||||
eventEmitter.emit(
|
||||
JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
|
||||
this.isDesktopSharingEnabled);
|
||||
|
||||
APP.store.dispatch(showDesktopSharingButton());
|
||||
|
||||
this._createRoom(tracks);
|
||||
APP.remoteControl.init();
|
||||
this._createRoom(tracks);
|
||||
|
||||
if (UIUtil.isButtonEnabled('contacts')
|
||||
&& !interfaceConfig.filmStripOnly) {
|
||||
@@ -624,7 +628,8 @@ export default {
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
(new ConferenceConnector(resolve, reject)).connect();
|
||||
(new ConferenceConnector(
|
||||
resolve, reject, this.invite)).connect();
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -633,14 +638,14 @@ export default {
|
||||
* @param {string} id id to check
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLocalId(id) {
|
||||
isLocalId (id) {
|
||||
return this.getMyUserId() === id;
|
||||
},
|
||||
/**
|
||||
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
|
||||
* @param mute true for mute and false for unmute.
|
||||
*/
|
||||
muteAudio(mute) {
|
||||
muteAudio (mute) {
|
||||
muteLocalAudio(mute);
|
||||
},
|
||||
/**
|
||||
@@ -651,51 +656,36 @@ export default {
|
||||
return this.audioMuted;
|
||||
},
|
||||
/**
|
||||
* Simulates toolbar button click for audio mute. Used by shortcuts
|
||||
* and API.
|
||||
* @param {boolean} force - If the track is not created, the operation
|
||||
* will be executed after the track is created. Otherwise the operation
|
||||
* will be ignored.
|
||||
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
|
||||
*/
|
||||
toggleAudioMuted(force = false) {
|
||||
if(!localAudio && force) {
|
||||
initialAudioMutedState = !initialAudioMutedState;
|
||||
return;
|
||||
}
|
||||
toggleAudioMuted () {
|
||||
this.muteAudio(!this.audioMuted);
|
||||
},
|
||||
/**
|
||||
* Simulates toolbar button click for video mute. Used by shortcuts and API.
|
||||
* @param mute true for mute and false for unmute.
|
||||
*/
|
||||
muteVideo(mute) {
|
||||
muteVideo (mute) {
|
||||
muteLocalVideo(mute);
|
||||
},
|
||||
/**
|
||||
* Simulates toolbar button click for video mute. Used by shortcuts and API.
|
||||
* @param {boolean} force - If the track is not created, the operation
|
||||
* will be executed after the track is created. Otherwise the operation
|
||||
* will be ignored.
|
||||
*/
|
||||
toggleVideoMuted(force = false) {
|
||||
if(!localVideo && force) {
|
||||
initialVideoMutedState = !initialVideoMutedState;
|
||||
return;
|
||||
}
|
||||
toggleVideoMuted () {
|
||||
this.muteVideo(!this.videoMuted);
|
||||
},
|
||||
/**
|
||||
* Retrieve list of conference participants (without local user).
|
||||
* @returns {JitsiParticipant[]}
|
||||
*/
|
||||
listMembers() {
|
||||
listMembers () {
|
||||
return room.getParticipants();
|
||||
},
|
||||
/**
|
||||
* Retrieve list of ids of conference participants (without local user).
|
||||
* @returns {string[]}
|
||||
*/
|
||||
listMembersIds() {
|
||||
listMembersIds () {
|
||||
return room.getParticipants().map(p => p.getId());
|
||||
},
|
||||
/**
|
||||
@@ -703,7 +693,7 @@ export default {
|
||||
* @id id to search for participant
|
||||
* @return {boolean} whether the participant is moderator
|
||||
*/
|
||||
isParticipantModerator(id) {
|
||||
isParticipantModerator (id) {
|
||||
let user = room.getParticipantById(id);
|
||||
return user && user.isModerator();
|
||||
},
|
||||
@@ -711,10 +701,10 @@ export default {
|
||||
* Check if SIP is supported.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
sipGatewayEnabled() {
|
||||
sipGatewayEnabled () {
|
||||
return room.isSIPCallingSupported();
|
||||
},
|
||||
get membersCount() {
|
||||
get membersCount () {
|
||||
return room.getParticipants().length + 1;
|
||||
},
|
||||
/**
|
||||
@@ -724,7 +714,7 @@ export default {
|
||||
* @returns true if the callstats integration is enabled, otherwise returns
|
||||
* false.
|
||||
*/
|
||||
isCallstatsEnabled() {
|
||||
isCallstatsEnabled () {
|
||||
return room && room.isCallstatsEnabled();
|
||||
},
|
||||
/**
|
||||
@@ -734,7 +724,7 @@ export default {
|
||||
* user feedback
|
||||
* @param detailedFeedback detailed feedback from the user. Not yet used
|
||||
*/
|
||||
sendFeedback(overallFeedback, detailedFeedback) {
|
||||
sendFeedback (overallFeedback, detailedFeedback) {
|
||||
return room.sendFeedback (overallFeedback, detailedFeedback);
|
||||
},
|
||||
|
||||
@@ -752,15 +742,15 @@ export default {
|
||||
/**
|
||||
* Returns the connection times stored in the library.
|
||||
*/
|
||||
getConnectionTimes() {
|
||||
getConnectionTimes () {
|
||||
return this._room.getConnectionTimes();
|
||||
},
|
||||
// used by torture currently
|
||||
isJoined() {
|
||||
isJoined () {
|
||||
return this._room
|
||||
&& this._room.isJoined();
|
||||
},
|
||||
getConnectionState() {
|
||||
getConnectionState () {
|
||||
return this._room
|
||||
&& this._room.getConnectionState();
|
||||
},
|
||||
@@ -769,7 +759,7 @@ export default {
|
||||
* @return {string|null} ICE connection state or <tt>null</tt> if there's no
|
||||
* P2P connection
|
||||
*/
|
||||
getP2PConnectionState() {
|
||||
getP2PConnectionState () {
|
||||
return this._room
|
||||
&& this._room.getP2PConnectionState();
|
||||
},
|
||||
@@ -777,7 +767,7 @@ export default {
|
||||
* Starts P2P (for tests only)
|
||||
* @private
|
||||
*/
|
||||
_startP2P() {
|
||||
_startP2P () {
|
||||
try {
|
||||
this._room && this._room.startP2PSession();
|
||||
} catch (error) {
|
||||
@@ -789,7 +779,7 @@ export default {
|
||||
* Stops P2P (for tests only)
|
||||
* @private
|
||||
*/
|
||||
_stopP2P() {
|
||||
_stopP2P () {
|
||||
try {
|
||||
this._room && this._room.stopP2PSession();
|
||||
} catch (error) {
|
||||
@@ -804,7 +794,7 @@ export default {
|
||||
* @returns {boolean} true if the connection is in interrupted state or
|
||||
* false otherwise.
|
||||
*/
|
||||
isConnectionInterrupted() {
|
||||
isConnectionInterrupted () {
|
||||
return this._room.isConnectionInterrupted();
|
||||
},
|
||||
/**
|
||||
@@ -815,20 +805,20 @@ export default {
|
||||
* @returns {JitsiParticipant|null} participant instance for given id or
|
||||
* null if not found.
|
||||
*/
|
||||
getParticipantById(id) {
|
||||
getParticipantById (id) {
|
||||
return room ? room.getParticipantById(id) : null;
|
||||
},
|
||||
/**
|
||||
* Get participant connection status for the participant.
|
||||
* Checks whether the user identified by given id is currently connected.
|
||||
*
|
||||
* @param {string} id participant's identifier(MUC nickname)
|
||||
*
|
||||
* @returns {ParticipantConnectionStatus|null} the status of the participant
|
||||
* or null if no such participant is found or participant is the local user.
|
||||
* @returns {boolean|null} true if participant's connection is ok or false
|
||||
* if the user is having connectivity issues.
|
||||
*/
|
||||
getParticipantConnectionStatus(id) {
|
||||
isParticipantConnectionActive (id) {
|
||||
let participant = this.getParticipantById(id);
|
||||
return participant ? participant.getConnectionStatus() : null;
|
||||
return participant ? participant.isConnectionActive() : null;
|
||||
},
|
||||
/**
|
||||
* Gets the display name foe the <tt>JitsiParticipant</tt> identified by
|
||||
@@ -839,7 +829,7 @@ export default {
|
||||
* @return {string} the participant's display name or the default string if
|
||||
* absent.
|
||||
*/
|
||||
getParticipantDisplayName(id) {
|
||||
getParticipantDisplayName (id) {
|
||||
let displayName = getDisplayName(id);
|
||||
if (displayName) {
|
||||
return displayName;
|
||||
@@ -852,7 +842,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
getMyUserId() {
|
||||
getMyUserId () {
|
||||
return this._room
|
||||
&& this._room.myUserId();
|
||||
},
|
||||
@@ -878,7 +868,7 @@ export default {
|
||||
* @param id the id for the user audio level to return (the id value is
|
||||
* returned for the participant using getMyUserId() method)
|
||||
*/
|
||||
getPeerSSRCAudioLevel(id) {
|
||||
getPeerSSRCAudioLevel (id) {
|
||||
return this.audioLevelsMap[id];
|
||||
},
|
||||
/**
|
||||
@@ -898,7 +888,7 @@ export default {
|
||||
},
|
||||
// end used by torture
|
||||
|
||||
getLogs() {
|
||||
getLogs () {
|
||||
return room.getLogs();
|
||||
},
|
||||
|
||||
@@ -907,7 +897,7 @@ export default {
|
||||
* debugging.
|
||||
* @param filename (optional) specify target filename
|
||||
*/
|
||||
saveLogs(filename = 'meetlog.json') {
|
||||
saveLogs (filename = 'meetlog.json') {
|
||||
// this can be called from console and will not have reference to this
|
||||
// that's why we reference the global var
|
||||
let logs = APP.conference.getLogs();
|
||||
@@ -978,6 +968,7 @@ export default {
|
||||
room = connection.initJitsiConference(APP.conference.roomName,
|
||||
this._getConferenceOptions());
|
||||
this._setLocalAudioVideoStreams(localTracks);
|
||||
this.invite = new Invite(room);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
_setupLocalParticipantProperties();
|
||||
@@ -1016,13 +1007,13 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Start using provided video stream.
|
||||
* Stops previous video stream.
|
||||
* @param {JitsiLocalTrack} [stream] new stream to use or null
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream(newStream) {
|
||||
/**
|
||||
* Start using provided video stream.
|
||||
* Stops previous video stream.
|
||||
* @param {JitsiLocalTrack} [stream] new stream to use or null
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream (newStream) {
|
||||
return room.replaceTrack(localVideo, newStream)
|
||||
.then(() => {
|
||||
// We call dispose after doing the replace because
|
||||
@@ -1057,7 +1048,7 @@ export default {
|
||||
* @param {JitsiLocalTrack} [stream] new stream to use or null
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useAudioStream(newStream) {
|
||||
useAudioStream (newStream) {
|
||||
return room.replaceTrack(localAudio, newStream)
|
||||
.then(() => {
|
||||
// We call dispose after doing the replace because
|
||||
@@ -1080,46 +1071,8 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers a tooltip to display when a feature was attempted to be used
|
||||
* while in audio only mode.
|
||||
*
|
||||
* @param {string} featureName - The name of the feature that attempted to
|
||||
* toggle.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_displayAudioOnlyTooltip(featureName) {
|
||||
let tooltipElementId = null;
|
||||
|
||||
switch (featureName) {
|
||||
case 'screenShare':
|
||||
tooltipElementId = '#screenshareWhileAudioOnly';
|
||||
break;
|
||||
case 'videoMute':
|
||||
tooltipElementId = '#unmuteWhileAudioOnly';
|
||||
break;
|
||||
}
|
||||
|
||||
if (tooltipElementId) {
|
||||
APP.UI.showToolbar(6000);
|
||||
APP.UI.showCustomToolbarPopup(
|
||||
tooltipElementId, true, 5000);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether or not the conference is currently in audio only mode.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isAudioOnly() {
|
||||
return Boolean(
|
||||
APP.store.getState()['features/base/conference'].audioOnly);
|
||||
},
|
||||
|
||||
videoSwitchInProgress: false,
|
||||
toggleScreenSharing(shareScreen = !this.isSharingScreen) {
|
||||
toggleScreenSharing (shareScreen = !this.isSharingScreen) {
|
||||
if (this.videoSwitchInProgress) {
|
||||
logger.warn("Switch in progress.");
|
||||
return;
|
||||
@@ -1129,11 +1082,6 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isAudioOnly()) {
|
||||
this._displayAudioOnlyTooltip('screenShare');
|
||||
return;
|
||||
}
|
||||
|
||||
this.videoSwitchInProgress = true;
|
||||
let externalInstallation = false;
|
||||
|
||||
@@ -1247,7 +1195,7 @@ export default {
|
||||
/**
|
||||
* Setup interaction between conference and UI.
|
||||
*/
|
||||
_setupListeners() {
|
||||
_setupListeners () {
|
||||
// add local streams when joined to the conference
|
||||
room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
||||
APP.store.dispatch(conferenceJoined(room));
|
||||
@@ -1286,8 +1234,6 @@ export default {
|
||||
|
||||
// check the roles for the new user and reflect them
|
||||
APP.UI.updateUserRole(user);
|
||||
|
||||
updateRemoteThumbnailsVisibility();
|
||||
});
|
||||
room.on(ConferenceEvents.USER_LEFT, (id, user) => {
|
||||
APP.store.dispatch(participantLeft(id, user));
|
||||
@@ -1295,30 +1241,18 @@ export default {
|
||||
APP.API.notifyUserLeft(id);
|
||||
APP.UI.removeUser(id, user.getDisplayName());
|
||||
APP.UI.onSharedVideoStop(id);
|
||||
|
||||
updateRemoteThumbnailsVisibility();
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.USER_STATUS_CHANGED, (id, status) => {
|
||||
let user = room.getParticipantById(id);
|
||||
if (user) {
|
||||
APP.UI.updateUserStatus(user, status);
|
||||
}
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
|
||||
APP.store.dispatch(participantRoleChanged(id, role));
|
||||
if (this.isLocalId(id)) {
|
||||
logger.info(`My role changed, new role: ${role}`);
|
||||
|
||||
APP.store.dispatch(localParticipantRoleChanged(role));
|
||||
|
||||
if (this.isModerator !== room.isModerator()) {
|
||||
this.isModerator = room.isModerator();
|
||||
APP.UI.updateLocalRole(room.isModerator());
|
||||
}
|
||||
} else {
|
||||
APP.store.dispatch(participantRoleChanged(id, role));
|
||||
|
||||
let user = room.getParticipantById(id);
|
||||
if (user) {
|
||||
APP.UI.updateUserRole(user);
|
||||
@@ -1391,8 +1325,8 @@ export default {
|
||||
|
||||
room.on(
|
||||
ConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
|
||||
id => {
|
||||
APP.UI.participantConnectionStatusChanged(id);
|
||||
(id, isActive) => {
|
||||
APP.UI.participantConnectionStatusChanged(id, isActive);
|
||||
});
|
||||
room.on(ConferenceEvents.DOMINANT_SPEAKER_CHANGED, (id) => {
|
||||
if (this.isLocalId(id)) {
|
||||
@@ -1415,15 +1349,10 @@ export default {
|
||||
room.on(ConferenceEvents.CONNECTION_RESTORED, () => {
|
||||
APP.UI.markVideoInterrupted(false);
|
||||
});
|
||||
room.on(ConferenceEvents.MESSAGE_RECEIVED, (id, body, ts) => {
|
||||
room.on(ConferenceEvents.MESSAGE_RECEIVED, (id, text, ts) => {
|
||||
let nick = getDisplayName(id);
|
||||
APP.API.notifyReceivedChatMessage({
|
||||
id,
|
||||
nick,
|
||||
body,
|
||||
ts
|
||||
});
|
||||
APP.UI.addMessage(id, nick, body, ts);
|
||||
APP.API.notifyReceivedChatMessage(id, nick, text, ts);
|
||||
APP.UI.addMessage(id, nick, text, ts);
|
||||
});
|
||||
APP.UI.addListener(UIEvents.MESSAGE_CREATED, (message) => {
|
||||
APP.API.notifySendingChatMessage(message);
|
||||
@@ -1447,10 +1376,6 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.VIDEO_UNMUTING_WHILE_AUDIO_ONLY,
|
||||
() => this._displayAudioOnlyTooltip('videoMute'));
|
||||
|
||||
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
|
||||
(smallVideo, isPinned) => {
|
||||
let smallVideoId = smallVideo.getId();
|
||||
@@ -1479,8 +1404,6 @@ export default {
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
updateRemoteThumbnailsVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1495,18 +1418,10 @@ export default {
|
||||
room.on(ConferenceEvents.DISPLAY_NAME_CHANGED, (id, displayName) => {
|
||||
const formattedDisplayName
|
||||
= displayName.substr(0, MAX_DISPLAY_NAME_LENGTH);
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id,
|
||||
name: formattedDisplayName
|
||||
}));
|
||||
APP.API.notifyDisplayNameChanged(id, formattedDisplayName);
|
||||
APP.UI.changeDisplayName(id, formattedDisplayName);
|
||||
});
|
||||
|
||||
room.on(
|
||||
ConferenceEvents.LOCK_STATE_CHANGED,
|
||||
(...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
|
||||
|
||||
room.on(ConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
|
||||
(participant, name, oldValue, newValue) => {
|
||||
if (name === "raisedHand") {
|
||||
@@ -1565,14 +1480,7 @@ export default {
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
|
||||
APP.UI.addListener(UIEvents.VIDEO_MUTED, muted => {
|
||||
if (this.isAudioOnly() && !muted) {
|
||||
this._displayAudioOnlyTooltip('videoMute');
|
||||
return;
|
||||
}
|
||||
|
||||
muteLocalVideo(muted);
|
||||
});
|
||||
APP.UI.addListener(UIEvents.VIDEO_MUTED, muteLocalVideo);
|
||||
|
||||
room.on(ConnectionQualityEvents.LOCAL_STATS_UPDATED,
|
||||
(stats) => {
|
||||
@@ -1591,10 +1499,7 @@ export default {
|
||||
|
||||
APP.UI.addListener(UIEvents.EMAIL_CHANGED, this.changeLocalEmail);
|
||||
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: from,
|
||||
email: data.value
|
||||
}));
|
||||
APP.store.dispatch(changeParticipantEmail(from, data.value));
|
||||
APP.UI.setUserEmail(from, data.value);
|
||||
});
|
||||
|
||||
@@ -1602,25 +1507,18 @@ export default {
|
||||
this.commands.defaults.AVATAR_URL,
|
||||
(data, from) => {
|
||||
APP.store.dispatch(
|
||||
participantUpdated({
|
||||
id: from,
|
||||
avatarURL: data.value
|
||||
}));
|
||||
changeParticipantAvatarURL(from, data.value));
|
||||
APP.UI.setUserAvatarUrl(from, data.value);
|
||||
});
|
||||
|
||||
room.addCommandListener(this.commands.defaults.AVATAR_ID,
|
||||
(data, from) => {
|
||||
APP.store.dispatch(
|
||||
participantUpdated({
|
||||
id: from,
|
||||
avatarID: data.value
|
||||
}));
|
||||
changeParticipantAvatarID(from, data.value));
|
||||
APP.UI.setUserAvatarID(from, data.value);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
|
||||
this.changeLocalDisplayName.bind(this));
|
||||
APP.UI.addListener(UIEvents.NICKNAME_CHANGED, changeLocalDisplayName);
|
||||
|
||||
APP.UI.addListener(UIEvents.START_MUTED_CHANGED,
|
||||
(startAudioMuted, startVideoMuted) => {
|
||||
@@ -1663,6 +1561,10 @@ export default {
|
||||
});
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.SIP_DIAL, (sipNumber) => {
|
||||
room.dial(sipNumber);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.RESOLUTION_CHANGED,
|
||||
(id, oldResolution, newResolution, delay) => {
|
||||
var logObject = {
|
||||
@@ -1717,20 +1619,13 @@ export default {
|
||||
micDeviceId: null
|
||||
})
|
||||
.then(([stream]) => {
|
||||
if (this.isAudioOnly()) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(stream => {
|
||||
this.useVideoStream(stream);
|
||||
logger.log('switched local video device');
|
||||
APP.settings.setCameraDeviceId(cameraDeviceId, true);
|
||||
})
|
||||
.catch((err) => {
|
||||
APP.UI.showDeviceErrorDialog(null, err);
|
||||
APP.UI.setSelectedCameraFromSettings();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1752,6 +1647,7 @@ export default {
|
||||
})
|
||||
.catch((err) => {
|
||||
APP.UI.showDeviceErrorDialog(err, null);
|
||||
APP.UI.setSelectedMicFromSettings();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1767,22 +1663,11 @@ export default {
|
||||
logger.warn('Failed to change audio output device. ' +
|
||||
'Default or previously set audio output device ' +
|
||||
'will be used instead.', err);
|
||||
APP.UI.setSelectedAudioOutputFromSettings();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly => {
|
||||
muteLocalVideo(audioOnly);
|
||||
|
||||
// Immediately update the UI by having remote videos and the large
|
||||
// video update themselves instead of waiting for some other event
|
||||
// to cause the update, usually PARTICIPANT_CONN_STATUS_CHANGED.
|
||||
// There is no guarantee another event will trigger the update
|
||||
// immediately and in all situations, for example because a remote
|
||||
// participant is having connection trouble so no status changes.
|
||||
APP.UI.updateAllVideos();
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
||||
);
|
||||
@@ -1817,8 +1702,6 @@ export default {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateRemoteThumbnailsVisibility();
|
||||
});
|
||||
room.addCommandListener(
|
||||
this.commands.defaults.SHARED_VIDEO, ({value, attributes}, id) => {
|
||||
@@ -1834,23 +1717,6 @@ export default {
|
||||
APP.UI.onSharedVideoUpdate(id, value, attributes);
|
||||
}
|
||||
});
|
||||
|
||||
function updateRemoteThumbnailsVisibility() {
|
||||
const localUserId = APP.conference.getMyUserId();
|
||||
const remoteParticipantsCount = room.getParticipantCount() - 1;
|
||||
|
||||
// Get the remote thumbnail count for cases where there are
|
||||
// non-participants displaying video, such as with video sharing.
|
||||
const remoteVideosCount = APP.UI.getRemoteVideosCount();
|
||||
|
||||
const shouldShowRemoteThumbnails = interfaceConfig.filmStripOnly
|
||||
|| (APP.UI.isPinned(localUserId) && remoteVideosCount)
|
||||
|| remoteVideosCount > 1
|
||||
|| remoteParticipantsCount !== remoteVideosCount;
|
||||
|
||||
APP.UI.setRemoteThumbnailsVisibility(
|
||||
Boolean(shouldShowRemoteThumbnails));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Adds any room listener.
|
||||
@@ -1893,8 +1759,8 @@ export default {
|
||||
}
|
||||
|
||||
mediaDeviceHelper.setCurrentMediaDevices(devices);
|
||||
|
||||
APP.UI.onAvailableDevicesChanged(devices);
|
||||
APP.store.dispatch(updateDeviceList(devices));
|
||||
});
|
||||
|
||||
this.deviceChangeListener = (devices) =>
|
||||
@@ -2027,9 +1893,9 @@ export default {
|
||||
* @param {boolean} [requestFeedback=false] if user feedback should be
|
||||
* requested
|
||||
*/
|
||||
hangup(requestFeedback = false) {
|
||||
hangup (requestFeedback = false) {
|
||||
eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
|
||||
APP.UI.hideRingOverlay();
|
||||
APP.UI.hideRingOverLay();
|
||||
let requestFeedbackPromise = requestFeedback
|
||||
? APP.UI.requestFeedbackOnHangup()
|
||||
// false - because the thank you dialog shouldn't be displayed
|
||||
@@ -2059,17 +1925,10 @@ export default {
|
||||
if (email === APP.settings.getEmail()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localId = room ? room.myUserId() : undefined;
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: localId,
|
||||
local: true,
|
||||
email
|
||||
}));
|
||||
APP.store.dispatch(changeParticipantEmail(room.myUserId(), email));
|
||||
|
||||
APP.settings.setEmail(email);
|
||||
APP.UI.setUserEmail(localId, email);
|
||||
APP.UI.setUserEmail(room.myUserId(), email);
|
||||
sendData(commands.EMAIL, email);
|
||||
},
|
||||
|
||||
@@ -2083,17 +1942,10 @@ export default {
|
||||
if (url === APP.settings.getAvatarUrl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localId = room ? room.myUserId() : undefined;
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: localId,
|
||||
local: true,
|
||||
avatarURL: url
|
||||
}));
|
||||
APP.store.dispatch(changeParticipantAvatarURL(room.myUserId(), url));
|
||||
|
||||
APP.settings.setAvatarUrl(url);
|
||||
APP.UI.setUserAvatarUrl(localId, url);
|
||||
APP.UI.setUserAvatarUrl(room.myUserId(), url);
|
||||
sendData(commands.AVATAR_URL, url);
|
||||
},
|
||||
|
||||
@@ -2105,7 +1957,7 @@ export default {
|
||||
* @throws NetworkError or InvalidStateError or Error if the operation
|
||||
* fails.
|
||||
*/
|
||||
sendEndpointMessage(to, payload) {
|
||||
sendEndpointMessage (to, payload) {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
@@ -2114,7 +1966,7 @@ export default {
|
||||
* @param {String} eventName the name of the event
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
addListener(eventName, listener) {
|
||||
addListener (eventName, listener) {
|
||||
eventEmitter.addListener(eventName, listener);
|
||||
},
|
||||
|
||||
@@ -2124,7 +1976,7 @@ export default {
|
||||
* listener
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
removeListener(eventName, listener) {
|
||||
removeListener (eventName, listener) {
|
||||
eventEmitter.removeListener(eventName, listener);
|
||||
},
|
||||
|
||||
@@ -2137,31 +1989,7 @@ export default {
|
||||
* is currently in the last N set or if there's no last N set at this point
|
||||
* and {false} otherwise
|
||||
*/
|
||||
isInLastN(participantId) {
|
||||
isInLastN (participantId) {
|
||||
return room.isInLastN(participantId);
|
||||
},
|
||||
/**
|
||||
* Changes the display name for the local user
|
||||
* @param nickname {string} the new display name
|
||||
*/
|
||||
changeLocalDisplayName(nickname = '') {
|
||||
const formattedNickname
|
||||
= nickname.trim().substr(0, MAX_DISPLAY_NAME_LENGTH);
|
||||
|
||||
if (formattedNickname === APP.settings.getDisplayName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: this.getMyUserId(),
|
||||
local: true,
|
||||
name: formattedNickname
|
||||
}));
|
||||
|
||||
APP.settings.setDisplayName(formattedNickname);
|
||||
if (room) {
|
||||
room.setDisplayName(formattedNickname);
|
||||
APP.UI.changeDisplayName(this.getMyUserId(), formattedNickname);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
10
config.js
@@ -41,7 +41,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// extension is required.
|
||||
desktopSharingFirefoxExtId: null,
|
||||
// Whether desktop sharing should be disabled on Firefox.
|
||||
desktopSharingFirefoxDisabled: false,
|
||||
desktopSharingFirefoxDisabled: true,
|
||||
// The maximum version of Firefox which requires a jidesha extension.
|
||||
// Example: if set to 41, we will require the extension for Firefox versions
|
||||
// up to and including 41. On Firefox 42 and higher, we will run without the
|
||||
@@ -57,9 +57,6 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
webrtcIceTcpDisable: false,
|
||||
|
||||
openSctp: true, // Toggle to enable/disable SCTP channels
|
||||
|
||||
// Disable hiding of remote thumbnails when in a 1-on-1 conference call.
|
||||
disable1On1Mode: false,
|
||||
disableStats: false,
|
||||
disableAudioLevels: false,
|
||||
channelLastN: -1, // The default value of the channel attribute last-n.
|
||||
@@ -68,6 +65,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
//enableClosePage: false, // enabling the close page will ignore the welcome
|
||||
// page redirection when call is hangup
|
||||
disableSimulcast: false,
|
||||
logStats: false, // Enable logging of PeerConnection stats via the focus
|
||||
// requireDisplayName: true, // Forces the participants that doesn't have display name to enter it when they enter the room.
|
||||
// startAudioMuted: 10, // every participant after the Nth will start audio muted
|
||||
// startVideoMuted: 10, // every participant after the Nth will start video muted
|
||||
@@ -79,8 +77,6 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
'During that time service will not be available. ' +
|
||||
'Apologise for inconvenience.',*/
|
||||
disableThirdPartyRequests: false,
|
||||
// The minumum value a video's height (or width, whichever is smaller) needs
|
||||
// to be in order to be considered high-definition.
|
||||
minHDHeight: 540,
|
||||
// If true - all users without token will be considered guests and all users
|
||||
// with token will be considered non-guests. Only guests will be allowed to
|
||||
@@ -97,7 +93,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// the room. If that succeeds the conference will stop sending data through
|
||||
// the JVB and use the peer to peer connection instead. When 3rd participant
|
||||
// joins the conference will be moved back to the JVB connection.
|
||||
enableP2P: true
|
||||
//enableP2P: true
|
||||
// How long we're going to wait, before going back to P2P after
|
||||
// the 3rd participant has left the conference (to filter out page reload)
|
||||
//backToP2PDelay: 5
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global APP, JitsiMeetJS, config */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Checks if we have data to use attach instead of connect. If we have the data
|
||||
@@ -61,27 +61,22 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
* everything is ok, else error.
|
||||
*/
|
||||
function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const { issuer, jwt } = APP.store.getState()['features/jwt'];
|
||||
|
||||
let connectionConfig = Object.assign({}, config);
|
||||
|
||||
connectionConfig.bosh += '?room=' + roomName;
|
||||
|
||||
let connection
|
||||
= new JitsiMeetJS.JitsiConnection(
|
||||
null,
|
||||
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
||||
connectionConfig);
|
||||
= new JitsiMeetJS.JitsiConnection(null, config.token, connectionConfig);
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_ESTABLISHED,
|
||||
handleConnectionEstablished);
|
||||
ConnectionEvents.CONNECTION_ESTABLISHED, handleConnectionEstablished
|
||||
);
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
handleConnectionFailed);
|
||||
ConnectionEvents.CONNECTION_FAILED, handleConnectionFailed
|
||||
);
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
connectionFailedHandler);
|
||||
ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler);
|
||||
|
||||
function connectionFailedHandler(error, errMsg) {
|
||||
APP.store.dispatch(connectionFailed(connection, error, errMsg));
|
||||
@@ -96,10 +91,12 @@ function connect(id, password, roomName) {
|
||||
function unsubscribe() {
|
||||
connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_ESTABLISHED,
|
||||
handleConnectionEstablished);
|
||||
handleConnectionEstablished
|
||||
);
|
||||
connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED,
|
||||
handleConnectionFailed);
|
||||
handleConnectionFailed
|
||||
);
|
||||
}
|
||||
|
||||
function handleConnectionEstablished() {
|
||||
@@ -132,6 +129,7 @@ function connect(id, password, roomName) {
|
||||
* @returns {Promise<JitsiConnection>}
|
||||
*/
|
||||
export function openConnection({id, password, retry, roomName}) {
|
||||
|
||||
let usernameOverride
|
||||
= jitsiLocalStorage.getItem("xmpp_username_override");
|
||||
let passwordOverride
|
||||
@@ -140,20 +138,25 @@ export function openConnection({id, password, retry, roomName}) {
|
||||
if (usernameOverride && usernameOverride.length > 0) {
|
||||
id = usernameOverride;
|
||||
}
|
||||
|
||||
if (passwordOverride && passwordOverride.length > 0) {
|
||||
password = passwordOverride;
|
||||
}
|
||||
|
||||
return connect(id, password, roomName).catch(err => {
|
||||
if (retry) {
|
||||
const { issuer, jwt } = APP.store.getState()['features/jwt'];
|
||||
|
||||
if (err === ConnectionErrors.PASSWORD_REQUIRED
|
||||
&& (!jwt || issuer === 'anonymous')) {
|
||||
return AuthHandler.requestAuth(roomName, connect);
|
||||
}
|
||||
return connect(id, password, roomName).catch(function (err) {
|
||||
if (!retry) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
if (err === ConnectionErrors.PASSWORD_REQUIRED) {
|
||||
// do not retry if token is not valid
|
||||
if (config.token) {
|
||||
throw err;
|
||||
} else {
|
||||
return AuthHandler.requestAuth(roomName, connect);
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
'extends': '../react/.eslintrc.js'
|
||||
};
|
||||
@@ -1,82 +1,75 @@
|
||||
/* global config, createConnectionExternally */
|
||||
|
||||
import getRoomName from '../react/features/base/config/getRoomName';
|
||||
import parseURLParams from '../react/features/base/config/parseURLParams';
|
||||
|
||||
/* global config, getRoomName, getConfigParamsFromUrl */
|
||||
/* global createConnectionExternally */
|
||||
/**
|
||||
* Implements external connect using createConnectionExternally function defined
|
||||
* in external_connect.js for Jitsi Meet. Parses the room name and JSON Web
|
||||
* Token (JWT) from the URL and executes createConnectionExternally.
|
||||
* Implements extrnal connect using createConnectionExtenally function defined
|
||||
* in external_connect.js for Jitsi Meet. Parses the room name and token from
|
||||
* the url and executes createConnectionExtenally.
|
||||
*
|
||||
* NOTE: If you are using lib-jitsi-meet without Jitsi Meet, you should use this
|
||||
* file as reference only because the implementation is Jitsi Meet-specific.
|
||||
* NOTE: If you are using lib-jitsi-meet without Jitsi Meet you should use this
|
||||
* file as reference only because the implementation is Jitsi Meet specific.
|
||||
*
|
||||
* NOTE: For optimal results this file should be included right after
|
||||
* external_connect.js.
|
||||
* exrnal_connect.js.
|
||||
*/
|
||||
|
||||
if (typeof createConnectionExternally === 'function') {
|
||||
// URL params have higher proirity than config params.
|
||||
let url
|
||||
= parseURLParams(window.location, true, 'hash')[
|
||||
'config.externalConnectUrl']
|
||||
|| config.externalConnectUrl;
|
||||
let roomName;
|
||||
/**
|
||||
* Executes createConnectionExternally function.
|
||||
*/
|
||||
(function () {
|
||||
var hashParams = getConfigParamsFromUrl("hash", true);
|
||||
var searchParams = getConfigParamsFromUrl("search", true);
|
||||
|
||||
if (url && (roomName = getRoomName())) {
|
||||
url += `?room=${roomName}`;
|
||||
//Url params have higher proirity than config params
|
||||
var url = config.externalConnectUrl;
|
||||
if(hashParams.hasOwnProperty('config.externalConnectUrl'))
|
||||
url = hashParams["config.externalConnectUrl"];
|
||||
|
||||
const token = parseURLParams(window.location, true, 'search').jwt;
|
||||
/**
|
||||
* Check if connect from connection.js was executed and executes the handler
|
||||
* that is going to finish the connect work.
|
||||
*/
|
||||
function checkForConnectHandlerAndConnect() {
|
||||
|
||||
if (token) {
|
||||
url += `&token=${token}`;
|
||||
if(window.APP && window.APP.connect.status === "ready") {
|
||||
window.APP.connect.handler();
|
||||
}
|
||||
|
||||
createConnectionExternally(
|
||||
url,
|
||||
connectionInfo => {
|
||||
// Sets that global variable to be used later by connect method
|
||||
// in connection.js.
|
||||
window.XMPPAttachInfo = {
|
||||
status: 'success',
|
||||
data: connectionInfo
|
||||
};
|
||||
checkForConnectHandlerAndConnect();
|
||||
},
|
||||
errorCallback);
|
||||
} else {
|
||||
errorCallback();
|
||||
}
|
||||
} else {
|
||||
errorCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if connect from connection.js was executed and executes the handler
|
||||
* that is going to finish the connect work.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkForConnectHandlerAndConnect() {
|
||||
window.APP
|
||||
&& window.APP.connect.status === 'ready'
|
||||
&& window.APP.connect.handler();
|
||||
}
|
||||
function error_callback(error){
|
||||
if(error) //error=undefined if external connect is disabled.
|
||||
console.warn(error);
|
||||
// Sets that global variable to be used later by connect method in
|
||||
// connection.js
|
||||
window.XMPPAttachInfo = {
|
||||
status: "error"
|
||||
};
|
||||
checkForConnectHandlerAndConnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a callback to be invoked if anything goes wrong.
|
||||
*
|
||||
* @param {Error} error - The specifics of what went wrong.
|
||||
* @returns {void}
|
||||
*/
|
||||
function errorCallback(error) {
|
||||
// The value of error is undefined if external connect is disabled.
|
||||
error && console.warn(error);
|
||||
if(!url || !window.createConnectionExternally) {
|
||||
error_callback();
|
||||
return;
|
||||
}
|
||||
var room_name = getRoomName();
|
||||
if(!room_name) {
|
||||
error_callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Sets that global variable to be used later by connect method in
|
||||
// connection.js.
|
||||
window.XMPPAttachInfo = {
|
||||
status: 'error'
|
||||
};
|
||||
checkForConnectHandlerAndConnect();
|
||||
}
|
||||
url += "?room=" + room_name;
|
||||
|
||||
var token = hashParams["config.token"] || config.token ||
|
||||
searchParams.jwt;
|
||||
if(token)
|
||||
url += "&token=" + token;
|
||||
|
||||
createConnectionExternally(url, function(connectionInfo) {
|
||||
// Sets that global variable to be used later by connect method in
|
||||
// connection.js
|
||||
window.XMPPAttachInfo = {
|
||||
status: "success",
|
||||
data: connectionInfo
|
||||
};
|
||||
checkForConnectHandlerAndConnect();
|
||||
}, error_callback);
|
||||
})();
|
||||
|
||||
@@ -66,4 +66,18 @@
|
||||
@include keyframes(slideInExtContainer) {
|
||||
from { width: 0; }
|
||||
to { width: $sidebarWidth; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Fade in / out animations
|
||||
**/
|
||||
|
||||
@include keyframes(fadeIn) {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@include keyframes(fadeOut) {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
/**
|
||||
* Safari will limit input in input elements to one character when user-select
|
||||
* none is applied. Other browsers already support selecting within inputs while
|
||||
* user-select is none. As such, disallow user-select except on inputs.
|
||||
*/
|
||||
*:not(input) {
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -150,28 +145,3 @@ form {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-style default OS scrollbar.
|
||||
*/
|
||||
::-webkit-scrollbar {
|
||||
background: transparent;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, .5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -31,28 +31,6 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background: #06a5df;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #06a5df;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#chat_container.is-conversation-mode #chatconversation {
|
||||
@@ -234,6 +212,28 @@
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
:not(.default-scrollbar)::-webkit-scrollbar {
|
||||
background: #06a5df;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-track {
|
||||
background: black;
|
||||
}
|
||||
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-track-piece {
|
||||
background: black;
|
||||
}
|
||||
|
||||
:not(.default-scrollbar)::-webkit-scrollbar-thumb {
|
||||
background: #06a5df;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#usermsg::-webkit-scrollbar-track-piece {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
|
||||
31
css/_device_settings_dialog.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
.settingsContent {
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
|
||||
#localVideoPreview {
|
||||
width: 50%;
|
||||
align-self: baseline;
|
||||
}
|
||||
|
||||
.deviceSelection {
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: left;
|
||||
margin-left: 10px;
|
||||
|
||||
.device {
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
|
||||
select {
|
||||
flex: 1;
|
||||
margin_right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* The dialog content element.
|
||||
*/
|
||||
.dial-out-content {
|
||||
margin-top: 5px;
|
||||
|
||||
/**
|
||||
* The style of the flag icon.
|
||||
*/
|
||||
.dial-out-flag-icon {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
* The style of the dial code element.
|
||||
*/
|
||||
.dial-out-code {
|
||||
padding-left: 25px !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* The dial-out dialog error element.
|
||||
*/
|
||||
.dial-out-error {
|
||||
color: $errorColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The style of the dial input element.
|
||||
*/
|
||||
.dial-out-input {
|
||||
padding-left: 70px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-styling the default dropdown inside the dial-out-content.
|
||||
*/
|
||||
.dropdown {
|
||||
left: $formPadding;
|
||||
position: absolute !important;
|
||||
width: 65px
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-styling the default form-control inside the dial-out-content.
|
||||
*/
|
||||
.form-control {
|
||||
padding-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dropdown-trigger-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 4px;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@
|
||||
flex-direction: column-reverse;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
z-index: $zindex1; // Set z-index to make element visible.
|
||||
width: $filmstripToggleButtonWidth;
|
||||
z-index: $zindex1; // Set z-index to make element visible
|
||||
width: $hideFilmstripButtonWidth;
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
@@ -50,7 +50,7 @@
|
||||
position:relative;
|
||||
height:196px;
|
||||
padding: 0;
|
||||
/* The filmstrip should not be covered by the left toolbar. */
|
||||
/*The filmstrip should not be covered by the left toolbar*/
|
||||
padding-left: $defaultToolbarSize + 5;
|
||||
bottom: 0;
|
||||
width:auto;
|
||||
@@ -58,22 +58,14 @@
|
||||
z-index: $filmstripVideosZ;
|
||||
transition: bottom 2s;
|
||||
overflow: visible !important;
|
||||
/*!!! Removes the gap between the local video container and the remote
|
||||
videos. */
|
||||
/*!!!Removes the gap between the local video container and the remote
|
||||
videos.*/
|
||||
font-size: 0pt;
|
||||
|
||||
#filmstripLocalVideo {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
bottom: -196px;
|
||||
}
|
||||
|
||||
.remote-videos-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.videocontainer {
|
||||
display: none;
|
||||
position: relative;
|
||||
@@ -87,8 +79,8 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Focused video thumbnail.
|
||||
*/
|
||||
* Focused video thumbnail.
|
||||
*/
|
||||
&.videoContainerFocused {
|
||||
transition-duration: 0.5s;
|
||||
-webkit-transition-duration: 0.5s;
|
||||
@@ -105,8 +97,8 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Hovered video thumbnail.
|
||||
*/
|
||||
* Hovered video thumbnail.
|
||||
*/
|
||||
&:hover {
|
||||
cursor: hand;
|
||||
border: $thumbnailVideoBorder solid $videoThumbnailHovered;
|
||||
@@ -118,7 +110,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* With the TemasysWebRTC plugin <object/> element is used
|
||||
/* With TemasysWebRTC plugin <object/> element is used
|
||||
instead of <video/> */
|
||||
& > video,
|
||||
& > object {
|
||||
@@ -129,22 +121,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Style the filmstrip videos in filmstrip-only mode.
|
||||
*/
|
||||
&__videos-filmstripOnly {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
padding-right: $defaultToolbarSize;
|
||||
}
|
||||
|
||||
.remote-videos-container {
|
||||
transition: opacity 1s;
|
||||
|
||||
&.hide-videos {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
.flag-icon-background {
|
||||
background-size: contain;
|
||||
background-position: 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.flag-icon {
|
||||
background-size: contain;
|
||||
background-position: 50%;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 1.33333333em;
|
||||
line-height: 1em;
|
||||
}
|
||||
.flag-icon:before {
|
||||
content: "\00a0";
|
||||
}
|
||||
.flag-icon-au {
|
||||
background-image: url(../images/countries/au.svg);
|
||||
}
|
||||
.flag-icon-ca {
|
||||
background-image: url(../images/countries/ca.svg);
|
||||
}
|
||||
.flag-icon-de {
|
||||
background-image: url(../images/countries/de.svg);
|
||||
}
|
||||
.flag-icon-gb {
|
||||
background-image: url(../images/countries/gb.svg);
|
||||
}
|
||||
.flag-icon-fr {
|
||||
background-image: url(../images/countries/fr.svg);
|
||||
}
|
||||
.flag-icon-us {
|
||||
background-image: url(../images/countries/us.svg);
|
||||
}
|
||||
197
css/_font.scss
@@ -26,125 +26,116 @@
|
||||
}
|
||||
|
||||
.icon-mic-camera-combined:before {
|
||||
content: "\e903";
|
||||
content: "\e903";
|
||||
}
|
||||
.icon-feedback:before {
|
||||
content: "\e91d";
|
||||
content: "\e91d";
|
||||
}
|
||||
.icon-toggle-filmstrip:before {
|
||||
content: "\e91c";
|
||||
content: "\e91c";
|
||||
}
|
||||
.icon-avatar:before {
|
||||
content: "\e901";
|
||||
content: "\e901";
|
||||
}
|
||||
.icon-hangup:before {
|
||||
content: "\e905";
|
||||
content: "\e905";
|
||||
}
|
||||
.icon-chat:before {
|
||||
content: "\e906";
|
||||
content: "\e906";
|
||||
}
|
||||
.icon-download:before {
|
||||
content: "\e902";
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-edit:before {
|
||||
content: "\e907";
|
||||
content: "\e907";
|
||||
}
|
||||
.icon-share-doc:before {
|
||||
content: "\e908";
|
||||
}
|
||||
.icon-kick:before {
|
||||
content: "\e904";
|
||||
}
|
||||
.icon-menu-up:before {
|
||||
content: "\e91f";
|
||||
}
|
||||
.icon-menu-down:before {
|
||||
content: "\e920";
|
||||
}
|
||||
.icon-full-screen:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
.icon-exit-full-screen:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
.icon-star-full:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.icon-security:before {
|
||||
content: "\e90d";
|
||||
}
|
||||
.icon-security-locked:before {
|
||||
content: "\e90e";
|
||||
}
|
||||
.icon-reload:before {
|
||||
content: "\e90f";
|
||||
}
|
||||
.icon-microphone:before {
|
||||
content: "\e910";
|
||||
}
|
||||
.icon-mic-empty:before {
|
||||
content: "\e911";
|
||||
}
|
||||
.icon-mic-disabled:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.icon-raised-hand:before {
|
||||
content: "\e91e";
|
||||
}
|
||||
.icon-contactList:before {
|
||||
content: "\e91b";
|
||||
}
|
||||
.icon-link:before {
|
||||
content: "\e913";
|
||||
}
|
||||
.icon-shared-video:before {
|
||||
content: "\e914";
|
||||
}
|
||||
.icon-settings:before {
|
||||
content: "\e915";
|
||||
}
|
||||
.icon-star:before {
|
||||
content: "\e916";
|
||||
}
|
||||
.icon-switch-camera:before {
|
||||
content: "\e921";
|
||||
}
|
||||
.icon-share-desktop:before {
|
||||
content: "\e917";
|
||||
}
|
||||
.icon-camera:before {
|
||||
content: "\e918";
|
||||
}
|
||||
.icon-camera-disabled:before {
|
||||
content: "\e919";
|
||||
}
|
||||
.icon-volume:before {
|
||||
content: "\e91a";
|
||||
}
|
||||
.icon-connection-lost:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.icon-connection:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
.icon-recDisable:before {
|
||||
content: "\e613";
|
||||
}
|
||||
.icon-recEnable:before {
|
||||
content: "\e614";
|
||||
}
|
||||
.icon-presentation:before {
|
||||
content: "\e603";
|
||||
}
|
||||
.icon-dialpad:before {
|
||||
content: "\e925";
|
||||
}
|
||||
.icon-visibility:before {
|
||||
content: "\e923";
|
||||
}
|
||||
.icon-visibility-off:before {
|
||||
content: "\e924";
|
||||
content: "\e908";
|
||||
}
|
||||
.icon-telephone:before {
|
||||
content: "\e0cd";
|
||||
content: "\e909";
|
||||
}
|
||||
.icon-kick:before {
|
||||
content: "\e904";
|
||||
}
|
||||
.icon-menu-up:before {
|
||||
content: "\e91f";
|
||||
}
|
||||
.icon-menu-down:before {
|
||||
content: "\e920";
|
||||
}
|
||||
.icon-full-screen:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
.icon-exit-full-screen:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
.icon-star-full:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.icon-security:before {
|
||||
content: "\e90d";
|
||||
}
|
||||
.icon-security-locked:before {
|
||||
content: "\e90e";
|
||||
}
|
||||
.icon-reload:before {
|
||||
content: "\e90f";
|
||||
}
|
||||
.icon-microphone:before {
|
||||
content: "\e910";
|
||||
}
|
||||
.icon-mic-empty:before {
|
||||
content: "\e911";
|
||||
}
|
||||
.icon-mic-disabled:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.icon-raised-hand:before {
|
||||
content: "\e91e";
|
||||
}
|
||||
.icon-contactList:before {
|
||||
content: "\e91b";
|
||||
}
|
||||
.icon-link:before {
|
||||
content: "\e913";
|
||||
}
|
||||
.icon-shared-video:before {
|
||||
content: "\e914";
|
||||
}
|
||||
.icon-settings:before {
|
||||
content: "\e915";
|
||||
}
|
||||
.icon-star:before {
|
||||
content: "\e916";
|
||||
}
|
||||
.icon-switch-camera:before {
|
||||
content: "\e921";
|
||||
}
|
||||
.icon-share-desktop:before {
|
||||
content: "\e917";
|
||||
}
|
||||
.icon-camera:before {
|
||||
content: "\e918";
|
||||
}
|
||||
.icon-camera-disabled:before {
|
||||
content: "\e919";
|
||||
}
|
||||
.icon-volume:before {
|
||||
content: "\e91a";
|
||||
}
|
||||
.icon-connection-lost:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.icon-connection:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
.icon-recDisable:before {
|
||||
content: "\e613";
|
||||
}
|
||||
.icon-recEnable:before {
|
||||
content: "\e614";
|
||||
}
|
||||
.icon-presentation:before {
|
||||
content: "\e603";
|
||||
}
|
||||
@@ -93,7 +93,6 @@
|
||||
}
|
||||
|
||||
&__avatar-container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
> img {
|
||||
height: 100%;
|
||||
|
||||
@@ -44,28 +44,4 @@
|
||||
border-width: 5px;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default "top" styles to support popovers appearing from the
|
||||
* left of the popover trigger element.
|
||||
*/
|
||||
&.left {
|
||||
margin-left: -$popoverMenuPadding;
|
||||
margin-top: 0;
|
||||
|
||||
.arrow {
|
||||
border-color: transparent transparent transparent $popoverBg;
|
||||
border-width: 5px 0px 5px 5px;
|
||||
margin-left: 0;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.jitsipopover {
|
||||
&__menu-padding {
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: $popoverMenuPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
.notice {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
position: relative;
|
||||
z-index: $zindex3;
|
||||
margin-top: 6px;
|
||||
|
||||
@include transform(translateX(-50%));
|
||||
|
||||
&__message {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
|
||||
@@ -41,37 +41,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
&__text,
|
||||
&__slider {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&__contents {
|
||||
display: flex;
|
||||
|
||||
/**
|
||||
* Positioning styles on the slider and its container are used to make
|
||||
* the container fit the popup width, by removing the slider from the
|
||||
* page flow, and then making the slider fit the container.
|
||||
*/
|
||||
.popupmenu__slider_container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.popupmenu__slider {
|
||||
bottom: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&__slider {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
|
||||
|
||||
@@ -113,12 +113,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#deviceOptionsWrapper {
|
||||
button {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile
|
||||
*/
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
#toast-container.notification-bottom-right {
|
||||
$videoOffset: 2 * ($thumbnailVideoMargin + $thumbnailsBorder) + $thumbnailVideoBorder;
|
||||
bottom: 135px;
|
||||
right: $filmstripToggleButtonWidth + $videoOffset;
|
||||
right: $hideFilmstripButtonWidth + $videoOffset;
|
||||
}
|
||||
|
||||
#toast-container * {
|
||||
|
||||
@@ -1,262 +1,186 @@
|
||||
/**
|
||||
* Round badge.
|
||||
*/
|
||||
.badge-round {
|
||||
background-color: $toolbarBadgeBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
color: $toolbarBadgeColor;
|
||||
// Do not inherit the font-family from the toolbar button, because it's an
|
||||
// icon style.
|
||||
font-family: $baseFontFamily;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
line-height: 13px;
|
||||
min-width: 13px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar button styles.
|
||||
*/
|
||||
.button {
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
z-index: $zindex1;
|
||||
display: inline-block;
|
||||
font-size: $toolbarFontSize !important;
|
||||
height: 50px;
|
||||
line-height: 50px !important;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
top:0px;
|
||||
vertical-align: middle;
|
||||
width: 50px;
|
||||
|
||||
&_hangup {
|
||||
color: $hangupColor;
|
||||
font-size: $hangupFontSize !important;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover, &:active {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:not(.toggled) {
|
||||
&:hover, &:active {
|
||||
// sum opacity with background layer should give us 0.8
|
||||
background: $toolbarSelectBackground;
|
||||
}
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: $toolbarToggleBackground;
|
||||
|
||||
&.icon-camera {
|
||||
@extend .icon-camera-disabled;
|
||||
}
|
||||
|
||||
&.icon-full-screen {
|
||||
@extend .icon-exit-full-screen;
|
||||
}
|
||||
|
||||
&.icon-microphone {
|
||||
@extend .icon-mic-disabled;
|
||||
}
|
||||
|
||||
&.icon-visibility {
|
||||
@extend .icon-visibility-off;
|
||||
}
|
||||
}
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
|
||||
&:hover, &:active, &.selected {
|
||||
background: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-container {
|
||||
display: block;
|
||||
left:0;
|
||||
min-height: 100px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right:0;
|
||||
text-align: center;
|
||||
top:0;
|
||||
z-index: $toolbarZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common toolbar styles.
|
||||
*/
|
||||
.toolbar {
|
||||
background-color: $toolbarBackground;
|
||||
height: 100%;
|
||||
pointer-events: auto;
|
||||
position: relative;
|
||||
z-index: $toolbarZ;
|
||||
height: 100%;
|
||||
pointer-events: auto;
|
||||
|
||||
/**
|
||||
* Splitter button in the toolbar.
|
||||
*/
|
||||
&__splitter {
|
||||
background: $splitterColor;
|
||||
display: inline-block;
|
||||
height: 50%;
|
||||
margin: 0 $splitterToolbarButtonMargin;
|
||||
vertical-align: middle;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary toolbar styles.
|
||||
*/
|
||||
&_primary {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 30px;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: $defaultToolbarSize;
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
|
||||
@include transform(translateX(-50%));
|
||||
|
||||
.button:first-child {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
.button:last-child {
|
||||
border-bottom-right-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&_primary a.button:last-child::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Secondary toolbar styles.
|
||||
*/
|
||||
&_secondary {
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
height: 100%;
|
||||
justify-content: flex-start;
|
||||
left: 0;
|
||||
padding-top: 10px;
|
||||
top: 0;
|
||||
transform: translateX(-100%);
|
||||
width: $defaultToolbarSize;
|
||||
-webkit-transform: translateX(-100%);
|
||||
|
||||
.button.toggled:not(.icon-raised-hand):not(.button-active) {
|
||||
background: $toolbarSelectBackground;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
|
||||
&:hover, &:active, &.selected {
|
||||
background: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles the toolbar in filmstrip-only mode.
|
||||
*/
|
||||
&_filmstrip-only {
|
||||
border-radius: 3px;
|
||||
bottom: 0;
|
||||
display: inline-block;
|
||||
height: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: $defaultToolbarSize;
|
||||
|
||||
.button:first-child {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
.button:last-child {
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar specific round badge.
|
||||
*/
|
||||
.badge-round {
|
||||
bottom: 9px;
|
||||
position: absolute;
|
||||
right: 9px;
|
||||
height: 50%;
|
||||
margin: 0 $splitterToolbarButtonMargin;
|
||||
background: $splitterColor;
|
||||
}
|
||||
}
|
||||
|
||||
.subject {
|
||||
background: linear-gradient(to bottom, rgba(255,255,255,.85) , rgba(255,255,255,.35));
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
box-shadow: 0 0 2px #000000, 0 0 10px #000000;
|
||||
#mainToolbarContainer{
|
||||
display: block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
z-index: $toolbarZ;
|
||||
pointer-events: none;
|
||||
min-height: 100px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#subject {
|
||||
position: relative;
|
||||
z-index: $zindex3;
|
||||
width: auto;
|
||||
padding: 5px;
|
||||
margin-left: 40%;
|
||||
margin-right: 40%;
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
background: linear-gradient(to bottom, rgba(255,255,255,.85) , rgba(255,255,255,.35));
|
||||
box-shadow: 0 0 2px #000000, 0 0 10px #000000;
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
#mainToolbar {
|
||||
height: $defaultToolbarSize;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 30px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: auto;
|
||||
z-index: $zindex3;
|
||||
|
||||
&.subject_slide-in {
|
||||
top: 80px;
|
||||
@include transition(top .3s ease-in);
|
||||
border-radius: 3px;
|
||||
.button:first-child {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
&.subject_slide-out {
|
||||
top: 0;
|
||||
@include transition(top .3s ease-out);
|
||||
.button:last-child {
|
||||
border-bottom-right-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
#extendedToolbar {
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
width: $defaultToolbarSize;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding-top: 10px;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
transform: translateX(-100%);
|
||||
-webkit-transform: translateX(-100%);
|
||||
}
|
||||
|
||||
#toolbar_button_hangup {
|
||||
color: #BF2117;
|
||||
font-size: $hangupFontSize !important;
|
||||
}
|
||||
|
||||
#toolbar_button_etherpad {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mainToolbar a.button:last-child::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
color: #FFFFFF;
|
||||
top:0px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
z-index: $zindex1;
|
||||
font-size: $toolbarFontSize !important;
|
||||
line-height: 50px !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.button[disabled] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.button.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.toggled {
|
||||
background: $toolbarToggleBackground !important;
|
||||
}
|
||||
|
||||
a.button.unclickable:hover,
|
||||
a.button.unclickable:active,
|
||||
a.button.unclickable.selected{
|
||||
cursor: default;
|
||||
background: none;
|
||||
}
|
||||
|
||||
a.button:hover,
|
||||
a.button:active,
|
||||
a.button.selected {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
// sum opacity with background layer should give us 0.8
|
||||
background: $toolbarSelectBackground;
|
||||
}
|
||||
|
||||
a.button>#avatar {
|
||||
border-radius: 50%;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
width: 30px;
|
||||
border-radius: 50%;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#feedbackButton {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Round badge.
|
||||
*/
|
||||
.badge-round {
|
||||
background-color: $toolbarBadgeBackground;
|
||||
color: $toolbarBadgeColor;
|
||||
font-size: 9px;
|
||||
line-height: 13px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
min-width: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
// Do not inherit the font-family from the toolbar button, because it's an
|
||||
// icon style.
|
||||
font-family: $baseFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar specific round badge.
|
||||
*/
|
||||
.toolbar .badge-round {
|
||||
position: absolute;
|
||||
right: 9px;
|
||||
bottom: 9px;
|
||||
}
|
||||
|
||||
/**
|
||||
* START of slide in animation for extended toolbar.
|
||||
*/
|
||||
@@ -348,13 +272,9 @@ a.button>#avatar {
|
||||
* START of fade in animation for main toolbar
|
||||
*/
|
||||
.fadeIn {
|
||||
opacity: 1;
|
||||
|
||||
@include transition(all .3s ease-in);
|
||||
@include animation('fadeIn .3s linear .2s forwards');
|
||||
}
|
||||
|
||||
.fadeOut {
|
||||
opacity: 0;
|
||||
|
||||
@include transition(all .3s ease-out);
|
||||
@include animation('fadeOut .5s linear forwards');
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
* Style variables
|
||||
*/
|
||||
$baseFontFamily: 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$hangupColor: #bf2117;
|
||||
$toolbarFontSize: 1.9em;
|
||||
$hangupFontSize: 2em;
|
||||
|
||||
/**
|
||||
* Size variables.
|
||||
*/
|
||||
$defaultToolbarSize: 50px;
|
||||
|
||||
// Video layout.
|
||||
$thumbnailToolbarHeight: 22px;
|
||||
@@ -18,7 +19,7 @@ $thumbnailIndicatorSize: $thumbnailToolbarHeight;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailsBorder: 2px;
|
||||
$thumbnailVideoBorder: 2px;
|
||||
$filmstripToggleButtonWidth: 17px;
|
||||
$hideFilmstripButtonWidth: 17px;
|
||||
|
||||
|
||||
/**
|
||||
@@ -33,16 +34,14 @@ $tooltipBg: rgba(0,0,0, 0.7);
|
||||
/**
|
||||
* Toolbar
|
||||
*/
|
||||
$defaultToolbarSize: 50px;
|
||||
$splitterToolbarButtonMargin: 18px;
|
||||
$toolbarBackground: rgba(0, 0, 0, 0.5);
|
||||
$toolbarBadgeBackground: #165ECC;
|
||||
$toolbarBadgeColor: #FFFFFF;
|
||||
$toolbarFontSize: 1.9em;
|
||||
$toolbarSelectBackground: rgba(0, 0, 0, .6);
|
||||
$toolbarTitleColor: #FFFFFF;
|
||||
$toolbarTitleFontSize: 19px;
|
||||
$toolbarBackground: rgba(0, 0, 0, 0.5);
|
||||
$toolbarSelectBackground: rgba(0, 0, 0, .6);
|
||||
$toolbarBadgeBackground: #165ECC;
|
||||
$toolbarBadgeColor: #FFFFFF;
|
||||
$toolbarToggleBackground: #12499C;
|
||||
$splitterToolbarButtonMargin: 18px;
|
||||
|
||||
/**
|
||||
* Main controls
|
||||
@@ -79,14 +78,6 @@ $rateStarDefault: #ccc;
|
||||
$rateStarActivity: #165ecc;
|
||||
$rateStarSize: 34px;
|
||||
|
||||
/**
|
||||
* Modals
|
||||
*/
|
||||
$modalButtonFontSize: 14px;
|
||||
$modalMockAKInputBackground: #fafbfc;
|
||||
$modalMockAKInputBorder: 1px solid #f4f5f7;
|
||||
$modalTextColor: #333;
|
||||
|
||||
/**
|
||||
* Notifications
|
||||
*/
|
||||
@@ -127,11 +118,11 @@ $toolbarZ: 400;
|
||||
$tooltipsZ: 401;
|
||||
$dropdownMaskZ: 900;
|
||||
$dropdownZ: 901;
|
||||
$overlayZ: 902;
|
||||
$jitsipopoverZ: 1010;
|
||||
$centeredVideoLabelZ: 1011;
|
||||
$notificationZ: 1012;
|
||||
$popoverZ: 1015;
|
||||
$overlayZ: 1016;
|
||||
|
||||
|
||||
/**
|
||||
@@ -149,7 +140,6 @@ $inputControlEmColor: #f29424;
|
||||
//buttons
|
||||
$linkFontColor: #489afe;
|
||||
$linkHoverFontColor: #287ade;
|
||||
$formPadding: 16px;
|
||||
|
||||
/**
|
||||
* Unsupported browser
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
/**
|
||||
* Override other styles to support vertical filmstrip mode.
|
||||
*/
|
||||
.vertical-filmstrip {
|
||||
.filmstrip {
|
||||
align-items: flex-end;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
|
||||
/**
|
||||
* Hide videos by making them slight to the right.
|
||||
*/
|
||||
.filmstrip__videos {
|
||||
right: 0;
|
||||
transition: right 2s;
|
||||
|
||||
&.hidden {
|
||||
bottom: auto;
|
||||
right: -196px;
|
||||
}
|
||||
}
|
||||
|
||||
#filmstripLocalVideo {
|
||||
height: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecssary padding that is normally used to prevent horizontal
|
||||
* filmstrip from overlapping the left edge of the screen.
|
||||
*/
|
||||
#filmstripLocalVideo,
|
||||
#filmstripRemoteVideos {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
overflow-x: hidden !important;
|
||||
|
||||
.remote-videos-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the hide filmstrip icon so it points towards the right edge
|
||||
* of the screen.
|
||||
*/
|
||||
&__toolbar {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the remote video menu trigger to the bottom left of the
|
||||
* video thumbnail.
|
||||
*/
|
||||
.remotevideomenu {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
top: auto;
|
||||
right: auto;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
#remoteVideos {
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.videocontainer {
|
||||
/**
|
||||
* Move status icons to the bottom right of the thumbnail.
|
||||
*/
|
||||
&__toolbar {
|
||||
text-align: right;
|
||||
|
||||
.toolbar-icon {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These styles are for the video labels that display on the top right. The
|
||||
* styles adjust the labels' positioning as the filmstrip itself or
|
||||
* filmstrip's remote videos appear and disappear.
|
||||
*
|
||||
* The class with-filmstrip is for when the filmstrip is visible.
|
||||
* The class without-filmstrip is for when the filmstrip has been toggled to
|
||||
* be hidden.
|
||||
* The class opening is for when the filmstrip is transitioning from hidden
|
||||
* to visible.
|
||||
* The class with-remote-videos is for when the filmstrip has remote videos
|
||||
* displayed, as opposed to 1-on-1 mode where they might be hidden.
|
||||
* The class without-remote-videos is for when the filmstrip is visible
|
||||
* but it has no videos to display.
|
||||
*/
|
||||
.video-state-indicator.moveToCorner {
|
||||
transition: right 0.5s;
|
||||
|
||||
&.with-filmstrip.with-remote-videos {
|
||||
&#recordingLabel {
|
||||
right: 200px;
|
||||
}
|
||||
|
||||
&#videoResolutionLabel {
|
||||
right: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
&.with-filmstrip.without-remote-videos {
|
||||
transition-delay: 0.5s;
|
||||
}
|
||||
|
||||
&.with-filmstrip.with-remote-videos.opening {
|
||||
transition: 0.9s;
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
&.without-filmstrip {
|
||||
transition: 1.2s ease-in-out;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move toastr closer to the bottom of the screen and move left to avoid
|
||||
* overlapping of videos when they are configured at default height.
|
||||
*/
|
||||
#toast-container {
|
||||
&.notification-bottom-right {
|
||||
bottom: 25px;
|
||||
right: 130 + 2 * ($thumbnailVideoMargin + $thumbnailsBorder) + $thumbnailVideoBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,12 +115,6 @@
|
||||
visibility: hidden;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&.audio-only {
|
||||
.videoThumbnailProblemFilter {
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#localVideoWrapper {
|
||||
@@ -495,42 +489,31 @@
|
||||
0px 0px 1px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.filmstrip-only {
|
||||
#videoResolutionLabel {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.video-state-indicator {
|
||||
background: $videoStateIndicatorBackground;
|
||||
color: $videoStateIndicatorColor;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
padding: 10px 5px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
#videoResolutionLabel,
|
||||
.centeredVideoLabel.moveToCorner {
|
||||
z-index: $tooltipsZ;
|
||||
.centeredVideoLabel {
|
||||
display: none;
|
||||
z-index: $centeredVideoLabelZ;
|
||||
}
|
||||
|
||||
.centeredVideoLabel {
|
||||
bottom: 45%;
|
||||
border-radius: 2px;
|
||||
display: none;
|
||||
-webkit-transition: all 2s 2s linear;
|
||||
transition: all 2s 2s linear;
|
||||
z-index: $centeredVideoLabelZ;
|
||||
|
||||
&.moveToCorner {
|
||||
bottom: auto;
|
||||
@@ -545,60 +528,4 @@
|
||||
|
||||
.moveToCorner + .moveToCorner {
|
||||
right: 80px;
|
||||
}
|
||||
|
||||
.video-state-indicator-menu {
|
||||
display: none;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: 20px;
|
||||
|
||||
.video-state-indicator-menu-options {
|
||||
background: $popoverBg;
|
||||
border-radius: 3px;
|
||||
color: $popoverFontColor;
|
||||
margin-top: 20px;
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
|
||||
div {
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
padding-right: 30px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
|
||||
&.active {
|
||||
background: $toolbarToggleBackground;
|
||||
}
|
||||
&:hover:not(.active) {
|
||||
background: $popupMenuSelectedItemBackground;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video-state-indicator-menu-options::after {
|
||||
content: " ";
|
||||
border-color: transparent transparent $popoverBg transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-state-indicator:hover,
|
||||
.video-state-indicator *:hover {
|
||||
background: $toolbarSelectBackground;
|
||||
|
||||
.video-state-indicator-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
.form-control {
|
||||
padding: $formPadding 0;
|
||||
padding: 16px 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
|
||||
@@ -26,13 +26,11 @@
|
||||
|
||||
@import 'font';
|
||||
@import 'font-awesome';
|
||||
/* Fonts END */
|
||||
|
||||
@import 'flag-icon';
|
||||
/* Fonts END */
|
||||
|
||||
/* Modules BEGIN */
|
||||
|
||||
@import 'dial-out';
|
||||
@import 'toastr';
|
||||
@import 'base';
|
||||
@import 'utils';
|
||||
@@ -40,7 +38,6 @@
|
||||
@import 'inlay';
|
||||
@import 'reload_overlay/reload_overlay';
|
||||
@import 'modals/desktop-picker/desktop-picker';
|
||||
@import 'modals/device-selection/device-selection';
|
||||
@import 'modals/dialog';
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'modals/speaker_stats/speaker_stats';
|
||||
@@ -57,6 +54,7 @@
|
||||
@import 'welcome_page';
|
||||
@import 'toolbars';
|
||||
@import 'side_toolbar_container';
|
||||
@import 'device_settings_dialog';
|
||||
@import 'jquery.contextMenu';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'redirect_page';
|
||||
@@ -73,6 +71,5 @@
|
||||
@import 'policy';
|
||||
@import 'filmstrip';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'vertical_filmstrip_overrides';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -76,16 +76,3 @@
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form {
|
||||
color: $modalTextColor;
|
||||
|
||||
.input-control {
|
||||
background: $modalMockAKInputBackground;
|
||||
border: $modalMockAKInputBorder;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
.modal-dialog-footer {
|
||||
font-size: $modalButtonFontSize;
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
.device-selection {
|
||||
color: $feedbackInputTextColor;
|
||||
|
||||
.device-selectors {
|
||||
font-size: 14px;
|
||||
|
||||
> div {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
> div:last-child {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.device-selector-icon {
|
||||
align-self: center;
|
||||
color: inherit;
|
||||
font-size: 20px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
/* device-selector-trigger stylings attempt to mimic AtlasKit button */
|
||||
.device-selector-trigger {
|
||||
background-color: rgba(9, 30, 66, 0.04);
|
||||
border-radius: 3px;
|
||||
color: #505f79;
|
||||
display: flex;
|
||||
height: 2.3em;
|
||||
justify-content: space-between;
|
||||
line-height: 2.3em;
|
||||
overflow: hidden;
|
||||
padding: 0 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(9,30,66,.08);
|
||||
}
|
||||
}
|
||||
.device-selector-trigger-disabled {
|
||||
.device-selector-trigger {
|
||||
color: #a5adba;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.device-selector-trigger-text {
|
||||
overflow: hidden;
|
||||
margin-left: 8px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.device-selection-column {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
&.column-selectors {
|
||||
margin-left: 15px;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
&.column-video {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.device-selection-video-container {
|
||||
border-radius: 3px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.video-input-preview {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
|
||||
> video {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.video-input-preview-error {
|
||||
color: $participantNameColor;
|
||||
display: none;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&.video-preview-has-error {
|
||||
background: black;
|
||||
|
||||
.video-input-preview-error {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.video-input-preview-display {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.audio-output-preview {
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-input-preview {
|
||||
background: #f4f5f7;
|
||||
border-radius: 5px;
|
||||
height: 6px;
|
||||
|
||||
.audio-input-preview-level {
|
||||
background: #0052cc;
|
||||
border-radius: 5px;
|
||||
height: 100%;
|
||||
-webkit-transition: width .1s ease-in-out;
|
||||
-moz-transition: width .1s ease-in-out;
|
||||
-o-transition: width .1s ease-in-out;
|
||||
transition: width .1s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,84 +4,4 @@
|
||||
*/
|
||||
#inviteDialogRemovePassword {
|
||||
cursor: hand;
|
||||
}
|
||||
|
||||
.invite-dialog {
|
||||
.dial-in-numbers {
|
||||
.dial-in-numbers-conference-id {
|
||||
color: orange;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
/*
|
||||
* dial-in-numbers-copy styling is needed for the feature of copying
|
||||
* text to the clipboard. The styling keeps the element invisible
|
||||
* to the user but still programmatically selectable for copying.
|
||||
*/
|
||||
.dial-in-numbers-copy {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.dial-in-numbers-trigger {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.dial-in-numbers-trigger-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.is-disabled,
|
||||
.is-loading {
|
||||
.dial-in-numbers-trigger-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-control {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.inviteLink {
|
||||
color: $readOnlyInputColor;
|
||||
}
|
||||
|
||||
.lock-state {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.password-overview {
|
||||
margin-top: 10px;
|
||||
|
||||
.form-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.password-overview-status,
|
||||
.remove-password {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.password-overview-toggle-edit,
|
||||
.remove-password-link {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.remove-password {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.remove-password-current {
|
||||
color: $inputControlEmColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
.speaker-stats {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
color: $auiDialogColor;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
z-index: $overlayZ;
|
||||
background: $defaultBackground;
|
||||
&.filmstrip-only {
|
||||
@include transparentBg($filmstripOnlyOverlayBg, 0.8);
|
||||
@include transparentBg($filmStripOnlyOverlayBg, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
&__container-light {
|
||||
@include transparentBg($defaultBackground, 0.7);
|
||||
&.filmstrip-only {
|
||||
@include transparentBg($filmstripOnlyOverlayBg, 0.2);
|
||||
@include transparentBg($filmStripOnlyOverlayBg, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ $overlayButtonBg: #0074E0;
|
||||
* Color variables
|
||||
**/
|
||||
$defaultBackground: #474747;
|
||||
$filmstripOnlyOverlayBg: #000;
|
||||
$filmStripOnlyOverlayBg: #000;
|
||||
$reloadProgressBarBg: #0074E0;
|
||||
|
||||
/**
|
||||
|
||||
6
debian/jitsi-meet-tokens.postinst
vendored
@@ -48,9 +48,7 @@ case "$1" in
|
||||
db_stop
|
||||
|
||||
if [ -f "$PROSODY_HOST_CONFIG" ] ; then
|
||||
# search for --plugin_paths, if this is not enabled this is the
|
||||
# first time we install tokens package and needs a config change
|
||||
if grep -q "\-\-plugin_paths" "$PROSODY_HOST_CONFIG"; then
|
||||
if grep -q "plugin_paths" "$PROSODY_HOST_CONFIG"; then
|
||||
# enable tokens in prosody host config
|
||||
sed -i 's/--plugin_paths/plugin_paths/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/authentication = "anonymous"/authentication = "token"/g' $PROSODY_HOST_CONFIG
|
||||
@@ -72,6 +70,8 @@ case "$1" in
|
||||
echo "Use the following command, after this package has been installed and"
|
||||
echo "after every prosody-trunk upgrade:"
|
||||
echo "sudo patch -N /usr/lib/prosody/modules/mod_bosh.lua /usr/share/jitsi-meet/prosody-plugins/mod_bosh.lua.patch"
|
||||
else
|
||||
echo "Failed apply auto-config to $PROSODY_HOST_CONFIG which most likely comes from not supported version of jitsi-meet"
|
||||
fi
|
||||
else
|
||||
echo "Prosody config not found at $PROSODY_HOST_CONFIG - unable to auto-configure token authentication"
|
||||
|
||||
12
doc/api.md
@@ -44,7 +44,7 @@ var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement);
|
||||
```
|
||||
|
||||
You can overwrite options set in [config.js] and [interface_config.js].
|
||||
For example, to enable the filmstrip-only interface mode, you can use:
|
||||
For example, to enable the film-strip-only interface mode, you can use:
|
||||
|
||||
```javascript
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
@@ -84,7 +84,7 @@ api.executeCommand('toggleAudio')
|
||||
api.executeCommand('toggleVideo')
|
||||
```
|
||||
|
||||
* **toggleFilmStrip** - Hides / shows the filmstrip. No arguments are required.
|
||||
* **toggleFilmStrip** - Hides / shows the film strip. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleFilmStrip')
|
||||
```
|
||||
@@ -123,13 +123,12 @@ You can also execute multiple commands using the `executeCommands` method:
|
||||
```javascript
|
||||
api.executeCommands(commands)
|
||||
```
|
||||
The `commands` parameter is an object with the names of the commands as keys and the arguments for the commands as values:
|
||||
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 the `addEventListener` method.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods (`addListener` or `on`).**
|
||||
```javascript
|
||||
api.addEventListener(event, listener)
|
||||
```
|
||||
@@ -199,7 +198,6 @@ changes. The listener will receive an object with the following structure:
|
||||
You can also add multiple event listeners by using `addEventListeners`.
|
||||
This method requires one argument of type Object. The object argument must
|
||||
have the names of the events as keys and the listeners of the events as values.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods.**
|
||||
|
||||
```javascript
|
||||
function incomingMessageListener(object)
|
||||
@@ -218,13 +216,12 @@ api.addEventListeners({
|
||||
```
|
||||
|
||||
If you want to remove a listener you can use `removeEventListener` method with argument the name of the event.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods( `removeListener`).**
|
||||
|
||||
```javascript
|
||||
api.removeEventListener("incomingMessage");
|
||||
```
|
||||
|
||||
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.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods.**
|
||||
```javascript
|
||||
api.removeEventListeners(["incomingMessage", "outgoingMessageListener"]);
|
||||
```
|
||||
@@ -243,4 +240,3 @@ 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
|
||||
[EventEmitter]: https://nodejs.org/api/events.html
|
||||
|
||||
@@ -76,18 +76,9 @@
|
||||
projects. For example:
|
||||
|
||||
* The instance of lib-jitsi-meet's `JitsiConnection` type should be named
|
||||
`connection` or `jitsiConnection` in jitsi-meet, not `client`.
|
||||
`connection` or `jitsiConnection` in jitsi-meet-react, not `client`.
|
||||
|
||||
* The class `ReducerRegistry` should be defined in ReducerRegistry.js and its
|
||||
imports in other files should use the same name. Don't define the class
|
||||
`Registry` in ReducerRegistry.js and then import it as `Reducers` in other
|
||||
files.
|
||||
|
||||
* The names of global constants (including ES6 module-global constants) should
|
||||
be written in uppercase with underscores to separate words. For example,
|
||||
`BACKGROUND_COLOR`.
|
||||
|
||||
* The underscore character at the beginning of a name signals that the
|
||||
respective variable, function, property is non-public i.e. private, protected,
|
||||
or internal. In contrast, the lack of an underscore at the beginning of a name
|
||||
signals public API.
|
||||
|
||||
@@ -23,10 +23,7 @@ VirtualHost "jitmeet.example.com"
|
||||
"ping"; -- Enable mod_ping
|
||||
}
|
||||
|
||||
c2s_require_encryption = false
|
||||
|
||||
Component "conference.jitmeet.example.com" "muc"
|
||||
storage = "null"
|
||||
--modules_enabled = { "token_verification" }
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
|
||||
|
||||
@@ -179,8 +179,6 @@ VirtualHost "jitsi.example.com"
|
||||
certificate = "/var/lib/prosody/jitsi.example.com.crt";
|
||||
}
|
||||
|
||||
c2s_require_encryption = false
|
||||
|
||||
------ Components ------
|
||||
-- You can specify components to add hosts that provide special services,
|
||||
-- like multi-user conferences, and transports.
|
||||
|
||||
@@ -55,7 +55,6 @@ VirtualHost "jitsi.example.com"
|
||||
"bosh";
|
||||
"pubsub";
|
||||
}
|
||||
c2s_require_encryption = false
|
||||
```
|
||||
- add domain with authentication for conference focus user:
|
||||
```
|
||||
@@ -238,4 +237,23 @@ You are now all set and ready to have your first meet by going to http://jitsi.e
|
||||
|
||||
|
||||
## Enabling recording
|
||||
[Jibri](https://github.com/jitsi/jibri)is a set of tools for recording and/or streaming a Jitsi Meet conference.
|
||||
Currently recording is only supported for linux-64 and macos. To enable it, add
|
||||
the following properties to sip-communicator.properties:
|
||||
```
|
||||
org.jitsi.videobridge.ENABLE_MEDIA_RECORDING=true
|
||||
org.jitsi.videobridge.MEDIA_RECORDING_PATH=/path/to/recordings/dir
|
||||
org.jitsi.videobridge.MEDIA_RECORDING_TOKEN=secret
|
||||
```
|
||||
|
||||
where /path/to/recordings/dir is the path to a pre-existing directory where recordings
|
||||
will be stored (needs to be writeable by the user running jitsi-videobridge),
|
||||
and "secret" is a string which will be used for authentication.
|
||||
|
||||
Then, edit the Jitsi-Meet config.js file and set:
|
||||
```
|
||||
enableRecording: true
|
||||
```
|
||||
|
||||
Restart jitsi-videobridge and start a new conference (making sure that the page
|
||||
is reloaded with the new config.js) -- the organizer of the conference should
|
||||
now have a "recording" button in the floating menu, near the "mute" button.
|
||||
|
||||
@@ -43,8 +43,8 @@ work properly with the native plugins we require.
|
||||
|
||||
Using Xcode
|
||||
|
||||
- Open **ios/jitsi-meet.xcworkspace** in Xcode. Make sure it's the workspace
|
||||
file!
|
||||
- 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.
|
||||
|
||||
|
||||
@@ -30,17 +30,6 @@ During the installation, you will be asked to enter the hostname of the Jitsi Me
|
||||
|
||||
This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also, you and your correspondents will be using it to access the web conferences.
|
||||
|
||||
#### Advanced configuration
|
||||
If installation is on a machine behind NAT further configuration of jitsi-videobridge is needed in order for it to be accessible.
|
||||
Provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000).
|
||||
The following extra lines need to be added the file `/etc/jitsi/videobridge/sip-communicator.properties`:
|
||||
```
|
||||
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
|
||||
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
|
||||
```
|
||||
See [the documenation of ice4j](https://github.com/jitsi/ice4j/blob/master/doc/configuration.md)
|
||||
for details.
|
||||
|
||||
### Open a conference
|
||||
|
||||
Launch a web browser (Chrome, Chromium or latest Opera) and enter in the URL bar the hostname (or IP address) you used in the previous step.
|
||||
@@ -74,7 +63,7 @@ Enjoy!
|
||||
## Uninstall
|
||||
|
||||
```sh
|
||||
apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-web jicofo jitsi-videobridge
|
||||
apt-get purge jigasi jitsi-meet jicofo jitsi-videobridge
|
||||
```
|
||||
|
||||
Sometimes the following packages will fail to uninstall properly:
|
||||
|
||||
36
flow-typed/npm/react-i18next_v2.x.x.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// flow-typed signature: 57cf34196930be78935a42e5c8ac3cb6
|
||||
// flow-typed version: ae6284e7b7/react-i18next_v2.x.x/flow_>=v0.36.x_<=v0.39.x
|
||||
|
||||
declare module 'react-i18next' {
|
||||
declare type TFunction = (key?: ?string, data?: ?Object) => string;
|
||||
declare type Locales = string | Array<string>;
|
||||
|
||||
declare type StatelessComponent<P> = (props: P) => ?React$Element<any>;
|
||||
|
||||
declare type Comp<P> = StatelessComponent<P> | Class<React$Component<*, P, *>>;
|
||||
|
||||
declare type Translator<OP, P> = {
|
||||
(component: StatelessComponent<P>): Class<React$Component<void, OP, void>>;
|
||||
<Def, St>(component: Class<React$Component<Def, P, St>>): Class<React$Component<Def, OP, St>>;
|
||||
}
|
||||
|
||||
declare function translate<OP, P>(locales: Locales): Translator<OP, P>;
|
||||
|
||||
declare type NamespacesProps = {
|
||||
components: Array<Comp<*>>,
|
||||
i18n: { loadNamespaces: Function },
|
||||
};
|
||||
|
||||
declare function loadNamespaces(props: NamespacesProps): Promise<void>;
|
||||
|
||||
declare type ProviderProps = { i18n: Object, children: React$Element<any> };
|
||||
|
||||
declare var I18nextProvider: Class<React$Component<void, ProviderProps, void>>;
|
||||
|
||||
declare type InterpolateProps = {
|
||||
children?: React$Element<any>,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
declare var Interpolate: Class<React$Component<void, InterpolateProps, void>>;
|
||||
}
|
||||
8
flow-typed/npm/redux_v3.x.x.js
vendored
@@ -1,5 +1,5 @@
|
||||
// flow-typed signature: 7f1a115f75043c44385071ea3f33c586
|
||||
// flow-typed version: 358375125e/redux_v3.x.x/flow_>=v0.33.x
|
||||
// flow-typed signature: ba132c96664f1a05288f3eb2272a3c35
|
||||
// flow-typed version: c4bbd91cfc/redux_v3.x.x/flow_>=v0.33.x
|
||||
|
||||
declare module 'redux' {
|
||||
|
||||
@@ -27,8 +27,6 @@ declare module 'redux' {
|
||||
|
||||
declare type Reducer<S, A> = (state: S, action: A) => S;
|
||||
|
||||
declare type CombinedReducer<S, A> = (state: $Shape<S> & {} | void, action: A) => S;
|
||||
|
||||
declare type Middleware<S, A> =
|
||||
(api: MiddlewareAPI<S, A>) =>
|
||||
(next: Dispatch<A>) => Dispatch<A>;
|
||||
@@ -51,7 +49,7 @@ declare module 'redux' {
|
||||
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): CombinedReducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
|
||||
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
Executable file → Normal file
5
fonts/jitsi.svg
Executable file → Normal file
@@ -7,7 +7,6 @@
|
||||
<font-face units-per-em="1024" ascent="1024" descent="0" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " d="" />
|
||||
<glyph unicode="" glyph-name="phone" d="M282 564c62-120 162-220 282-282l94 94c12 12 30 16 44 10 48-16 100-24 152-24 24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44z" />
|
||||
<glyph unicode="" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
|
||||
<glyph unicode="" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
|
||||
<glyph unicode="" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
|
||||
@@ -21,6 +20,7 @@
|
||||
<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" />
|
||||
<glyph unicode="" glyph-name="edit" d="M884 724l-78-78-160 160 78 78c16 16 44 16 60 0l100-100c16-16 16-44 0-60zM128 288l472 472 160-160-472-472h-160v160z" />
|
||||
<glyph unicode="" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
|
||||
<glyph unicode="" glyph-name="telephone" d="M854 362c24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44l-94-94c62-122 162-220 282-282l94 94c12 12 30 14 44 10 48-16 98-24 152-24zM854 810v44h-44v-44h44zM768 896h128v-128h-86v-86h-42v214zM640 810v-128h-128v44h86v42h-86v128h128v-42h-86v-44h86zM726 896v-214h-44v214h44z" />
|
||||
<glyph unicode="" glyph-name="star-full" d="M512 288l-264-160 70 300-232 202 306 26 120 282 120-282 306-26-232-202 70-300z" />
|
||||
<glyph unicode="" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
|
||||
<glyph unicode="" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />
|
||||
@@ -45,7 +45,4 @@
|
||||
<glyph unicode="" glyph-name="menu-up" d="M512 682l256-256-60-60-196 196-196-196-60 60z" />
|
||||
<glyph unicode="" glyph-name="menu-down" d="M708 658l60-60-256-256-256 256 60 60 196-196z" />
|
||||
<glyph unicode="" glyph-name="switch-camera" d="M640 362l150 150-150 150v-108h-256v108l-150-150 150-150v108h256v-108zM854 854c46 0 84-40 84-86v-512c0-46-38-86-84-86h-684c-46 0-84 40-84 86v512c0 46 38 86 84 86h136l78 84h256l78-84h136z" />
|
||||
<glyph unicode="" glyph-name="visibility" d="M512 640c70 0 128-58 128-128s-58-128-128-128-128 58-128 128 58 128 128 128zM512 298c118 0 214 96 214 214s-96 214-214 214-214-96-214-214 96-214 214-214zM512 832c214 0 396-132 470-320-74-188-256-320-470-320s-396 132-470 320c74 188 256 320 470 320z" />
|
||||
<glyph unicode="" glyph-name="visibility-off" d="M506 640h6c70 0 128-58 128-128v-8zM322 606c-14-28-24-60-24-94 0-118 96-214 214-214 34 0 66 10 94 24l-66 66c-8-2-18-4-28-4-70 0-128 58-128 128 0 10 2 20 4 28zM86 842l54 54 756-756-54-54c-47.968 47.365-96.266 94.401-144 142-58-24-120-36-186-36-214 0-396 132-470 320 34 84 90 156 160 212-39.017 38.983-77.307 78.693-116 118zM512 726c-28 0-54-6-78-16l-92 92c52 20 110 30 170 30 214 0 394-132 468-320-32-80-82-148-146-202l-124 124c10 24 16 50 16 78 0 118-96 214-214 214z" />
|
||||
<glyph unicode="" glyph-name="dialpad" d="M512 982c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 726c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM768 726c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM768 470c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 470c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM768 810c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86zM256 470c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM256 726c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM256 982c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 214c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86z" />
|
||||
</font></defs></svg>
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 15 KiB |
BIN
fonts/jitsi.ttf
Executable file → Normal file
BIN
fonts/jitsi.woff
Executable file → Normal file
494
fonts/selection.json
Executable file → Normal file
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
|
||||
<g stroke-width="1pt">
|
||||
<path fill="#006" d="M0 0h640v480H0z"/>
|
||||
<path d="M0 0v27.95L307.037 250h38.647v-27.95L38.647 0H0zm345.684 0v27.95L38.647 250H0v-27.95L307.037 0h38.647z" fill="#fff"/>
|
||||
<path d="M144.035 0v250h57.614V0h-57.615zM0 83.333v83.333h345.684V83.333H0z" fill="#fff"/>
|
||||
<path d="M0 100v50h345.684v-50H0zM155.558 0v250h34.568V0h-34.568zM0 250l115.228-83.334h25.765L25.765 250H0zM0 0l115.228 83.333H89.463L0 18.633V0zm204.69 83.333L319.92 0h25.764L230.456 83.333H204.69zM345.685 250l-115.228-83.334h25.765l89.464 64.7V250z" fill="#c00"/>
|
||||
<path d="M299.762 392.523l-43.653 3.795 6.013 43.406-30.187-31.764-30.186 31.764 6.014-43.406-43.653-3.795 37.68-22.364-24.244-36.495 40.97 15.514 13.42-41.713 13.42 41.712 40.97-15.515-24.242 36.494m224.444 62.372l-10.537-15.854 17.81 6.742 5.824-18.125 5.825 18.126 17.807-6.742-10.537 15.854 16.37 9.718-18.965 1.65 2.616 18.85-13.116-13.793-13.117 13.794 2.616-18.85-18.964-1.65m16.368-291.815l-10.537-15.856 17.81 6.742 5.824-18.122 5.825 18.12 17.807-6.74-10.537 15.855 16.37 9.717-18.965 1.65 2.616 18.85-13.116-13.793-13.117 13.794 2.616-18.85-18.964-1.65m-89.418 104.883l-10.537-15.853 17.808 6.742 5.825-18.125 5.825 18.125 17.808-6.742-10.536 15.853 16.37 9.72-18.965 1.65 2.615 18.85-13.117-13.795-13.117 13.795 2.617-18.85-18.964-1.65m216.212-37.929l-10.558-15.854 17.822 6.742 5.782-18.125 5.854 18.125 17.772-6.742-10.508 15.854 16.362 9.718-18.97 1.65 2.608 18.85-13.118-13.793-13.117 13.793 2.61-18.85-18.936-1.65m-22.251 73.394l-10.367 6.425 2.914-11.84-9.316-7.863 12.165-.896 4.605-11.29 4.606 11.29 12.165.897-9.317 7.863 2.912 11.84" fill-rule="evenodd" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
|
||||
<g transform="translate(74.118) scale(.9375)">
|
||||
<path fill="#fff" d="M81.137 0h362.276v512H81.137z"/>
|
||||
<path fill="#bf0a30" d="M-100 0H81.138v512H-100zm543.413 0H624.55v512H443.414zM135.31 247.41l-14.067 4.808 65.456 57.446c4.95 14.764-1.72 19.116-5.97 26.86l71.06-9.02-1.85 71.512 14.718-.423-3.21-70.918 71.13 8.432c-4.402-9.297-8.32-14.233-4.247-29.098l65.414-54.426-11.447-4.144c-9.36-7.222 4.044-34.784 6.066-52.178 0 0-38.195 13.135-40.698 6.262l-9.727-18.685-34.747 38.17c-3.796.91-5.413-.6-6.304-3.808l16.053-79.766-25.42 14.297c-2.128.91-4.256.125-5.658-2.355l-24.45-49.06-25.21 50.95c-1.9 1.826-3.803 2.037-5.382.796l-24.204-13.578 14.53 79.143c-1.156 3.14-3.924 4.025-7.18 2.324l-33.216-37.737c-4.345 6.962-7.29 18.336-13.033 20.885-5.744 2.387-24.98-4.823-37.873-7.637 4.404 15.895 18.176 42.302 9.46 50.957z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 934 B |
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
|
||||
<path fill="#ffce00" d="M0 320h640v160.002H0z"/>
|
||||
<path d="M0 0h640v160H0z"/>
|
||||
<path fill="#d00" d="M0 160h640v160H0z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 220 B |
@@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
|
||||
<g fill-rule="evenodd" stroke-width="1pt">
|
||||
<path fill="#fff" d="M0 0h640v480H0z"/>
|
||||
<path fill="#00267f" d="M0 0h213.337v480H0z"/>
|
||||
<path fill="#f31830" d="M426.662 0H640v480H426.662z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 301 B |
@@ -1,15 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill-opacity=".67" d="M-85.333 0h682.67v512h-682.67z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#a)" transform="translate(80) scale(.94)">
|
||||
<g stroke-width="1pt">
|
||||
<path fill="#006" d="M-256 0H768.02v512.01H-256z"/>
|
||||
<path d="M-256 0v57.244l909.535 454.768H768.02V454.77L-141.515 0H-256zM768.02 0v57.243L-141.515 512.01H-256v-57.243L653.535 0H768.02z" fill="#fff"/>
|
||||
<path d="M170.675 0v512.01h170.67V0h-170.67zM-256 170.67v170.67H768.02V170.67H-256z" fill="#fff"/>
|
||||
<path d="M-256 204.804v102.402H768.02V204.804H-256zM204.81 0v512.01h102.4V0h-102.4zM-256 512.01L85.34 341.34h76.324l-341.34 170.67H-256zM-256 0L85.34 170.67H9.016L-256 38.164V0zm606.356 170.67L691.696 0h76.324L426.68 170.67h-76.324zM768.02 512.01L426.68 341.34h76.324L768.02 473.848v38.162z" fill="#c00"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 956 B |
@@ -1,18 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
|
||||
<g fill-rule="evenodd" transform="scale(.9375)">
|
||||
<g stroke-width="1pt">
|
||||
<path d="M0 0h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0z" fill="#bd3d44"/>
|
||||
<path d="M0 39.385h972.81V78.77H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0z" fill="#fff"/>
|
||||
</g>
|
||||
<path fill="#192f5d" d="M0 0h389.12v275.69H0z"/>
|
||||
<g fill="#fff">
|
||||
<path d="M32.427 11.8l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 39.37l3.54 10.896h11.458L70.583 57l3.542 10.897-9.27-6.734-9.269 6.734L59.126 57l-9.269-6.734h11.458zm64.852 0l3.54 10.896h11.457L135.435 57l3.54 10.897-9.268-6.734-9.27 6.734L123.978 57l-9.27-6.734h11.458zm64.855 0l3.54 10.896h11.458L200.29 57l3.541 10.897-9.27-6.734-9.268 6.734L188.833 57l-9.269-6.734h11.457zm64.855 0l3.54 10.896h11.458L265.145 57l3.541 10.897-9.269-6.734-9.27 6.734L253.69 57l-9.27-6.734h11.458zm64.852 0l3.54 10.896h11.457L329.997 57l3.54 10.897-9.268-6.734-9.27 6.734L318.54 57l-9.27-6.734h11.458zM32.427 66.939l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 94.508l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zM32.427 122.078l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 149.647l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458z"/>
|
||||
<g>
|
||||
<path d="M32.427 177.217l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 204.786l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M32.427 232.356l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -127,8 +127,9 @@
|
||||
'error', loadErrHandler, true /* capture phase type of listener */);
|
||||
</script>
|
||||
<script><!--#include virtual="/config.js" --></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="static/utils.js?v=1"></script>
|
||||
<!--#include virtual="connection_optimization/connection_optimization.html" -->
|
||||
<script src="libs/do_external_connect.min.js?v=1"></script>
|
||||
<script src="connection_optimization/do_external_connect.js?v=1"></script>
|
||||
<script><!--#include virtual="/interface_config.js" --></script>
|
||||
<script><!--#include virtual="/logging_config.js" --></script>
|
||||
<script src="libs/lib-jitsi-meet.min.js?v=139"></script>
|
||||
|
||||
@@ -38,7 +38,7 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||
//main toolbar
|
||||
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'hangup',
|
||||
//extended toolbar
|
||||
'profile', 'contacts', 'chat', 'recording', 'etherpad', 'sharedvideo', 'dialout', 'settings', 'raisehand', 'filmstrip'], // jshint ignore:line
|
||||
'profile', 'contacts', 'chat', 'recording', 'etherpad', 'sharedvideo', 'sip', 'dialpad', 'settings', 'raisehand', 'filmstrip'], // jshint ignore:line
|
||||
/**
|
||||
* Main Toolbar Buttons
|
||||
* All of them should be in TOOLBAR_BUTTONS
|
||||
@@ -55,12 +55,6 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||
* Whether to only show the filmstrip (and hide the toolbar).
|
||||
*/
|
||||
filmStripOnly: false,
|
||||
|
||||
/**
|
||||
* Whether to show thumbnails in filmstrip as a column instead of as a row.
|
||||
*/
|
||||
VERTICAL_FILMSTRIP: true,
|
||||
|
||||
//A html text to be shown to guests on the close page, false disables it
|
||||
CLOSE_PAGE_GUEST_HINT: false,
|
||||
RANDOM_AVATAR_URL_PREFIX: false,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4</string>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -78,8 +78,6 @@
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleLightContent</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface Proximity : NSObject<RCTBridgeModule>
|
||||
@end
|
||||
|
||||
@implementation Proximity
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
/**
|
||||
* Enables / disables the proximity sensor monitoring. On iOS enabling the
|
||||
* proximity sensor automatically dims the screen and disables touch controls,
|
||||
* so there is nothing else to do (unlike on Android)!
|
||||
*
|
||||
* @param enabled {@code YES} to enable proximity (sensor) monitoring;
|
||||
* {@code NO}, otherwise.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(setEnabled:(BOOL)enabled) {
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:enabled];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -14,7 +14,6 @@
|
||||
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
|
||||
0B42DFAE1E2FD90700111B12 /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B42DFAD1E2FD90700111B12 /* AudioMode.m */; };
|
||||
0B96CAF11E8CF0E8005F348C /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B96CAF01E8CF0E8005F348C /* Proximity.m */; };
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
|
||||
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
|
||||
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
|
||||
@@ -81,13 +80,6 @@
|
||||
remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
|
||||
remoteInfo = RCTVibration;
|
||||
};
|
||||
0B50D60B1EA7891100B34818 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5B09C20C78C74A548AAAC1FA /* KCKeepAwake.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 325F5AB71E6763EC00E6CDE4;
|
||||
remoteInfo = "KCKeepAwake-tvOS";
|
||||
};
|
||||
0B8752851E26E54A004C5CAB /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5B09C20C78C74A548AAAC1FA /* KCKeepAwake.xcodeproj */;
|
||||
@@ -281,11 +273,10 @@
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
|
||||
0965153BB98645B4A8B6AA10 /* RNBackgroundTimer.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNBackgroundTimer.xcodeproj; path = "../node_modules/react-native-background-timer/ios/RNBackgroundTimer.xcodeproj"; sourceTree = "<group>"; };
|
||||
0B42DFAD1E2FD90700111B12 /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AudioMode.m; path = app/AudioMode.m; sourceTree = "<group>"; };
|
||||
0B96CAF01E8CF0E8005F348C /* Proximity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Proximity.m; path = app/Proximity.m; sourceTree = "<group>"; };
|
||||
0EA8C046B2BF46279796F07D /* libKCKeepAwake.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libKCKeepAwake.a; sourceTree = "<group>"; };
|
||||
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
|
||||
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07F961A680F5B00A75B9A /* jitsi-meet-react.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet-react.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = app/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = app/AppDelegate.m; sourceTree = "<group>"; };
|
||||
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
@@ -303,7 +294,7 @@
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
|
||||
B30EF2301DC0ED7C00690F45 /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
|
||||
B3A9D0241E0481E10009343D /* POSIX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = POSIX.m; path = app/POSIX.m; sourceTree = "<group>"; };
|
||||
B3B083EB1D4955FF0069CEE7 /* jitsi-meet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "jitsi-meet.entitlements"; sourceTree = "<group>"; };
|
||||
B3B083EB1D4955FF0069CEE7 /* jitsi-meet-react.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "jitsi-meet-react.entitlements"; sourceTree = "<group>"; };
|
||||
B96AF9B6FBC0453798399985 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; };
|
||||
BF9643811C34FBB300B0BBDF /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
BF9643831C34FBBB00B0BBDF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
@@ -401,7 +392,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0B8752861E26E54A004C5CAB /* libKCKeepAwake.a */,
|
||||
0B50D60C1EA7891100B34818 /* libKCKeepAwake-tvOS.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -436,7 +426,6 @@
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
B3A9D0241E0481E10009343D /* POSIX.m */,
|
||||
0B96CAF01E8CF0E8005F348C /* Proximity.m */,
|
||||
);
|
||||
name = app;
|
||||
sourceTree = "<group>";
|
||||
@@ -530,7 +519,7 @@
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* app */,
|
||||
B3BA19B71DC6B02F00BCD481 /* Frameworks */,
|
||||
B3B083EB1D4955FF0069CEE7 /* jitsi-meet.entitlements */,
|
||||
B3B083EB1D4955FF0069CEE7 /* jitsi-meet-react.entitlements */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
6956B374CC3C453DB7B8E82D /* Resources */,
|
||||
@@ -542,7 +531,7 @@
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
|
||||
13B07F961A680F5B00A75B9A /* jitsi-meet-react.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -581,9 +570,9 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
13B07F861A680F5B00A75B9A /* jitsi-meet */ = {
|
||||
13B07F861A680F5B00A75B9A /* jitsi-meet-react */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet" */;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet-react" */;
|
||||
buildPhases = (
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
@@ -596,9 +585,9 @@
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "jitsi-meet";
|
||||
name = "jitsi-meet-react";
|
||||
productName = "Hello World";
|
||||
productReference = 13B07F961A680F5B00A75B9A /* jitsi-meet.app */;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* jitsi-meet-react.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
@@ -620,7 +609,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "jitsi-meet" */;
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "jitsi-meet-react" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
@@ -695,7 +684,7 @@
|
||||
);
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* jitsi-meet */,
|
||||
13B07F861A680F5B00A75B9A /* jitsi-meet-react */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -736,13 +725,6 @@
|
||||
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
0B50D60C1EA7891100B34818 /* libKCKeepAwake-tvOS.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = "libKCKeepAwake-tvOS.a";
|
||||
remoteRef = 0B50D60B1EA7891100B34818 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
0B8752861E26E54A004C5CAB /* libKCKeepAwake.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
@@ -963,7 +945,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B3A9D0251E0481E10009343D /* POSIX.m in Sources */,
|
||||
0B96CAF11E8CF0E8005F348C /* Proximity.m in Sources */,
|
||||
0B42DFAE1E2FD90700111B12 /* AudioMode.m in Sources */,
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
@@ -989,7 +970,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "jitsi-meet.entitlements";
|
||||
CODE_SIGN_ENTITLEMENTS = "jitsi-meet-react.entitlements";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -1019,7 +1000,7 @@
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeet.ios;
|
||||
PRODUCT_NAME = "jitsi-meet";
|
||||
PRODUCT_NAME = "jitsi-meet-react";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1028,7 +1009,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "jitsi-meet.entitlements";
|
||||
CODE_SIGN_ENTITLEMENTS = "jitsi-meet-react.entitlements";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -1057,7 +1038,7 @@
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.JitsiMeet.ios;
|
||||
PRODUCT_NAME = "jitsi-meet";
|
||||
PRODUCT_NAME = "jitsi-meet-react";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
@@ -1164,7 +1145,7 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet" */ = {
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet-react" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
13B07F941A680F5B00A75B9A /* Debug */,
|
||||
@@ -1173,7 +1154,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "jitsi-meet" */ = {
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "jitsi-meet-react" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||
@@ -29,9 +29,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "jitsi-meet"
|
||||
ReferencedContainer = "container:jitsi-meet.xcodeproj">
|
||||
BuildableName = "jitsi-meet-react.app"
|
||||
BlueprintName = "jitsi-meet-react"
|
||||
ReferencedContainer = "container:jitsi-meet-react.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@@ -47,9 +47,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "jitsi-meet"
|
||||
ReferencedContainer = "container:jitsi-meet.xcodeproj">
|
||||
BuildableName = "jitsi-meet-react.app"
|
||||
BlueprintName = "jitsi-meet-react"
|
||||
ReferencedContainer = "container:jitsi-meet-react.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
@@ -70,9 +70,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "jitsi-meet"
|
||||
ReferencedContainer = "container:jitsi-meet.xcodeproj">
|
||||
BuildableName = "jitsi-meet-react.app"
|
||||
BlueprintName = "jitsi-meet-react"
|
||||
ReferencedContainer = "container:jitsi-meet-react.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
@@ -89,9 +89,9 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "jitsi-meet.app"
|
||||
BlueprintName = "jitsi-meet"
|
||||
ReferencedContainer = "container:jitsi-meet.xcodeproj">
|
||||
BuildableName = "jitsi-meet-react.app"
|
||||
BlueprintName = "jitsi-meet-react"
|
||||
ReferencedContainer = "container:jitsi-meet-react.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:jitsi-meet.xcodeproj">
|
||||
location = "group:jitsi-meet-react.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
}
|
||||
@@ -1,415 +0,0 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"addParticipants": "",
|
||||
"roomLocked": "",
|
||||
"roomUnlocked": "",
|
||||
"passwordSetRemotely": "",
|
||||
"connectionsettings": "",
|
||||
"poweredby": "",
|
||||
"feedback": "",
|
||||
"inviteUrlDefaultMsg": "",
|
||||
"me": "",
|
||||
"speaker": "",
|
||||
"raisedHand": "",
|
||||
"defaultNickname": "",
|
||||
"defaultLink": "",
|
||||
"callingName": "",
|
||||
"audioOnly": {
|
||||
"audioOnly": "",
|
||||
"featureToggleDisabled": "",
|
||||
"howToDisable": ""
|
||||
},
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "",
|
||||
"chromeGrantPermissions": "",
|
||||
"androidGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "",
|
||||
"operaGrantPermissions": "",
|
||||
"iexplorerGrantPermissions": "",
|
||||
"safariGrantPermissions": "",
|
||||
"nwjsGrantPermissions": "",
|
||||
"edgeGrantPermissions": ""
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"keyboardShortcuts": "",
|
||||
"raiseHand": "",
|
||||
"pushToTalk": "",
|
||||
"toggleScreensharing": "",
|
||||
"toggleFilmstrip": "",
|
||||
"toggleShortcuts": "",
|
||||
"focusLocal": "",
|
||||
"focusRemote": "",
|
||||
"toggleChat": "",
|
||||
"mute": "",
|
||||
"fullScreen": "",
|
||||
"videoMute": "",
|
||||
"showSpeakerStats": ""
|
||||
},
|
||||
"welcomepage": {
|
||||
"disable": "",
|
||||
"feature1": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature2": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature3": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature4": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature5": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature6": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature7": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"feature8": {
|
||||
"content": "",
|
||||
"title": ""
|
||||
},
|
||||
"go": "",
|
||||
"join": "",
|
||||
"privacy": "",
|
||||
"roomname": "",
|
||||
"roomnamePlaceHolder": "",
|
||||
"sendFeedback": "",
|
||||
"terms": ""
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
"title": ""
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"title": "",
|
||||
"text": "",
|
||||
"rejoinKeyTitle": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"audioonly": "",
|
||||
"mute": "",
|
||||
"videomute": "",
|
||||
"authenticate": "",
|
||||
"lock": "",
|
||||
"invite": "",
|
||||
"chat": "",
|
||||
"etherpad": "",
|
||||
"sharedvideo": "",
|
||||
"sharescreen": "",
|
||||
"fullscreen": "",
|
||||
"sip": "",
|
||||
"Settings": "",
|
||||
"hangup": "",
|
||||
"login": "",
|
||||
"logout": "",
|
||||
"dialpad": "",
|
||||
"sharedVideoMutedPopup": "",
|
||||
"micMutedPopup": "",
|
||||
"talkWhileMutedPopup": "",
|
||||
"unableToUnmutePopup": "",
|
||||
"cameraDisabled": "",
|
||||
"micDisabled": "",
|
||||
"filmstrip": "",
|
||||
"profile": "",
|
||||
"raiseHand": ""
|
||||
},
|
||||
"unsupportedBrowser": {
|
||||
"appInstalled": "",
|
||||
"appNotInstalled": "",
|
||||
"downloadApp": "",
|
||||
"joinConversation": "",
|
||||
"startConference": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "",
|
||||
"filmstrip": "",
|
||||
"contactlist": ""
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "",
|
||||
"popover": ""
|
||||
},
|
||||
"messagebox": ""
|
||||
},
|
||||
"settings": {
|
||||
"title": "",
|
||||
"update": "",
|
||||
"name": "",
|
||||
"startAudioMuted": "",
|
||||
"startVideoMuted": "",
|
||||
"selectCamera": "",
|
||||
"selectMic": "",
|
||||
"selectAudioOutput": "",
|
||||
"followMe": "",
|
||||
"noDevice": "",
|
||||
"cameraAndMic": "",
|
||||
"moderator": "",
|
||||
"password": "",
|
||||
"audioVideo": ""
|
||||
},
|
||||
"profile": {
|
||||
"title": "",
|
||||
"setDisplayNameLabel": "",
|
||||
"setEmailLabel": "",
|
||||
"setEmailInput": ""
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "",
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"kick": "",
|
||||
"muted": "",
|
||||
"domute": "",
|
||||
"flip": "",
|
||||
"remoteControl": ""
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "",
|
||||
"bitrate": "",
|
||||
"packetloss": "",
|
||||
"resolution": "",
|
||||
"framerate": "",
|
||||
"less": "",
|
||||
"more": "",
|
||||
"address": "",
|
||||
"remoteport": "",
|
||||
"remoteport_plural_undefined": "",
|
||||
"localport": "",
|
||||
"localport_plural_undefined": "",
|
||||
"localaddress": "",
|
||||
"localaddress_plural_undefined": "",
|
||||
"remoteaddress": "",
|
||||
"remoteaddress_plural_undefined": "",
|
||||
"transport": "",
|
||||
"transport_plural_undefined": "",
|
||||
"bandwidth": "",
|
||||
"na": ""
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": "",
|
||||
"muted": "",
|
||||
"mutedTitle": "",
|
||||
"raisedHand": ""
|
||||
},
|
||||
"dialog": {
|
||||
"add": "",
|
||||
"allow": "",
|
||||
"kickMessage": "",
|
||||
"popupError": "",
|
||||
"passwordErrorTitle": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"connectError": "",
|
||||
"connectErrorWithMsg": "",
|
||||
"incorrectPassword": "",
|
||||
"connecting": "",
|
||||
"copy": "",
|
||||
"error": "",
|
||||
"createPassword": "",
|
||||
"detectext": "",
|
||||
"failtoinstall": "",
|
||||
"failedpermissions": "",
|
||||
"conferenceReloadTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"rejoinNow": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupported": "",
|
||||
"internalErrorTitle": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"currentPassword": "",
|
||||
"passwordLabel": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"done": "",
|
||||
"Remove": "",
|
||||
"removePassword": "",
|
||||
"shareVideoTitle": "",
|
||||
"shareVideoLinkError": "",
|
||||
"removeSharedVideoTitle": "",
|
||||
"removeSharedVideoMsg": "",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"WaitingForHost": "",
|
||||
"WaitForHostMsg": "",
|
||||
"IamHost": "",
|
||||
"Cancel": "",
|
||||
"Submit": "",
|
||||
"retry": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"Save": "",
|
||||
"recording": "",
|
||||
"recordingToken": "",
|
||||
"Dial": "",
|
||||
"sipMsg": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"shareLink": "",
|
||||
"settings1": "",
|
||||
"settings2": "",
|
||||
"settings3": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": "",
|
||||
"tokenAuthFailedTitle": "",
|
||||
"tokenAuthFailed": "",
|
||||
"displayNameRequired": "",
|
||||
"enterDisplayName": "",
|
||||
"extensionRequired": "",
|
||||
"firefoxExtensionPrompt": "",
|
||||
"rateExperience": "",
|
||||
"feedbackHelp": "",
|
||||
"feedbackQuestion": "",
|
||||
"thankYou": "",
|
||||
"sorryFeedback": "",
|
||||
"liveStreaming": "",
|
||||
"streamKey": "",
|
||||
"startLiveStreaming": "",
|
||||
"stopStreamingWarning": "",
|
||||
"stopRecordingWarning": "",
|
||||
"stopLiveStreaming": "",
|
||||
"stopRecording": "",
|
||||
"doNotShowWarningAgain": "",
|
||||
"doNotShowMessageAgain": "",
|
||||
"permissionDenied": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"micErrorPresent": "",
|
||||
"cameraErrorPresent": "",
|
||||
"cameraUnsupportedResolutionError": "",
|
||||
"cameraUnknownError": "",
|
||||
"cameraPermissionDeniedError": "",
|
||||
"cameraNotFoundError": "",
|
||||
"cameraConstraintFailedError": "",
|
||||
"micUnknownError": "",
|
||||
"micPermissionDeniedError": "",
|
||||
"micNotFoundError": "",
|
||||
"micConstraintFailedError": "",
|
||||
"micNotSendingData": "",
|
||||
"cameraNotSendingData": "",
|
||||
"goToStore": "",
|
||||
"externalInstallationTitle": "",
|
||||
"externalInstallationMsg": "",
|
||||
"muteParticipantTitle": "",
|
||||
"muteParticipantBody": "",
|
||||
"muteParticipantButton": "",
|
||||
"remoteControlTitle": "",
|
||||
"remoteControlRequestMessage": "",
|
||||
"remoteControlShareScreenWarning": "",
|
||||
"remoteControlDeniedMessage": "",
|
||||
"remoteControlAllowedMessage": "",
|
||||
"remoteControlErrorMessage": "",
|
||||
"remoteControlStopMessage": "",
|
||||
"close": "",
|
||||
"shareYourScreen": "",
|
||||
"yourEntireScreen": "",
|
||||
"applicationWindow": ""
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"and": ""
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "",
|
||||
"CONNECTING": "",
|
||||
"RECONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ATTACHED": ""
|
||||
},
|
||||
"recording": {
|
||||
"pending": "",
|
||||
"on": "",
|
||||
"off": "",
|
||||
"failedToStart": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"unavailable": ""
|
||||
},
|
||||
"liveStreaming": {
|
||||
"pending": "",
|
||||
"on": "",
|
||||
"off": "",
|
||||
"unavailable": "",
|
||||
"failedToStart": "",
|
||||
"buttonTooltip": "",
|
||||
"streamIdRequired": "",
|
||||
"streamIdHelp": "",
|
||||
"error": "",
|
||||
"busy": ""
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"name": "",
|
||||
"seconds": "",
|
||||
"speakerStats": "",
|
||||
"speakerTime": ""
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "",
|
||||
"noPermission": "",
|
||||
"previewUnavailable": "",
|
||||
"selectADevice": "",
|
||||
"testAudio": ""
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "",
|
||||
"dialInNumbers": "",
|
||||
"errorFetchingNumbers": "",
|
||||
"hidePassword": "",
|
||||
"inviteTo": "",
|
||||
"loadingNumbers": "",
|
||||
"locked": "",
|
||||
"noNumbers": "",
|
||||
"numbersDisabled": "",
|
||||
"showPassword": "",
|
||||
"unlocked": ""
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,6 @@
|
||||
"defaultNickname": "ex. Jane Pink",
|
||||
"defaultLink": "e.g. __url__",
|
||||
"callingName": "__name__",
|
||||
"audioOnly": {
|
||||
"audioOnly": "Audio only",
|
||||
"featureToggleDisabled": "Toggling of __feature__ is disabled while in audio only mode"
|
||||
},
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"chromeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
@@ -96,7 +92,6 @@
|
||||
"rejoinKeyTitle": "Rejoin"
|
||||
},
|
||||
"toolbar": {
|
||||
"audioonly": "Enable / Disable audio only mode (saves bandwidth)",
|
||||
"mute": "Mute / Unmute",
|
||||
"videomute": "Start / Stop camera",
|
||||
"authenticate": "Authenticate",
|
||||
@@ -154,10 +149,12 @@
|
||||
"selectAudioOutput": "Audio output",
|
||||
"followMe": "Everyone follows me",
|
||||
"noDevice": "None",
|
||||
"noPermission": "Permission to use device is not granted",
|
||||
"cameraAndMic": "Camera and microphone",
|
||||
"moderator": "MODERATOR",
|
||||
"password": "SET PASSWORD",
|
||||
"audioVideo": "AUDIO AND VIDEO"
|
||||
"audioVideo": "AUDIO AND VIDEO",
|
||||
"setPasswordLabel": "Lock your room with a password."
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profile",
|
||||
@@ -199,7 +196,7 @@
|
||||
"transport_plural": "Transports:",
|
||||
"bandwidth": "Estimated bandwidth:",
|
||||
"na": "Come back here for connection information once the conference starts",
|
||||
"peer_to_peer": " (p2p)"
|
||||
"direct": " (direct)"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "disconnected",
|
||||
@@ -217,7 +214,6 @@
|
||||
},
|
||||
"dialog": {
|
||||
"add": "Add",
|
||||
"allow": "Allow",
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
"popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser's security settings and try again.",
|
||||
"passwordErrorTitle": "Password Error",
|
||||
@@ -229,6 +225,8 @@
|
||||
"connecting": "Connecting",
|
||||
"copy": "Copy",
|
||||
"error": "Error",
|
||||
"roomLocked": "This call is locked. New callers must have the link and enter the password to join",
|
||||
"addPassword": "Add a password",
|
||||
"createPassword": "Create password",
|
||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||
"failtoinstall": "Failed to install desktop sharing extension",
|
||||
@@ -277,6 +275,8 @@
|
||||
"Save": "Save",
|
||||
"recording": "Recording",
|
||||
"recordingToken": "Enter recording token",
|
||||
"Dial": "Dial",
|
||||
"sipMsg": "Enter SIP number",
|
||||
"passwordCheck": "Are you sure you would like to remove your password?",
|
||||
"passwordMsg": "Set a password to lock your room",
|
||||
"shareLink": "Share the link to the call",
|
||||
@@ -321,11 +321,11 @@
|
||||
"cameraUnknownError": "Cannot use camera for a unknown reason.",
|
||||
"cameraPermissionDeniedError": "You have not granted permission to use your camera. You can still join the conference but others won't see you. Use the camera button in the address bar to fix this.",
|
||||
"cameraNotFoundError": "Camera was not found.",
|
||||
"cameraConstraintFailedError": "Your camera does not satisfy some of the required constraints.",
|
||||
"cameraConstraintFailedError": "Yor camera does not satisfy some of required constraints.",
|
||||
"micUnknownError": "Cannot use microphone for a unknown reason.",
|
||||
"micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
|
||||
"micNotFoundError": "Microphone was not found.",
|
||||
"micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
|
||||
"micConstraintFailedError": "Yor microphone does not satisfy some of required constraints.",
|
||||
"micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to restart the application.",
|
||||
"cameraNotSendingData": "We are unable to access your camera. Please check if another application is using this device, select another device from the settings menu or try to restart the application.",
|
||||
"goToStore": "Go to the webstore",
|
||||
@@ -334,9 +334,7 @@
|
||||
"muteParticipantTitle": "Mute this participant?",
|
||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantButton": "Mute",
|
||||
"remoteControlTitle": "Remote desktop control",
|
||||
"remoteControlRequestMessage": "Will you allow __user__ to remotely control your desktop?",
|
||||
"remoteControlShareScreenWarning": "Note that if you press \"Allow\" you will share your screen!",
|
||||
"remoteControlTitle": "Remote Control",
|
||||
"remoteControlDeniedMessage": "__user__ rejected your remote control request!",
|
||||
"remoteControlAllowedMessage": "__user__ accepted your remote control request!",
|
||||
"remoteControlErrorMessage": "An error occurred while trying to request remote control permissions from __user__!",
|
||||
@@ -424,37 +422,5 @@
|
||||
"seconds": "__count__s",
|
||||
"speakerStats": "Speaker Stats",
|
||||
"speakerTime": "Speaker Time"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "Device settings",
|
||||
"noPermission": "Permission not granted",
|
||||
"previewUnavailable": "Preview unavailable",
|
||||
"selectADevice": "Select a device",
|
||||
"testAudio": "Test sound"
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "Add password",
|
||||
"callNumber": "Call __number__",
|
||||
"enterId": "Enter Meeting ID: __meetingId__ following by # to dial in from a phone",
|
||||
"howToDialIn": "To dial in, use one of the following numbers and meeting ID",
|
||||
"hidePassword": "Hide password",
|
||||
"inviteTo": "Invite people to __conferenceName__",
|
||||
"invitedYouTo": "__userName__ has invited you to the __meetingUrl__ conference",
|
||||
"locked": "This call is locked. New callers must have the link and enter the password to join.",
|
||||
"showPassword": "Show password",
|
||||
"unlocked": "This call is unlocked. Any new caller with the link may join the call."
|
||||
},
|
||||
"videoStatus": {
|
||||
"hd": "HD",
|
||||
"hdVideo": "HD video",
|
||||
"sd": "SD",
|
||||
"sdVideo": "SD video"
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "Dial",
|
||||
"dialOut": "Call a phone number",
|
||||
"statusMessage": "is now __status__",
|
||||
"enterPhone": "Enter phone number",
|
||||
"phoneNotAllowed": "Oh, we don't support that destination yet! Sorry!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,5 @@ var loggingConfig = { // eslint-disable-line no-unused-vars
|
||||
// Option to disable LogCollector (which stores the logs on CallStats)
|
||||
//disableLogCollector: true,
|
||||
// Logging level adjustments for verbose modules:
|
||||
'modules/xmpp/strophe.util.js': 'log',
|
||||
'modules/statistics/CallStats.js': 'info'
|
||||
'modules/xmpp/strophe.util.js': 'log'
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
'extends': '../../react/.eslintrc.js'
|
||||
};
|
||||
@@ -1,271 +1,239 @@
|
||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||
import { getJitsiMeetTransport } from '../transport';
|
||||
/* global APP, getConfigParamsFromUrl */
|
||||
|
||||
import { API_ID } from './constants';
|
||||
/**
|
||||
* Implements API class that communicates with external api class
|
||||
* and provides interface to access Jitsi Meet features by external
|
||||
* applications that embed Jitsi Meet
|
||||
*/
|
||||
|
||||
declare var APP: Object;
|
||||
import postisInit from 'postis';
|
||||
|
||||
/**
|
||||
* List of the available commands.
|
||||
* @type {{
|
||||
* displayName: inputDisplayNameHandler,
|
||||
* toggleAudio: toggleAudio,
|
||||
* toggleVideo: toggleVideo,
|
||||
* toggleFilmStrip: toggleFilmStrip,
|
||||
* toggleChat: toggleChat,
|
||||
* toggleContactList: toggleContactList
|
||||
* }}
|
||||
*/
|
||||
let commands = {};
|
||||
|
||||
/**
|
||||
* The state of screen sharing(started/stopped) before the screen sharing is
|
||||
* enabled and initialized.
|
||||
* NOTE: This flag help us to cache the state and use it if toggle-share-screen
|
||||
* was received before the initialization.
|
||||
*/
|
||||
let initialScreenSharingState = false;
|
||||
let hashParams = getConfigParamsFromUrl();
|
||||
|
||||
/**
|
||||
* The transport instance used for communication with external apps.
|
||||
*
|
||||
* @type {Transport}
|
||||
* JitsiMeetExternalAPI id - unique for a webpage.
|
||||
*/
|
||||
const transport = getJitsiMeetTransport();
|
||||
let jitsi_meet_external_api_id = hashParams.jitsi_meet_external_api_id;
|
||||
|
||||
/**
|
||||
* Initializes supported commands.
|
||||
*
|
||||
* @returns {void}
|
||||
* Object that will execute sendMessage
|
||||
*/
|
||||
let target = window.opener ? window.opener : window.parent;
|
||||
|
||||
/**
|
||||
* Postis instance. Used to communicate with the external application.
|
||||
*/
|
||||
let postis;
|
||||
|
||||
/**
|
||||
* Current status (enabled/disabled) of API.
|
||||
*/
|
||||
let enabled = false;
|
||||
|
||||
function initCommands() {
|
||||
commands = {
|
||||
'display-name':
|
||||
APP.conference.changeLocalDisplayName.bind(APP.conference),
|
||||
'toggle-audio': () => APP.conference.toggleAudioMuted(true),
|
||||
'toggle-video': () => APP.conference.toggleVideoMuted(true),
|
||||
'toggle-film-strip': APP.UI.toggleFilmstrip,
|
||||
'toggle-chat': APP.UI.toggleChat,
|
||||
'toggle-contact-list': APP.UI.toggleContactList,
|
||||
'toggle-share-screen': toggleScreenSharing,
|
||||
'video-hangup': () => APP.conference.hangup(),
|
||||
'email': APP.conference.changeLocalEmail,
|
||||
'avatar-url': APP.conference.changeLocalAvatarUrl
|
||||
"display-name": APP.UI.inputDisplayNameHandler,
|
||||
"toggle-audio": APP.conference.toggleAudioMuted.bind(APP.conference),
|
||||
"toggle-video": APP.conference.toggleVideoMuted.bind(APP.conference),
|
||||
"toggle-film-strip": APP.UI.toggleFilmStrip,
|
||||
"toggle-chat": APP.UI.toggleChat,
|
||||
"toggle-contact-list": APP.UI.toggleContactList,
|
||||
"toggle-share-screen":
|
||||
APP.conference.toggleScreenSharing.bind(APP.conference),
|
||||
"video-hangup": () => APP.conference.hangup(),
|
||||
"email": APP.conference.changeLocalEmail,
|
||||
"avatar-url": APP.conference.changeLocalAvatarUrl,
|
||||
"remote-control-event": event =>
|
||||
APP.remoteControl.onRemoteControlAPIEvent(event)
|
||||
};
|
||||
transport.on('event', ({ data, name }) => {
|
||||
if (name && commands[name]) {
|
||||
commands[name](...data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
Object.keys(commands).forEach(function (key) {
|
||||
postis.listen(key, args => commands[key](...args));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for desktop/screen sharing enabled events and toggles the screen
|
||||
* sharing if needed.
|
||||
*
|
||||
* @param {boolean} enabled - Current screen sharing enabled status.
|
||||
* @returns {void}
|
||||
* Sends message to the external application.
|
||||
* @param message {object}
|
||||
* @param method {string}
|
||||
* @param params {object} the object that will be sent as JSON string
|
||||
*/
|
||||
function onDesktopSharingEnabledChanged(enabled = false) {
|
||||
if (enabled && initialScreenSharingState) {
|
||||
toggleScreenSharing();
|
||||
function sendMessage(message) {
|
||||
if(enabled) {
|
||||
postis.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the API should be enabled or not.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function shouldBeEnabled() {
|
||||
return typeof API_ID === 'number';
|
||||
function shouldBeEnabled () {
|
||||
return (typeof jitsi_meet_external_api_id === "number");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes on toggle-share-screen command.
|
||||
*
|
||||
* @returns {void}
|
||||
* Sends event object to the external application that has been subscribed
|
||||
* for that event.
|
||||
* @param name the name event
|
||||
* @param object data associated with the event
|
||||
*/
|
||||
function toggleScreenSharing() {
|
||||
if (APP.conference.isDesktopSharingEnabled) {
|
||||
APP.conference.toggleScreenSharing();
|
||||
} else {
|
||||
initialScreenSharingState = !initialScreenSharingState;
|
||||
function triggerEvent (name, object) {
|
||||
if(enabled) {
|
||||
sendMessage({method: name, params: object});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements API class that communicates with external API class and provides
|
||||
* interface to access Jitsi Meet features by external applications that embed
|
||||
* Jitsi Meet.
|
||||
*/
|
||||
class API {
|
||||
/**
|
||||
* Initializes the API. Setups message event listeners that will receive
|
||||
* information from external applications that embed Jitsi Meet. It also
|
||||
* sends a message to the external application that API is initialized.
|
||||
*
|
||||
* @param {Object} options - Optional parameters.
|
||||
* @param {boolean} options.forceEnable - True to forcefully enable the
|
||||
* module.
|
||||
* @returns {void}
|
||||
* Constructs new instance
|
||||
* @constructor
|
||||
*/
|
||||
init({ forceEnable } = {}) {
|
||||
if (!shouldBeEnabled() && !forceEnable) {
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* Initializes the APIConnector. Setups message event listeners that will
|
||||
* receive information from external applications that embed Jitsi Meet.
|
||||
* It also sends a message to the external application that APIConnector
|
||||
* is initialized.
|
||||
* @param options {object}
|
||||
* @param forceEnable {boolean} if true the module will be enabled.
|
||||
*/
|
||||
init (options = {}) {
|
||||
if(!shouldBeEnabled() && !options.forceEnable)
|
||||
return;
|
||||
|
||||
enabled = true;
|
||||
|
||||
if(!postis) {
|
||||
this._initPostis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Current status (enabled/disabled) of API.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._enabled = true;
|
||||
|
||||
APP.conference.addListener(
|
||||
JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
|
||||
onDesktopSharingEnabledChanged);
|
||||
|
||||
/**
|
||||
* initializes postis library.
|
||||
* @private
|
||||
*/
|
||||
_initPostis() {
|
||||
let postisOptions = {
|
||||
window: target
|
||||
};
|
||||
if(typeof jitsi_meet_external_api_id === "number")
|
||||
postisOptions.scope
|
||||
= "jitsi_meet_external_api_" + jitsi_meet_external_api_id;
|
||||
postis = postisInit(postisOptions);
|
||||
initCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends event to the external application.
|
||||
*
|
||||
* @param {Object} event - The event to be sent.
|
||||
* @returns {void}
|
||||
*/
|
||||
_sendEvent(event = {}) {
|
||||
if (this._enabled) {
|
||||
transport.sendEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that message was sent.
|
||||
*
|
||||
* @param {string} message - Message body.
|
||||
* @returns {void}
|
||||
* @param {string} body message body
|
||||
*/
|
||||
notifySendingChatMessage(message) {
|
||||
this._sendEvent({
|
||||
name: 'outgoing-message',
|
||||
message
|
||||
});
|
||||
notifySendingChatMessage (body) {
|
||||
triggerEvent("outgoing-message", {"message": body});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that message was
|
||||
* received.
|
||||
*
|
||||
* @param {Object} options - Object with the message properties.
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* message was received.
|
||||
* @param {string} id user id
|
||||
* @param {string} nick user nickname
|
||||
* @param {string} body message body
|
||||
* @param {number} ts message creation timestamp
|
||||
*/
|
||||
notifyReceivedChatMessage({ body, id, nick, ts } = {}) {
|
||||
notifyReceivedChatMessage (id, nick, body, ts) {
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendEvent({
|
||||
name: 'incoming-message',
|
||||
from: id,
|
||||
message: body,
|
||||
nick,
|
||||
stamp: ts
|
||||
});
|
||||
triggerEvent(
|
||||
"incoming-message",
|
||||
{"from": id, "nick": nick, "message": body, "stamp": ts}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that user joined the
|
||||
* conference.
|
||||
*
|
||||
* @param {string} id - User id.
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* user joined the conference.
|
||||
* @param {string} id user id
|
||||
*/
|
||||
notifyUserJoined(id) {
|
||||
this._sendEvent({
|
||||
name: 'participant-joined',
|
||||
id
|
||||
});
|
||||
notifyUserJoined (id) {
|
||||
triggerEvent("participant-joined", {id});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that user left the
|
||||
* conference.
|
||||
*
|
||||
* @param {string} id - User id.
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* user left the conference.
|
||||
* @param {string} id user id
|
||||
*/
|
||||
notifyUserLeft(id) {
|
||||
this._sendEvent({
|
||||
name: 'participant-left',
|
||||
id
|
||||
});
|
||||
notifyUserLeft (id) {
|
||||
triggerEvent("participant-left", {id});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that user changed their
|
||||
* nickname.
|
||||
*
|
||||
* @param {string} id - User id.
|
||||
* @param {string} displayname - User nickname.
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* user changed their nickname.
|
||||
* @param {string} id user id
|
||||
* @param {string} displayName user nickname
|
||||
*/
|
||||
notifyDisplayNameChanged(id, displayname) {
|
||||
this._sendEvent({
|
||||
name: 'display-name-change',
|
||||
displayname,
|
||||
id
|
||||
});
|
||||
notifyDisplayNameChanged (id, displayName) {
|
||||
triggerEvent("display-name-change", {id, displayname: displayName});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that the conference has
|
||||
* been joined.
|
||||
*
|
||||
* @param {string} roomName - The room name.
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* user changed their nickname.
|
||||
* @param {string} id user id
|
||||
* @param {string} displayName user nickname
|
||||
*/
|
||||
notifyConferenceJoined(roomName) {
|
||||
this._sendEvent({
|
||||
name: 'video-conference-joined',
|
||||
roomName
|
||||
});
|
||||
notifyConferenceJoined (room) {
|
||||
triggerEvent("video-conference-joined", {roomName: room});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that user changed their
|
||||
* nickname.
|
||||
*
|
||||
* @param {string} roomName - User id.
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* user changed their nickname.
|
||||
* @param {string} id user id
|
||||
* @param {string} displayName user nickname
|
||||
*/
|
||||
notifyConferenceLeft(roomName) {
|
||||
this._sendEvent({
|
||||
name: 'video-conference-left',
|
||||
roomName
|
||||
});
|
||||
notifyConferenceLeft (room) {
|
||||
triggerEvent("video-conference-left", {roomName: room});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that we are ready to be
|
||||
* closed.
|
||||
*
|
||||
* @returns {void}
|
||||
* Notify external application (if API is enabled) that
|
||||
* we are ready to be closed.
|
||||
*/
|
||||
notifyReadyToClose() {
|
||||
this._sendEvent({ name: 'video-ready-to-close' });
|
||||
notifyReadyToClose () {
|
||||
triggerEvent("video-ready-to-close", {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the allocated resources.
|
||||
*
|
||||
* @returns {void}
|
||||
* Sends remote control event.
|
||||
* @param {RemoteControlEvent} event the remote control event.
|
||||
*/
|
||||
dispose() {
|
||||
if (this._enabled) {
|
||||
this._enabled = false;
|
||||
APP.conference.removeListener(
|
||||
JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
|
||||
onDesktopSharingEnabledChanged);
|
||||
}
|
||||
sendRemoteControlEvent(event) {
|
||||
sendMessage({method: "remote-control-event", params: event});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listeners.
|
||||
*/
|
||||
dispose () {
|
||||
if(enabled)
|
||||
postis.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// XXX The function parseURLParams is exported by the feature base/config (as
|
||||
// defined in the terminology of react/). However, this file is (very likely)
|
||||
// bundled in external_api in addition to app.bundle and, consequently, it is
|
||||
// best to import as little as possible here (rather than the whole feature
|
||||
// base/config) in order to minimize the amount of source code bundled into
|
||||
// multiple bundles.
|
||||
import parseURLParams from '../../react/features/base/config/parseURLParams';
|
||||
|
||||
/**
|
||||
* JitsiMeetExternalAPI id - unique for a webpage.
|
||||
*/
|
||||
export const API_ID
|
||||
= parseURLParams(window.location).jitsi_meet_external_api_id;
|
||||
703
modules/API/external/external_api.js
vendored
@@ -1,428 +1,393 @@
|
||||
import EventEmitter from 'events';
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import {
|
||||
PostMessageTransportBackend,
|
||||
Transport
|
||||
} from '../../transport';
|
||||
/**
|
||||
* Implements API class that embeds Jitsi Meet in external applications.
|
||||
*/
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
var postisInit = require("postis");
|
||||
|
||||
/**
|
||||
* The minimum width for the Jitsi Meet frame
|
||||
* @type {number}
|
||||
*/
|
||||
var MIN_WIDTH = 790;
|
||||
|
||||
/**
|
||||
* The minimum height for the Jitsi Meet frame
|
||||
* @type {number}
|
||||
*/
|
||||
var MIN_HEIGHT = 300;
|
||||
|
||||
/**
|
||||
* Last id of api object
|
||||
* @type {number}
|
||||
*/
|
||||
var id = 0;
|
||||
|
||||
/**
|
||||
* Maps the names of the commands expected by the API with the name of the
|
||||
* commands expected by jitsi-meet
|
||||
*/
|
||||
const commands = {
|
||||
avatarUrl: 'avatar-url',
|
||||
displayName: 'display-name',
|
||||
email: 'email',
|
||||
hangup: 'video-hangup',
|
||||
toggleAudio: 'toggle-audio',
|
||||
toggleChat: 'toggle-chat',
|
||||
toggleContactList: 'toggle-contact-list',
|
||||
toggleFilmStrip: 'toggle-film-strip',
|
||||
toggleShareScreen: 'toggle-share-screen',
|
||||
toggleVideo: 'toggle-video'
|
||||
var commands = {
|
||||
"displayName": "display-name",
|
||||
"toggleAudio": "toggle-audio",
|
||||
"toggleVideo": "toggle-video",
|
||||
"toggleFilmStrip": "toggle-film-strip",
|
||||
"toggleChat": "toggle-chat",
|
||||
"toggleContactList": "toggle-contact-list",
|
||||
"toggleShareScreen": "toggle-share-screen",
|
||||
"hangup": "video-hangup",
|
||||
"email": "email",
|
||||
"avatarUrl": "avatar-url"
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the names of the events expected by the API with the name of the
|
||||
* events expected by jitsi-meet
|
||||
*/
|
||||
const events = {
|
||||
'display-name-change': 'displayNameChange',
|
||||
'incoming-message': 'incomingMessage',
|
||||
'outgoing-message': 'outgoingMessage',
|
||||
'participant-joined': 'participantJoined',
|
||||
'participant-left': 'participantLeft',
|
||||
'video-ready-to-close': 'readyToClose',
|
||||
'video-conference-joined': 'videoConferenceJoined',
|
||||
'video-conference-left': 'videoConferenceLeft'
|
||||
var events = {
|
||||
"incomingMessage": "incoming-message",
|
||||
"outgoingMessage": "outgoing-message",
|
||||
"displayNameChange": "display-name-change",
|
||||
"participantJoined": "participant-joined",
|
||||
"participantLeft": "participant-left",
|
||||
"videoConferenceJoined": "video-conference-joined",
|
||||
"videoConferenceLeft": "video-conference-left",
|
||||
"readyToClose": "video-ready-to-close"
|
||||
};
|
||||
|
||||
/**
|
||||
* Last id of api object
|
||||
* @type {number}
|
||||
* Sends the passed object to Jitsi Meet
|
||||
* @param postis {Postis object} the postis instance that is going to be used
|
||||
* to send the message
|
||||
* @param object the object to be sent
|
||||
* - method {sting}
|
||||
* - params {object}
|
||||
*/
|
||||
let id = 0;
|
||||
|
||||
/**
|
||||
* The minimum height for the Jitsi Meet frame
|
||||
* @type {number}
|
||||
*/
|
||||
const MIN_HEIGHT = 300;
|
||||
|
||||
/**
|
||||
* The minimum width for the Jitsi Meet frame
|
||||
* @type {number}
|
||||
*/
|
||||
const MIN_WIDTH = 790;
|
||||
function sendMessage(postis, object) {
|
||||
postis.send(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds given number to the numberOfParticipants property of given APIInstance.
|
||||
*
|
||||
* @param {JitsiMeetExternalAPI} APIInstance - The instance of the API.
|
||||
* @param {int} number - The number of participants to be added to
|
||||
* @param {JitsiMeetExternalAPI} APIInstance the instance of the
|
||||
* JitsiMeetExternalAPI
|
||||
* @param {int} number - the number of participants to be added to
|
||||
* numberOfParticipants property (this parameter can be negative number if the
|
||||
* numberOfParticipants should be decreased).
|
||||
* @returns {void}
|
||||
*/
|
||||
function changeParticipantNumber(APIInstance, number) {
|
||||
APIInstance.numberOfParticipants += number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates array with URL params based on the passed config object that will
|
||||
* be used for the Jitsi Meet URL generation.
|
||||
*
|
||||
* @param {Object} config - The config object.
|
||||
* @returns {Array<string>} The array with URL param strings.
|
||||
* Constructs new API instance. Creates iframe element that loads
|
||||
* Jitsi Meet.
|
||||
* @param domain the domain name of the server that hosts the conference
|
||||
* @param room_name the name of the room to join
|
||||
* @param width width of the iframe
|
||||
* @param height height of the iframe
|
||||
* @param parent_node the node that will contain the iframe
|
||||
* @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
|
||||
* @param {string} [jwt] the JWT token if needed by jitsi-meet for
|
||||
* authentication.
|
||||
* @constructor
|
||||
*/
|
||||
function configToURLParamsArray(config = {}) {
|
||||
const params = [];
|
||||
function JitsiMeetExternalAPI(domain, room_name, width, height, parentNode,
|
||||
configOverwrite, interfaceConfigOverwrite, noSsl, jwt) {
|
||||
if (!width || width < MIN_WIDTH)
|
||||
width = MIN_WIDTH;
|
||||
if (!height || height < MIN_HEIGHT)
|
||||
height = MIN_HEIGHT;
|
||||
|
||||
for (const key in config) { // eslint-disable-line guard-for-in
|
||||
try {
|
||||
params.push(
|
||||
`${key}=${encodeURIComponent(JSON.stringify(config[key]))}`);
|
||||
} catch (e) {
|
||||
console.warn(`Error encoding ${key}: ${e}`);
|
||||
}
|
||||
this.parentNode = null;
|
||||
if (parentNode) {
|
||||
this.parentNode = parentNode;
|
||||
} else {
|
||||
var scriptTag = document.scripts[document.scripts.length - 1];
|
||||
this.parentNode = scriptTag.parentNode;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the URL for the iframe.
|
||||
*
|
||||
* @param {string} domain - The domain name of the server that hosts the
|
||||
* conference.
|
||||
* @param {string} [options] - Another optional parameters.
|
||||
* @param {Object} [options.configOverwrite] - Object containing configuration
|
||||
* options defined in config.js to be overridden.
|
||||
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
|
||||
* configuration options defined in interface_config.js to be overridden.
|
||||
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
|
||||
* authentication.
|
||||
* @param {boolean} [options.noSsl] - If the value is true https won't be used.
|
||||
* @param {string} [options.roomName] - The name of the room to join.
|
||||
* @returns {string} The URL.
|
||||
*/
|
||||
function generateURL(domain, options = {}) {
|
||||
const {
|
||||
configOverwrite,
|
||||
interfaceConfigOverwrite,
|
||||
jwt,
|
||||
noSSL,
|
||||
roomName
|
||||
} = options;
|
||||
|
||||
let url = `${noSSL ? 'http' : 'https'}://${domain}/${roomName || ''}`;
|
||||
this.iframeHolder =
|
||||
this.parentNode.appendChild(document.createElement("div"));
|
||||
this.iframeHolder.id = "jitsiConference" + id;
|
||||
if(width)
|
||||
this.iframeHolder.style.width = width + "px";
|
||||
if(height)
|
||||
this.iframeHolder.style.height = height + "px";
|
||||
this.frameName = "jitsiConferenceFrame" + id;
|
||||
this.url = (noSsl) ? "http" : "https" +"://" + domain + "/";
|
||||
if(room_name)
|
||||
this.url += room_name;
|
||||
|
||||
if (jwt) {
|
||||
url += `?jwt=${jwt}`;
|
||||
this.url += '?jwt=' + jwt;
|
||||
}
|
||||
|
||||
url += `#jitsi_meet_external_api_id=${id}`;
|
||||
this.url += "#jitsi_meet_external_api_id=" + id;
|
||||
|
||||
const configURLParams = configToURLParamsArray(configOverwrite);
|
||||
|
||||
if (configURLParams.length) {
|
||||
url += `&config.${configURLParams.join('&config.')}`;
|
||||
var key;
|
||||
if (configOverwrite) {
|
||||
for (key in configOverwrite) {
|
||||
if (!configOverwrite.hasOwnProperty(key) ||
|
||||
typeof key !== 'string')
|
||||
continue;
|
||||
this.url += "&config." + key + "=" + configOverwrite[key];
|
||||
}
|
||||
}
|
||||
|
||||
const interfaceConfigURLParams
|
||||
= configToURLParamsArray(interfaceConfigOverwrite);
|
||||
|
||||
if (interfaceConfigURLParams.length) {
|
||||
url += `&interfaceConfig.${
|
||||
interfaceConfigURLParams.join('&interfaceConfig.')}`;
|
||||
if (interfaceConfigOverwrite) {
|
||||
for (key in interfaceConfigOverwrite) {
|
||||
if (!interfaceConfigOverwrite.hasOwnProperty(key) ||
|
||||
typeof key !== 'string')
|
||||
continue;
|
||||
this.url += "&interfaceConfig." + key + "=" +
|
||||
interfaceConfigOverwrite[key];
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
this.frame = document.createElement("iframe");
|
||||
this.frame.src = this.url;
|
||||
this.frame.name = this.frameName;
|
||||
this.frame.id = this.frameName;
|
||||
this.frame.width = "100%";
|
||||
this.frame.height = "100%";
|
||||
this.frame.setAttribute("allowFullScreen","true");
|
||||
this.frame = this.iframeHolder.appendChild(this.frame);
|
||||
this.postis = postisInit({
|
||||
window: this.frame.contentWindow,
|
||||
scope: "jitsi_meet_external_api_" + id
|
||||
});
|
||||
|
||||
this.eventHandlers = {};
|
||||
|
||||
// Map<{string} event_name, {boolean} postis_listener_added>
|
||||
this.postisListeners = {};
|
||||
|
||||
this.numberOfParticipants = 1;
|
||||
this._setupListeners();
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IFrame API interface class.
|
||||
* Executes command. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* toggleAudio - mutes / unmutes audio with no arguments
|
||||
* toggleVideo - mutes / unmutes video with no arguments
|
||||
* filmStrip - hides / shows the film strip with no arguments
|
||||
* If the command doesn't require any arguments the parameter should be set
|
||||
* to empty array or it may be omitted.
|
||||
* @param name the name of the command
|
||||
* @param arguments array of arguments
|
||||
*/
|
||||
class JitsiMeetExternalAPI extends EventEmitter {
|
||||
/**
|
||||
* Constructs new API instance. Creates iframe and loads Jitsi Meet in it.
|
||||
*
|
||||
* @param {string} domain - The domain name of the server that hosts the
|
||||
* conference.
|
||||
* @param {string} [roomName] - The name of the room to join.
|
||||
* @param {number} [width] - Width of the iframe.
|
||||
* @param {number} [height] - Height of the iframe.
|
||||
* @param {DOMElement} [parentNode] - The node that will contain the
|
||||
* iframe.
|
||||
* @param {Object} [configOverwrite] - Object containing configuration
|
||||
* options defined in config.js to be overridden.
|
||||
* @param {Object} [interfaceConfigOverwrite] - Object containing
|
||||
* configuration options defined in interface_config.js to be overridden.
|
||||
* @param {boolean} [noSSL] - If the value is true https won't be used.
|
||||
* @param {string} [jwt] - The JWT token if needed by jitsi-meet for
|
||||
* authentication.
|
||||
*/
|
||||
constructor(domain, // eslint-disable-line max-params
|
||||
roomName = '',
|
||||
width = MIN_WIDTH,
|
||||
height = MIN_HEIGHT,
|
||||
parentNode = document.body,
|
||||
configOverwrite = {},
|
||||
interfaceConfigOverwrite = {},
|
||||
noSSL = false,
|
||||
jwt = undefined) {
|
||||
super();
|
||||
this.parentNode = parentNode;
|
||||
this.url = generateURL(domain, {
|
||||
configOverwrite,
|
||||
interfaceConfigOverwrite,
|
||||
jwt,
|
||||
noSSL,
|
||||
roomName
|
||||
});
|
||||
this._createIFrame(Math.max(height, MIN_HEIGHT),
|
||||
Math.max(width, MIN_WIDTH));
|
||||
this._transport = new Transport({
|
||||
backend: new PostMessageTransportBackend({
|
||||
postisOptions: {
|
||||
scope: `jitsi_meet_external_api_${id}`,
|
||||
window: this.frame.contentWindow
|
||||
}
|
||||
})
|
||||
});
|
||||
this.numberOfParticipants = 1;
|
||||
this._setupListeners();
|
||||
id++;
|
||||
JitsiMeetExternalAPI.prototype.executeCommand
|
||||
= function(name, ...argumentsList) {
|
||||
if(!(name in commands)) {
|
||||
logger.error("Not supported command name.");
|
||||
return;
|
||||
}
|
||||
sendMessage(this.postis, {method: commands[name], params: argumentsList});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the iframe element.
|
||||
*
|
||||
* @param {number} height - The height of the iframe.
|
||||
* @param {number} width - The with of the iframe.
|
||||
* @returns {void}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_createIFrame(height, width) {
|
||||
this.iframeHolder
|
||||
= this.parentNode.appendChild(document.createElement('div'));
|
||||
this.iframeHolder.id = `jitsiConference${id}`;
|
||||
this.iframeHolder.style.width = `${width}px`;
|
||||
this.iframeHolder.style.height = `${height}px`;
|
||||
/**
|
||||
* Executes commands. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* toggleAudio - mutes / unmutes audio. no arguments
|
||||
* toggleVideo - mutes / unmutes video. no arguments
|
||||
* filmStrip - hides / shows the film strip. no arguments
|
||||
* toggleChat - hides / shows chat. no arguments.
|
||||
* toggleContactList - hides / shows contact list. no arguments.
|
||||
* toggleShareScreen - starts / stops screen sharing. no arguments.
|
||||
* @param object the object with commands to be executed. The keys of the
|
||||
* object are the commands that will be executed and the values are the
|
||||
* arguments for the command.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.executeCommands = function(object) {
|
||||
for(var key in object)
|
||||
this.executeCommand(key, object[key]);
|
||||
};
|
||||
|
||||
this.frameName = `jitsiConferenceFrame${id}`;
|
||||
/**
|
||||
* Adds event listeners to Meet Jitsi. The object key should be the name of
|
||||
* the event and value - the listener.
|
||||
* Currently we support the following
|
||||
* events:
|
||||
* incomingMessage - receives event notifications about incoming
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* "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 - receives event notifications about outgoing
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* "message": txt//the text of the message
|
||||
* }}
|
||||
* displayNameChanged - receives event notifications about display name
|
||||
* change. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid,//the JID of the participant that changed his display name
|
||||
* displayname: displayName //the new display name
|
||||
* }}
|
||||
* participantJoined - receives event notifications about new participant.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* participantLeft - receives event notifications about the participant that
|
||||
* left the room.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* video-conference-joined - receives event notifications about the local user
|
||||
* has successfully joined the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* video-conference-left - receives event notifications about the local user
|
||||
* has left the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* readyToClose - all hangup operations are completed and Jitsi Meet is ready
|
||||
* to be disposed.
|
||||
* @param object
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.addEventListeners = function(object) {
|
||||
for(var i in object)
|
||||
this.addEventListener(i, object[i]);
|
||||
};
|
||||
|
||||
this.frame = document.createElement('iframe');
|
||||
this.frame.src = this.url;
|
||||
this.frame.name = this.frameName;
|
||||
this.frame.id = this.frameName;
|
||||
this.frame.width = '100%';
|
||||
this.frame.height = '100%';
|
||||
this.frame.setAttribute('allowFullScreen', 'true');
|
||||
this.frame = this.iframeHolder.appendChild(this.frame);
|
||||
/**
|
||||
* Adds event listeners to Meet Jitsi. Currently we support the following
|
||||
* events:
|
||||
* incomingMessage - receives event notifications about incoming
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* "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 - receives event notifications about outgoing
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* "message": txt//the text of the message
|
||||
* }}
|
||||
* displayNameChanged - receives event notifications about display name
|
||||
* change. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid,//the JID of the participant that changed his display name
|
||||
* displayname: displayName //the new display name
|
||||
* }}
|
||||
* participantJoined - receives event notifications about new participant.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* participantLeft - receives event notifications about participant the that
|
||||
* left the room.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* video-conference-joined - receives event notifications fired when the local
|
||||
* user has joined the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* video-conference-left - receives event notifications fired when the local
|
||||
* user has joined the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* @param event the name of the event
|
||||
* @param listener the listener
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.addEventListener = function(event, listener) {
|
||||
if(!(event in events)) {
|
||||
logger.error("Not supported event name.");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups listeners that are used internally for JitsiMeetExternalAPI.
|
||||
*
|
||||
* @returns {void}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_setupListeners() {
|
||||
|
||||
this._transport.on('event', ({ name, ...data }) => {
|
||||
if (name === 'participant-joined') {
|
||||
changeParticipantNumber(this, 1);
|
||||
} else if (name === 'participant-left') {
|
||||
changeParticipantNumber(this, -1);
|
||||
}
|
||||
|
||||
const eventName = events[name];
|
||||
|
||||
if (eventName) {
|
||||
this.emit(eventName, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
// We cannot remove listeners from postis that's why we are handling the
|
||||
// callback that way.
|
||||
if(!this.postisListeners[event]) {
|
||||
this.postis.listen(events[event], function(data) {
|
||||
if((event in this.eventHandlers) &&
|
||||
typeof this.eventHandlers[event] === "function")
|
||||
this.eventHandlers[event].call(null, data);
|
||||
}.bind(this));
|
||||
this.postisListeners[event] = true;
|
||||
}
|
||||
this.eventHandlers[event] = listener;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds event listener to Meet Jitsi.
|
||||
*
|
||||
* @param {string} event - The name of the event.
|
||||
* @param {Function} listener - The listener.
|
||||
* @returns {void}
|
||||
*
|
||||
* @deprecated
|
||||
* NOTE: This method is not removed for backward comatability purposes.
|
||||
*/
|
||||
addEventListener(event, listener) {
|
||||
this.on(event, listener);
|
||||
/**
|
||||
* Removes event listener.
|
||||
* @param event the name of the event.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.removeEventListener = function(event) {
|
||||
if(!(event in this.eventHandlers))
|
||||
{
|
||||
logger.error("The event " + event + " is not registered.");
|
||||
return;
|
||||
}
|
||||
delete this.eventHandlers[event];
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds event listeners to Meet Jitsi.
|
||||
*
|
||||
* @param {Object} listeners - The object key should be the name of
|
||||
* the event and value - the listener.
|
||||
* Currently we support the following
|
||||
* events:
|
||||
* incomingMessage - receives event notifications about incoming
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* '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 - receives event notifications about outgoing
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* 'message': txt//the text of the message
|
||||
* }}
|
||||
* displayNameChanged - receives event notifications about display name
|
||||
* change. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid,//the JID of the participant that changed his display name
|
||||
* displayname: displayName //the new display name
|
||||
* }}
|
||||
* participantJoined - receives event notifications about new participant.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* participantLeft - receives event notifications about the participant that
|
||||
* left the room.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* video-conference-joined - receives event notifications about the local
|
||||
* user has successfully joined the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* video-conference-left - receives event notifications about the local user
|
||||
* has left the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* readyToClose - all hangup operations are completed and Jitsi Meet is
|
||||
* ready to be disposed.
|
||||
* @returns {void}
|
||||
*
|
||||
* @deprecated
|
||||
* NOTE: This method is not removed for backward comatability purposes.
|
||||
*/
|
||||
addEventListeners(listeners) {
|
||||
for (const event in listeners) { // eslint-disable-line guard-for-in
|
||||
this.addEventListener(event, listeners[event]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes event listeners.
|
||||
* @param events array with the names of the events.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.removeEventListeners = function(events) {
|
||||
for(var i = 0; i < events.length; i++)
|
||||
this.removeEventListener(events[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the listeners and removes the Jitsi Meet frame.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
dispose() {
|
||||
this._transport.dispose();
|
||||
this.removeAllListeners();
|
||||
if (this.iframeHolder) {
|
||||
this.iframeHolder.parentNode.removeChild(this.iframeHolder);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the number of participants in the conference.
|
||||
* NOTE: the local participant is included.
|
||||
* @returns {int} the number of participants in the conference.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.getNumberOfParticipants = function() {
|
||||
return this.numberOfParticipants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes command. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* toggleAudio - mutes / unmutes audio with no arguments.
|
||||
* toggleVideo - mutes / unmutes video with no arguments.
|
||||
* toggleFilmStrip - hides / shows the filmstrip with no arguments.
|
||||
* If the command doesn't require any arguments the parameter should be set
|
||||
* to empty array or it may be omitted.
|
||||
*
|
||||
* @param {string} name - The name of the command.
|
||||
* @returns {void}
|
||||
*/
|
||||
executeCommand(name, ...args) {
|
||||
if (!(name in commands)) {
|
||||
logger.error('Not supported command name.');
|
||||
/**
|
||||
* Setups listeners that are used internally for JitsiMeetExternalAPI.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype._setupListeners = function() {
|
||||
this.postis.listen("participant-joined",
|
||||
changeParticipantNumber.bind(null, this, 1));
|
||||
this.postis.listen("participant-left",
|
||||
changeParticipantNumber.bind(null, this, -1));
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
this._transport.sendEvent({
|
||||
data: args,
|
||||
name: commands[name]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes commands. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* toggleAudio - mutes / unmutes audio. no arguments
|
||||
* toggleVideo - mutes / unmutes video. no arguments
|
||||
* toggleFilmStrip - hides / shows the filmstrip. no arguments
|
||||
* toggleChat - hides / shows chat. no arguments.
|
||||
* toggleContactList - hides / shows contact list. no arguments.
|
||||
* toggleShareScreen - starts / stops screen sharing. no arguments.
|
||||
*
|
||||
* @param {Object} commandList - The object with commands to be executed.
|
||||
* The keys of the object are the commands that will be executed and the
|
||||
* values are the arguments for the command.
|
||||
* @returns {void}
|
||||
*/
|
||||
executeCommands(commandList) {
|
||||
for (const key in commandList) { // eslint-disable-line guard-for-in
|
||||
this.executeCommand(key, commandList[key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of participants in the conference. The local
|
||||
* participant is included.
|
||||
*
|
||||
* @returns {int} The number of participants in the conference.
|
||||
*/
|
||||
getNumberOfParticipants() {
|
||||
return this.numberOfParticipants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes event listener.
|
||||
*
|
||||
* @param {string} event - The name of the event.
|
||||
* @returns {void}
|
||||
*
|
||||
* @deprecated
|
||||
* NOTE: This method is not removed for backward comatability purposes.
|
||||
*/
|
||||
removeEventListener(event) {
|
||||
this.removeAllListeners(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes event listeners.
|
||||
*
|
||||
* @param {Array<string>} eventList - Array with the names of the events.
|
||||
* @returns {void}
|
||||
*
|
||||
* @deprecated
|
||||
* NOTE: This method is not removed for backward comatability purposes.
|
||||
*/
|
||||
removeEventListeners(eventList) {
|
||||
eventList.forEach(event => this.removeEventListener(event));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes the listeners and removes the Jitsi Meet frame.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.dispose = function() {
|
||||
this.postis.destroy();
|
||||
var frame = document.getElementById(this.frameName);
|
||||
if(frame)
|
||||
frame.src = 'about:blank';
|
||||
var self = this;
|
||||
window.setTimeout(function () {
|
||||
self.iframeHolder.removeChild(self.frame);
|
||||
self.iframeHolder.parentNode.removeChild(self.iframeHolder);
|
||||
}, 10);
|
||||
};
|
||||
|
||||
module.exports = JitsiMeetExternalAPI;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export default from './API';
|
||||
export * from './constants';
|
||||
@@ -54,13 +54,13 @@ class State {
|
||||
this._propertyChangeCallback = propertyChangeCallback;
|
||||
}
|
||||
|
||||
get filmstripVisible () { return this._filmstripVisible; }
|
||||
get filmStripVisible () { return this._filmStripVisible; }
|
||||
|
||||
set filmstripVisible (b) {
|
||||
var oldValue = this._filmstripVisible;
|
||||
set filmStripVisible (b) {
|
||||
var oldValue = this._filmStripVisible;
|
||||
if (oldValue !== b) {
|
||||
this._filmstripVisible = b;
|
||||
this._firePropertyChange('filmstripVisible', oldValue, b);
|
||||
this._filmStripVisible = b;
|
||||
this._firePropertyChange('filmStripVisible', oldValue, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ class State {
|
||||
|
||||
/**
|
||||
* Represents the "Follow Me" feature which enables a moderator to
|
||||
* (partially) control the user experience/interface (e.g. filmstrip
|
||||
* (partially) control the user experience/interface (e.g. film strip
|
||||
* visibility) of (other) non-moderator particiapnts.
|
||||
*
|
||||
* @author Lyubomir Marinov
|
||||
@@ -143,7 +143,7 @@ class FollowMe {
|
||||
* @private
|
||||
*/
|
||||
_setFollowMeInitialState() {
|
||||
this._filmstripToggled.bind(this, this._UI.isFilmstripVisible());
|
||||
this._filmStripToggled.bind(this, this._UI.isFilmStripVisible());
|
||||
|
||||
var pinnedId = VideoLayout.getPinnedId();
|
||||
var isPinned = false;
|
||||
@@ -169,9 +169,9 @@ class FollowMe {
|
||||
* @private
|
||||
*/
|
||||
_addFollowMeListeners () {
|
||||
this.filmstripEventHandler = this._filmstripToggled.bind(this);
|
||||
this._UI.addListener(UIEvents.TOGGLED_FILMSTRIP,
|
||||
this.filmstripEventHandler);
|
||||
this.filmStripEventHandler = this._filmStripToggled.bind(this);
|
||||
this._UI.addListener(UIEvents.TOGGLED_FILM_STRIP,
|
||||
this.filmStripEventHandler);
|
||||
|
||||
var self = this;
|
||||
this.pinnedEndpointEventHandler = function (smallVideo, isPinned) {
|
||||
@@ -190,8 +190,8 @@ class FollowMe {
|
||||
* @private
|
||||
*/
|
||||
_removeFollowMeListeners () {
|
||||
this._UI.removeListener(UIEvents.TOGGLED_FILMSTRIP,
|
||||
this.filmstripEventHandler);
|
||||
this._UI.removeListener(UIEvents.TOGGLED_FILM_STRIP,
|
||||
this.filmStripEventHandler);
|
||||
this._UI.removeListener(UIEvents.TOGGLED_SHARED_DOCUMENT,
|
||||
this.sharedDocEventHandler);
|
||||
this._UI.removeListener(UIEvents.PINNED_ENDPOINT,
|
||||
@@ -214,14 +214,14 @@ class FollowMe {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this instance that the (visibility of the) filmstrip was
|
||||
* Notifies this instance that the (visibility of the) film strip was
|
||||
* toggled (in the user interface of the local participant).
|
||||
*
|
||||
* @param filmstripVisible {Boolean} {true} if the filmstrip was shown (as a
|
||||
* result of the toggle) or {false} if the filmstrip was hidden
|
||||
* @param filmStripVisible {Boolean} {true} if the film strip was shown (as
|
||||
* a result of the toggle) or {false} if the film strip was hidden
|
||||
*/
|
||||
_filmstripToggled (filmstripVisible) {
|
||||
this._local.filmstripVisible = filmstripVisible;
|
||||
_filmStripToggled (filmStripVisible) {
|
||||
this._local.filmStripVisible = filmStripVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,7 +279,7 @@ class FollowMe {
|
||||
_COMMAND,
|
||||
{
|
||||
attributes: {
|
||||
filmstripVisible: local.filmstripVisible,
|
||||
filmStripVisible: local.filmStripVisible,
|
||||
nextOnStage: local.nextOnStage,
|
||||
sharedDocumentVisible: local.sharedDocumentVisible
|
||||
}
|
||||
@@ -316,32 +316,32 @@ class FollowMe {
|
||||
|
||||
// Applies the received/remote command to the user experience/interface
|
||||
// of the local participant.
|
||||
this._onFilmstripVisible(attributes.filmstripVisible);
|
||||
this._onFilmStripVisible(attributes.filmStripVisible);
|
||||
this._onNextOnStage(attributes.nextOnStage);
|
||||
this._onSharedDocumentVisible(attributes.sharedDocumentVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a filmstrip open / close event received from FOLLOW-ME
|
||||
* Process a film strip open / close event received from FOLLOW-ME
|
||||
* command.
|
||||
* @param filmstripVisible indicates if the filmstrip has been shown or
|
||||
* @param filmStripVisible indicates if the film strip has been shown or
|
||||
* hidden
|
||||
* @private
|
||||
*/
|
||||
_onFilmstripVisible(filmstripVisible) {
|
||||
if (typeof filmstripVisible !== 'undefined') {
|
||||
_onFilmStripVisible(filmStripVisible) {
|
||||
if (typeof filmStripVisible !== 'undefined') {
|
||||
// XXX The Command(s) API doesn't preserve the types (of
|
||||
// attributes, at least) at the time of this writing so take into
|
||||
// account that what originated as a Boolean may be a String on
|
||||
// receipt.
|
||||
filmstripVisible = (filmstripVisible == 'true');
|
||||
filmStripVisible = (filmStripVisible == 'true');
|
||||
|
||||
// FIXME The UI (module) very likely doesn't (want to) expose its
|
||||
// eventEmitter as a public field. I'm not sure at the time of this
|
||||
// writing whether calling UI.toggleFilmstrip() is acceptable (from
|
||||
// writing whether calling UI.toggleFilmStrip() is acceptable (from
|
||||
// a design standpoint) either.
|
||||
if (filmstripVisible !== this._UI.isFilmstripVisible())
|
||||
this._UI.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||
if (filmStripVisible !== this._UI.isFilmStripVisible())
|
||||
this._UI.eventEmitter.emit(UIEvents.TOGGLE_FILM_STRIP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
299
modules/UI/UI.js
@@ -6,6 +6,8 @@ var UI = {};
|
||||
|
||||
import Chat from "./side_pannels/chat/Chat";
|
||||
import SidePanels from "./side_pannels/SidePanels";
|
||||
import Toolbar from "./toolbars/Toolbar";
|
||||
import ToolbarToggler from "./toolbars/ToolbarToggler";
|
||||
import Avatar from "./avatar/Avatar";
|
||||
import SideContainerToggler from "./side_pannels/SideContainerToggler";
|
||||
import UIUtil from "./util/UIUtil";
|
||||
@@ -15,7 +17,7 @@ import SharedVideoManager from './shared_video/SharedVideo';
|
||||
import Recording from "./recording/Recording";
|
||||
|
||||
import VideoLayout from "./videolayout/VideoLayout";
|
||||
import Filmstrip from "./videolayout/Filmstrip";
|
||||
import FilmStrip from "./videolayout/FilmStrip";
|
||||
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
||||
import Profile from "./side_pannels/profile/Profile";
|
||||
import Settings from "./../settings/Settings";
|
||||
@@ -23,30 +25,6 @@ import RingOverlay from "./ring_overlay/RingOverlay";
|
||||
import UIErrors from './UIErrors';
|
||||
import { debounce } from "../util/helpers";
|
||||
|
||||
|
||||
import {
|
||||
updateDeviceList
|
||||
} from '../../react/features/base/devices';
|
||||
import {
|
||||
setAudioMuted,
|
||||
setVideoMuted
|
||||
} from '../../react/features/base/media';
|
||||
import {
|
||||
openDeviceSelectionDialog
|
||||
} from '../../react/features/device-selection';
|
||||
import {
|
||||
checkAutoEnableDesktopSharing,
|
||||
dockToolbox,
|
||||
setAudioIconEnabled,
|
||||
setToolbarButton,
|
||||
setVideoIconEnabled,
|
||||
showDialPadButton,
|
||||
showEtherpadButton,
|
||||
showSharedVideoButton,
|
||||
showDialOutButton,
|
||||
showToolbox
|
||||
} from '../../react/features/toolbox';
|
||||
|
||||
var EventEmitter = require("events");
|
||||
UI.messageHandler = require("./util/MessageHandler");
|
||||
var messageHandler = UI.messageHandler;
|
||||
@@ -105,6 +83,16 @@ JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.CONSTRAINT_FAILED]
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.NO_DATA_FROM_SOURCE]
|
||||
= "dialog.micNotSendingData";
|
||||
|
||||
/**
|
||||
* Initialize toolbars with side panels.
|
||||
*/
|
||||
function setupToolbars() {
|
||||
// Initialize toolbar buttons
|
||||
Toolbar.init(eventEmitter);
|
||||
// Initialize side panels
|
||||
SidePanels.init(eventEmitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the application in and out of full screen mode
|
||||
* (a.k.a. presentation mode in Chrome).
|
||||
@@ -208,11 +196,11 @@ UI.setRaisedHandStatus = (participant, raisedHandStatus) => {
|
||||
/**
|
||||
* Sets the local "raised hand" status.
|
||||
*/
|
||||
UI.setLocalRaisedHandStatus
|
||||
= raisedHandStatus =>
|
||||
VideoLayout.setRaisedHandStatus(
|
||||
UI.setLocalRaisedHandStatus = (raisedHandStatus) => {
|
||||
VideoLayout.setRaisedHandStatus(
|
||||
APP.conference.getMyUserId(),
|
||||
raisedHandStatus);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize conference UI.
|
||||
@@ -243,7 +231,7 @@ UI.initConference = function () {
|
||||
UI.setUserAvatarID(id, Settings.getAvatarId());
|
||||
}
|
||||
|
||||
APP.store.dispatch(checkAutoEnableDesktopSharing());
|
||||
Toolbar.checkAutoEnableDesktopSharing();
|
||||
|
||||
if(!interfaceConfig.filmStripOnly) {
|
||||
Feedback.init(eventEmitter);
|
||||
@@ -265,7 +253,7 @@ UI.mucJoined = function () {
|
||||
/***
|
||||
* Handler for toggling filmstrip
|
||||
*/
|
||||
UI.handleToggleFilmstrip = () => UI.toggleFilmstrip();
|
||||
UI.handleToggleFilmStrip = () => UI.toggleFilmStrip();
|
||||
|
||||
/**
|
||||
* Sets tooltip defaults.
|
||||
@@ -306,12 +294,9 @@ UI.start = function () {
|
||||
// Set the defaults for tooltips.
|
||||
_setTooltipDefaults();
|
||||
|
||||
ToolbarToggler.init();
|
||||
SideContainerToggler.init(eventEmitter);
|
||||
Filmstrip.init(eventEmitter);
|
||||
|
||||
// By default start with remote videos hidden and rely on other logic to
|
||||
// make them visible.
|
||||
UI.setRemoteThumbnailsVisibility(false);
|
||||
FilmStrip.init(eventEmitter);
|
||||
|
||||
VideoLayout.init(eventEmitter);
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
@@ -321,32 +306,23 @@ UI.start = function () {
|
||||
|
||||
sharedVideoManager = new SharedVideoManager(eventEmitter);
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
let debouncedShowToolbar
|
||||
= debounce(
|
||||
() => UI.showToolbar(),
|
||||
100,
|
||||
{ leading: true, trailing: false });
|
||||
|
||||
let debouncedShowToolbar = debounce(() => {
|
||||
UI.showToolbar();
|
||||
}, 100, { leading: true, trailing: false });
|
||||
$("#videoconference_page").mousemove(debouncedShowToolbar);
|
||||
setupToolbars();
|
||||
|
||||
// Initialise the recording module.
|
||||
if (config.enableRecording) {
|
||||
if (config.enableRecording)
|
||||
Recording.init(eventEmitter, config.recordingType);
|
||||
}
|
||||
// Initialize side panels
|
||||
SidePanels.init(eventEmitter);
|
||||
} else {
|
||||
$("body").addClass("filmstrip-only");
|
||||
UI.showToolbar();
|
||||
Filmstrip.setFilmstripOnly();
|
||||
UIUtil.setVisible('mainToolbarContainer', false);
|
||||
FilmStrip.setupFilmStripOnly();
|
||||
messageHandler.enableNotifications(false);
|
||||
JitsiPopover.enabled = false;
|
||||
}
|
||||
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
$("body").addClass("vertical-filmstrip");
|
||||
}
|
||||
|
||||
document.title = interfaceConfig.APP_NAME;
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
@@ -370,18 +346,9 @@ UI.start = function () {
|
||||
|
||||
}
|
||||
|
||||
const { callee } = APP.store.getState()['features/jwt'];
|
||||
|
||||
callee && UI.showRingOverlay();
|
||||
};
|
||||
|
||||
/**
|
||||
* Invokes cleanup of any deferred execution within relevant UI modules.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.stopDaemons = () => {
|
||||
VideoLayout.resetLargeVideo();
|
||||
if(APP.tokenData.callee) {
|
||||
UI.showRingOverlay();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -476,8 +443,7 @@ UI.initEtherpad = name => {
|
||||
logger.log('Etherpad is enabled');
|
||||
etherpadManager
|
||||
= new EtherpadManager(config.etherpad_base, name, eventEmitter);
|
||||
|
||||
APP.store.dispatch(showEtherpadButton());
|
||||
Toolbar.showEtherpadButton();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -493,7 +459,7 @@ UI.getSharedDocumentManager = () => etherpadManager;
|
||||
UI.addUser = function (user) {
|
||||
var id = user.getId();
|
||||
var displayName = user.getDisplayName();
|
||||
UI.hideRingOverlay();
|
||||
UI.hideRingOverLay();
|
||||
if (UI.ContactList)
|
||||
UI.ContactList.addContact(id);
|
||||
|
||||
@@ -552,9 +518,8 @@ UI.onPeerVideoTypeChanged
|
||||
UI.updateLocalRole = isModerator => {
|
||||
VideoLayout.showModeratorIndicator();
|
||||
|
||||
APP.store.dispatch(showDialOutButton(isModerator));
|
||||
APP.store.dispatch(showSharedVideoButton());
|
||||
|
||||
Toolbar.showSipCallButton(isModerator);
|
||||
Toolbar.showSharedVideoButton(isModerator);
|
||||
Recording.showRecordingButton(isModerator);
|
||||
SettingsMenu.showStartMutedOptions(isModerator);
|
||||
SettingsMenu.showFollowMeOptions(isModerator);
|
||||
@@ -597,40 +562,25 @@ UI.updateUserRole = user => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the user status.
|
||||
*
|
||||
* @param {JitsiParticipant} user - The user which status we need to update.
|
||||
* @param {string} status - The new status.
|
||||
*/
|
||||
UI.updateUserStatus = (user, status) => {
|
||||
let displayName = user.getDisplayName();
|
||||
messageHandler.notify(
|
||||
displayName, '', 'connected', "dialOut.statusMessage",
|
||||
{
|
||||
status: UIUtil.escapeHtml(status)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles smileys in the chat.
|
||||
*/
|
||||
UI.toggleSmileys = () => Chat.toggleSmileys();
|
||||
|
||||
/**
|
||||
* Toggles filmstrip.
|
||||
* Toggles film strip.
|
||||
*/
|
||||
UI.toggleFilmstrip = function () {
|
||||
var self = Filmstrip;
|
||||
self.toggleFilmstrip.apply(self, arguments);
|
||||
UI.toggleFilmStrip = function () {
|
||||
var self = FilmStrip;
|
||||
self.toggleFilmStrip.apply(self, arguments);
|
||||
VideoLayout.resizeVideoArea(true, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if the filmstrip is currently visible or not.
|
||||
* @returns {true} if the filmstrip is currently visible, otherwise
|
||||
* Indicates if the film strip is currently visible or not.
|
||||
* @returns {true} if the film strip is currently visible, otherwise
|
||||
*/
|
||||
UI.isFilmstripVisible = () => Filmstrip.isFilmstripVisible();
|
||||
UI.isFilmStripVisible = () => FilmStrip.isFilmStripVisible();
|
||||
|
||||
/**
|
||||
* Toggles chat panel.
|
||||
@@ -723,10 +673,7 @@ UI.askForNickname = function () {
|
||||
UI.setAudioMuted = function (id, muted) {
|
||||
VideoLayout.onAudioMute(id, muted);
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
APP.store.dispatch(setAudioMuted(muted));
|
||||
APP.store.dispatch(setToolbarButton('microphone', {
|
||||
toggled: muted
|
||||
}));
|
||||
Toolbar.toggleAudioIcon(muted);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -736,21 +683,10 @@ UI.setAudioMuted = function (id, muted) {
|
||||
UI.setVideoMuted = function (id, muted) {
|
||||
VideoLayout.onVideoMute(id, muted);
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
APP.store.dispatch(setVideoMuted(muted));
|
||||
APP.store.dispatch(setToolbarButton('camera', {
|
||||
toggled: muted
|
||||
}));
|
||||
Toolbar.toggleVideoIcon(muted);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers an update of remote video and large video displays so they may pick
|
||||
* up any state changes that have occurred elsewhere.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.updateAllVideos = () => VideoLayout.updateAllVideos();
|
||||
|
||||
/**
|
||||
* Adds a listener that would be notified on the given type of event.
|
||||
*
|
||||
@@ -777,7 +713,9 @@ UI.removeListener = function (type, listener) {
|
||||
* @param type the type of the event we're emitting
|
||||
* @param options the parameters for the event
|
||||
*/
|
||||
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
|
||||
UI.emitEvent = function (type, options) {
|
||||
eventEmitter.emit(type, options);
|
||||
};
|
||||
|
||||
UI.clickOnVideo = function (videoNumber) {
|
||||
let videos = $("#remoteVideos .videocontainer:not(#mixedstream)");
|
||||
@@ -790,11 +728,15 @@ UI.clickOnVideo = function (videoNumber) {
|
||||
videos[videoIndex].click();
|
||||
};
|
||||
|
||||
// Used by torture.
|
||||
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
|
||||
//Used by torture
|
||||
UI.showToolbar = function (timeout) {
|
||||
return ToolbarToggler.showToolbar(timeout);
|
||||
};
|
||||
|
||||
// Used by torture.
|
||||
UI.dockToolbar = dock => APP.store.dispatch(dockToolbox(dock));
|
||||
//Used by torture
|
||||
UI.dockToolbar = function (isDock) {
|
||||
ToolbarToggler.dockToolbar(isDock);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the avatar for participant.
|
||||
@@ -820,9 +762,6 @@ UI.setUserEmail = function (id, email) {
|
||||
Avatar.setUserEmail(id, email);
|
||||
|
||||
changeAvatar(id, Avatar.getAvatarUrl(id));
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
Profile.changeEmail(email);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -906,9 +845,11 @@ UI.handleLastNEndpoints = function (leavingIds, enteringIds) {
|
||||
* Will handle notification about participant's connectivity status change.
|
||||
*
|
||||
* @param {string} id the id of remote participant(MUC jid)
|
||||
* @param {boolean} isActive true if the connection is ok or false if the user
|
||||
* is having connectivity issues.
|
||||
*/
|
||||
UI.participantConnectionStatusChanged = function (id) {
|
||||
VideoLayout.onParticipantConnectionStatusChanged(id);
|
||||
UI.participantConnectionStatusChanged = function (id, isActive) {
|
||||
VideoLayout.onParticipantConnectionStatusChanged(id, isActive);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -969,18 +910,16 @@ UI.promptDisplayName = () => {
|
||||
* @param {string} id user id
|
||||
* @param {number} lvl audio level
|
||||
*/
|
||||
UI.setAudioLevel = (id, lvl) => VideoLayout.setAudioLevel(id, lvl);
|
||||
UI.setAudioLevel = function (id, lvl) {
|
||||
VideoLayout.setAudioLevel(id, lvl);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update state of desktop sharing buttons.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.updateDesktopSharingButtons
|
||||
= () =>
|
||||
APP.store.dispatch(setToolbarButton('desktop', {
|
||||
toggled: APP.conference.isSharingScreen
|
||||
}));
|
||||
UI.updateDesktopSharingButtons = function () {
|
||||
Toolbar.updateDesktopSharingButtonState();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide connection quality statistics from UI.
|
||||
@@ -1031,8 +970,11 @@ UI.addMessage = function (from, displayName, message, stamp) {
|
||||
Chat.updateChatConversation(from, displayName, message, stamp);
|
||||
};
|
||||
|
||||
UI.updateDTMFSupport
|
||||
= isDTMFSupported => APP.store.dispatch(showDialPadButton(isDTMFSupported));
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
UI.updateDTMFSupport = function (isDTMFSupported) {
|
||||
//TODO: enable when the UI is ready
|
||||
//Toolbar.showDialPadButton(isDTMFSupported);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show user feedback dialog if its required and enabled after pressing the
|
||||
@@ -1131,7 +1073,29 @@ UI.onLocalRaiseHandChanged = function (isRaisedHand) {
|
||||
* @param {object[]} devices new list of available devices
|
||||
*/
|
||||
UI.onAvailableDevicesChanged = function (devices) {
|
||||
APP.store.dispatch(updateDeviceList(devices));
|
||||
SettingsMenu.changeDevicesList(devices);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets microphone's <select> element to select microphone ID from settings.
|
||||
*/
|
||||
UI.setSelectedMicFromSettings = function () {
|
||||
SettingsMenu.setSelectedMicFromSettings();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets camera's <select> element to select camera ID from settings.
|
||||
*/
|
||||
UI.setSelectedCameraFromSettings = function () {
|
||||
SettingsMenu.setSelectedCameraFromSettings();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets audio outputs's <select> element to select audio output ID from
|
||||
* settings.
|
||||
*/
|
||||
UI.setSelectedAudioOutputFromSettings = function () {
|
||||
SettingsMenu.setSelectedAudioOutputFromSettings();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1150,15 +1114,6 @@ UI.getLargeVideo = function () {
|
||||
return VideoLayout.getLargeVideo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether or not the passed in user id is currently pinned to the large
|
||||
* video.
|
||||
*
|
||||
* @param {string} userId - The id of the user to check is pinned or not.
|
||||
* @returns {boolean} True if the user is currently pinned to the large video.
|
||||
*/
|
||||
UI.isPinned = userId => VideoLayout.getPinnedId() === userId;
|
||||
|
||||
/**
|
||||
* Shows dialog with a link to FF extension.
|
||||
*/
|
||||
@@ -1360,8 +1315,7 @@ UI.onSharedVideoStop = function (id, attributes) {
|
||||
* @param {boolean} enabled indicates if the camera button should be enabled
|
||||
* or disabled
|
||||
*/
|
||||
UI.setCameraButtonEnabled
|
||||
= enabled => APP.store.dispatch(setVideoIconEnabled(enabled));
|
||||
UI.setCameraButtonEnabled = enabled => Toolbar.setVideoIconEnabled(enabled);
|
||||
|
||||
/**
|
||||
* Enables / disables microphone toolbar button.
|
||||
@@ -1369,29 +1323,28 @@ UI.setCameraButtonEnabled
|
||||
* @param {boolean} enabled indicates if the microphone button should be
|
||||
* enabled or disabled
|
||||
*/
|
||||
UI.setMicrophoneButtonEnabled
|
||||
= enabled => APP.store.dispatch(setAudioIconEnabled(enabled));
|
||||
UI.setMicrophoneButtonEnabled = enabled => Toolbar.setAudioIconEnabled(enabled);
|
||||
|
||||
UI.showRingOverlay = function () {
|
||||
const { callee } = APP.store.getState()['features/jwt'];
|
||||
|
||||
callee && RingOverlay.show(callee, interfaceConfig.DISABLE_RINGING);
|
||||
|
||||
Filmstrip.toggleFilmstrip(false, false);
|
||||
RingOverlay.show(APP.tokenData.callee, interfaceConfig.DISABLE_RINGING);
|
||||
FilmStrip.toggleFilmStrip(false, false);
|
||||
};
|
||||
|
||||
UI.hideRingOverlay
|
||||
= () => RingOverlay.hide() && Filmstrip.toggleFilmstrip(true, false);
|
||||
UI.hideRingOverLay = function () {
|
||||
if (!RingOverlay.hide())
|
||||
return;
|
||||
FilmStrip.toggleFilmStrip(true, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if any the "top" overlays are currently visible. The check includes
|
||||
* the call overlay, the suspended overlay, the GUM permissions overlay, and the
|
||||
* page-reload overlay.
|
||||
* the call overlay, suspended overlay, GUM permissions overlay
|
||||
* and a page reload overlay.
|
||||
*
|
||||
* @returns {*|boolean} {true} if an overlay is visible; {false}, otherwise
|
||||
* @returns {*|boolean} {true} if the overlay is visible, {false} otherwise
|
||||
*/
|
||||
UI.isOverlayVisible = function () {
|
||||
return this.isRingOverlayVisible() || this.overlayVisible;
|
||||
return RingOverlay.isVisible() || this.overlayVisible;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1406,23 +1359,6 @@ UI.isRingOverlayVisible = () => RingOverlay.isVisible();
|
||||
*/
|
||||
UI.onUserFeaturesChanged = user => VideoLayout.onUserFeaturesChanged(user);
|
||||
|
||||
/**
|
||||
* Returns the number of known remote videos.
|
||||
*
|
||||
* @returns {number} The number of remote videos.
|
||||
*/
|
||||
UI.getRemoteVideosCount = () => VideoLayout.getRemoteVideosCount();
|
||||
|
||||
/**
|
||||
* Makes remote thumbnail videos visible or not visible.
|
||||
*
|
||||
* @param {boolean} shouldHide - True if remote thumbnails should be hidden,
|
||||
* false f they should be visible.
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.setRemoteThumbnailsVisibility
|
||||
= shouldHide => Filmstrip.setRemoteVideoVisibility(shouldHide);
|
||||
|
||||
const UIListeners = new Map([
|
||||
[
|
||||
UIEvents.ETHERPAD_CLICKED,
|
||||
@@ -1438,33 +1374,16 @@ const UIListeners = new Map([
|
||||
UI.toggleChat
|
||||
], [
|
||||
UIEvents.TOGGLE_SETTINGS,
|
||||
() => {
|
||||
// Opening of device selection is special-cased as it is a dialog
|
||||
// opened through a button in settings and not directly displayed in
|
||||
// settings itself. As it is not useful to only have a settings menu
|
||||
// with a button to open a dialog, open the dialog directly instead.
|
||||
if (interfaceConfig.SETTINGS_SECTIONS.length === 1
|
||||
&& UIUtil.isSettingEnabled('devices')) {
|
||||
APP.store.dispatch(openDeviceSelectionDialog());
|
||||
} else {
|
||||
UI.toggleSidePanel("settings_container");
|
||||
}
|
||||
}
|
||||
() => UI.toggleSidePanel("settings_container")
|
||||
], [
|
||||
UIEvents.TOGGLE_CONTACT_LIST,
|
||||
UI.toggleContactList
|
||||
], [
|
||||
UIEvents.TOGGLE_PROFILE,
|
||||
() => {
|
||||
const {
|
||||
isGuest
|
||||
} = APP.store.getState()['features/jwt'];
|
||||
|
||||
isGuest && UI.toggleSidePanel('profile_container');
|
||||
}
|
||||
() => APP.tokenData.isGuest && UI.toggleSidePanel("profile_container")
|
||||
], [
|
||||
UIEvents.TOGGLE_FILMSTRIP,
|
||||
UI.handleToggleFilmstrip
|
||||
UIEvents.TOGGLE_FILM_STRIP,
|
||||
UI.handleToggleFilmStrip
|
||||
], [
|
||||
UIEvents.FOLLOW_ME_ENABLED,
|
||||
enabled => (followMeHandler && followMeHandler.enableFollowMe(enabled))
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
/* global APP, config, JitsiMeetJS, Promise */
|
||||
|
||||
import { openConnection } from '../../../connection';
|
||||
import { setJWT } from '../../../react/features/jwt';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import LoginDialog from './LoginDialog';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
import {openConnection} from '../../../connection';
|
||||
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
let externalAuthWindow;
|
||||
let authRequiredDialog;
|
||||
@@ -75,20 +73,15 @@ function redirectToTokenAuthService(roomName) {
|
||||
* @param room the name fo the conference room.
|
||||
*/
|
||||
function initJWTTokenListener(room) {
|
||||
var listener = function ({ data, source }) {
|
||||
if (externalAuthWindow !== source) {
|
||||
var listener = function (event) {
|
||||
if (externalAuthWindow !== event.source) {
|
||||
logger.warn("Ignored message not coming " +
|
||||
"from external authnetication window");
|
||||
return;
|
||||
}
|
||||
|
||||
let jwt;
|
||||
|
||||
if (data && (jwt = data.jwtToken)) {
|
||||
logger.info("Received JSON Web Token (JWT):", jwt);
|
||||
|
||||
APP.store.dispatch(setJWT(jwt));
|
||||
|
||||
if (event.data && event.data.jwtToken) {
|
||||
config.token = event.data.jwtToken;
|
||||
logger.info("Received JWT token:", config.token);
|
||||
var roomName = room.getName();
|
||||
openConnection({retry: false, roomName: roomName })
|
||||
.then(function (connection) {
|
||||
|
||||
@@ -30,7 +30,7 @@ let users = {};
|
||||
export default {
|
||||
/**
|
||||
* Sets prop in users object.
|
||||
* @param id {string} user id or undefined for the local user.
|
||||
* @param id {string} user id
|
||||
* @param prop {string} name of the prop
|
||||
* @param val {string} value to be set
|
||||
*/
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
// FIXME: Fixes the issue with not be able to return avatar for the
|
||||
// local user when the conference has been left. Maybe there is beter
|
||||
// way to solve it.
|
||||
if(!id || APP.conference.isLocalId(id)) {
|
||||
if(APP.conference.isLocalId(id)) {
|
||||
id = "local";
|
||||
}
|
||||
if(!val || (users[id] && users[id][prop] === val))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import VideoLayout from "../videolayout/VideoLayout";
|
||||
import LargeContainer from '../videolayout/LargeContainer';
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import Filmstrip from '../videolayout/Filmstrip';
|
||||
import FilmStrip from '../videolayout/FilmStrip';
|
||||
|
||||
/**
|
||||
* Etherpad options.
|
||||
@@ -103,7 +103,7 @@ class Etherpad extends LargeContainer {
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
resize (containerWidth, containerHeight, animate) {
|
||||
let height = containerHeight - Filmstrip.getFilmstripHeight();
|
||||
let height = containerHeight - FilmStrip.getFilmStripHeight();
|
||||
let width = containerWidth;
|
||||
|
||||
$(this.iframe).width(width).height(height);
|
||||
|
||||
210
modules/UI/invite/Invite.js
Normal file
@@ -0,0 +1,210 @@
|
||||
/* global JitsiMeetJS, APP */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import InviteDialogView from './InviteDialogView';
|
||||
import createRoomLocker from './RoomLocker';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
|
||||
/**
|
||||
* Invite module
|
||||
* Constructor takes conference object giving
|
||||
* ability to subscribe on its events
|
||||
*/
|
||||
class Invite {
|
||||
constructor(conference) {
|
||||
this.conference = conference;
|
||||
this.inviteUrl = APP.ConferenceUrl.getInviteUrl();
|
||||
this.createRoomLocker(conference);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registering listeners.
|
||||
* Primarily listeners for conference events.
|
||||
*/
|
||||
registerListeners() {
|
||||
|
||||
this.conference.on(ConferenceEvents.LOCK_STATE_CHANGED,
|
||||
(locked, error) => {
|
||||
|
||||
logger.log("Received channel password lock change: ", locked,
|
||||
error);
|
||||
|
||||
if (!locked) {
|
||||
this.getRoomLocker().resetPassword();
|
||||
}
|
||||
|
||||
this.setLockedFromElsewhere(locked);
|
||||
});
|
||||
|
||||
this.conference.on(ConferenceEvents.USER_ROLE_CHANGED, (id) => {
|
||||
if (APP.conference.isLocalId(id)
|
||||
&& this.isModerator !== this.conference.isModerator()) {
|
||||
|
||||
this.setModerator(this.conference.isModerator());
|
||||
}
|
||||
});
|
||||
|
||||
this.conference.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
||||
let roomLocker = this.getRoomLocker();
|
||||
roomLocker.hideRequirePasswordDialog();
|
||||
});
|
||||
|
||||
APP.UI.addListener( UIEvents.INVITE_CLICKED,
|
||||
() => { this.openLinkDialog(); });
|
||||
|
||||
APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
|
||||
() => {
|
||||
let roomLocker = this.getRoomLocker();
|
||||
this.setLockedFromElsewhere(true);
|
||||
roomLocker.requirePassword().then(() => {
|
||||
let pass = roomLocker.password;
|
||||
// we received that password is required, but user is trying
|
||||
// anyway to login without a password, mark room as not
|
||||
// locked in case he succeeds (maybe someone removed the
|
||||
// password meanwhile), if it is still locked another
|
||||
// password required will be received and the room again
|
||||
// will be marked as locked.
|
||||
if (!pass)
|
||||
this.setLockedFromElsewhere(false);
|
||||
this.conference.join(pass);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the view.
|
||||
* If dialog hasn't been defined -
|
||||
* creates it and updates
|
||||
*/
|
||||
updateView() {
|
||||
if (!this.view) {
|
||||
this.initDialog();
|
||||
}
|
||||
|
||||
this.view.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Room locker factory
|
||||
* @param room
|
||||
* @returns {Object} RoomLocker
|
||||
* @factory
|
||||
*/
|
||||
createRoomLocker(room = this.conference) {
|
||||
let roomLocker = createRoomLocker(room);
|
||||
this.roomLocker = roomLocker;
|
||||
return this.getRoomLocker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Room locker getter
|
||||
* @returns {Object} RoomLocker
|
||||
*/
|
||||
getRoomLocker() {
|
||||
return this.roomLocker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the invite link dialog.
|
||||
*/
|
||||
openLinkDialog () {
|
||||
if (!this.view) {
|
||||
this.initDialog();
|
||||
}
|
||||
|
||||
this.view.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog initialization.
|
||||
* creating view object using as a model this module
|
||||
*/
|
||||
initDialog() {
|
||||
this.view = new InviteDialogView(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Password getter
|
||||
* @returns {String} password
|
||||
*/
|
||||
getPassword() {
|
||||
return this.getRoomLocker().password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches between the moderator view and normal view.
|
||||
*
|
||||
* @param isModerator indicates if the participant is moderator
|
||||
*/
|
||||
setModerator(isModerator) {
|
||||
this.isModerator = isModerator;
|
||||
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to unlock the room.
|
||||
* If the current user is moderator.
|
||||
*/
|
||||
setRoomUnlocked() {
|
||||
if (this.isModerator) {
|
||||
this.getRoomLocker().lock().then(() => {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to lock the room if
|
||||
* the current user is moderator.
|
||||
* Takes the password.
|
||||
* @param {String} newPass
|
||||
*/
|
||||
setRoomLocked(newPass) {
|
||||
let isModerator = this.isModerator;
|
||||
if (isModerator && (newPass || !this.getRoomLocker().isLocked)) {
|
||||
this.getRoomLocker().lock(newPass).then(() => {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for encoding
|
||||
* Invite URL
|
||||
* @returns {string}
|
||||
*/
|
||||
getEncodedInviteUrl() {
|
||||
return encodeURI(this.inviteUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is locked flag.
|
||||
* Delegates to room locker
|
||||
* @returns {Boolean} isLocked
|
||||
*/
|
||||
isLocked() {
|
||||
return this.getRoomLocker().isLocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flag locked from elsewhere to room locker.
|
||||
* @param isLocked
|
||||
*/
|
||||
setLockedFromElsewhere(isLocked) {
|
||||
let roomLocker = this.getRoomLocker();
|
||||
let oldLockState = roomLocker.isLocked;
|
||||
if (oldLockState !== isLocked) {
|
||||
roomLocker.lockedElsewhere = isLocked;
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
|
||||
this.updateView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Invite;
|
||||
378
modules/UI/invite/InviteDialogView.js
Normal file
@@ -0,0 +1,378 @@
|
||||
/* global $, APP, JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Substate for password
|
||||
* @type {{LOCKED: string, UNLOCKED: string}}
|
||||
*/
|
||||
const States = {
|
||||
LOCKED: 'locked',
|
||||
UNLOCKED: 'unlocked'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class representing view for Invite dialog
|
||||
* @class InviteDialogView
|
||||
*/
|
||||
export default class InviteDialogView {
|
||||
constructor(model) {
|
||||
this.unlockHint = "unlockHint";
|
||||
this.lockHint = "lockHint";
|
||||
this.model = model;
|
||||
|
||||
if (this.model.inviteUrl === null) {
|
||||
this.inviteAttributes = `data-i18n="[value]inviteUrlDefaultMsg"`;
|
||||
} else {
|
||||
this.inviteAttributes
|
||||
= `value="${this.model.getEncodedInviteUrl()}"`;
|
||||
}
|
||||
|
||||
this.initDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of dialog property
|
||||
*/
|
||||
initDialog() {
|
||||
let dialog = {};
|
||||
dialog.closeFunction = this.closeFunction.bind(this);
|
||||
dialog.submitFunction = this.submitFunction.bind(this);
|
||||
dialog.loadedFunction = this.loadedFunction.bind(this);
|
||||
|
||||
dialog.titleKey = "dialog.shareLink";
|
||||
this.dialog = dialog;
|
||||
|
||||
this.dialog.states = this.getStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for submitting dialog
|
||||
* @param e
|
||||
* @param v
|
||||
*/
|
||||
submitFunction(e, v) {
|
||||
if (v && this.model.inviteUrl) {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
|
||||
} else {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for load dialog
|
||||
* @param event
|
||||
*/
|
||||
loadedFunction(event) {
|
||||
if (this.model.inviteUrl) {
|
||||
document.getElementById('inviteLinkRef').select();
|
||||
} else {
|
||||
if (event && event.target) {
|
||||
$(event.target).find('button[value=true]')
|
||||
.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for closing dialog
|
||||
* @param e
|
||||
* @param v
|
||||
* @param m
|
||||
* @param f
|
||||
*/
|
||||
closeFunction(e, v, m, f) {
|
||||
$(document).off('click', '.copyInviteLink', this.copyToClipboard);
|
||||
|
||||
if(!v && !m && !f)
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all states of the dialog
|
||||
* @returns {{}}
|
||||
*/
|
||||
getStates() {
|
||||
let {
|
||||
titleKey
|
||||
} = this.dialog;
|
||||
let doneMsg = APP.translation.generateTranslationHTML('dialog.done');
|
||||
let states = {};
|
||||
let buttons = {};
|
||||
buttons[`${doneMsg}`] = true;
|
||||
|
||||
states[States.UNLOCKED] = {
|
||||
titleKey,
|
||||
html: this.getShareLinkBlock() + this.getAddPasswordBlock(),
|
||||
buttons
|
||||
};
|
||||
states[States.LOCKED] = {
|
||||
titleKey,
|
||||
html: this.getShareLinkBlock() + this.getPasswordBlock(),
|
||||
buttons
|
||||
};
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for invite link input
|
||||
* @returns {string}
|
||||
*/
|
||||
getShareLinkBlock() {
|
||||
let classes = 'button-control button-control_light copyInviteLink';
|
||||
return (
|
||||
`<div class="form-control">
|
||||
<label class="form-control__label" for="inviteLinkRef"
|
||||
data-i18n="${this.dialog.titleKey}"></label>
|
||||
<div class="form-control__container">
|
||||
<input class="input-control inviteLink"
|
||||
id="inviteLinkRef" type="text"
|
||||
${this.inviteAttributes} readonly>
|
||||
<button data-i18n="dialog.copy" class="${classes}"></button>
|
||||
</div>
|
||||
<p class="form-control__hint ${this.lockHint}">
|
||||
<span class="icon-security-locked"></span>
|
||||
<span data-i18n="dialog.roomLocked"></span>
|
||||
</p>
|
||||
<p class="form-control__hint ${this.unlockHint}">
|
||||
<span class="icon-security"></span>
|
||||
<span data-i18n="roomUnlocked"></span>
|
||||
</p>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for adding password input
|
||||
* @returns {string}
|
||||
*/
|
||||
getAddPasswordBlock() {
|
||||
let html;
|
||||
|
||||
if (this.model.isModerator) {
|
||||
html = (`
|
||||
<div class="form-control">
|
||||
<label class="form-control__label"
|
||||
for="newPasswordInput" data-i18n="dialog.addPassword">
|
||||
</label>
|
||||
<div class="form-control__container">
|
||||
<input class="input-control"
|
||||
id="newPasswordInput"
|
||||
type="text"
|
||||
data-i18n="[placeholder]dialog.createPassword">
|
||||
<button id="addPasswordBtn" id="inviteDialogAddPassword"
|
||||
disabled data-i18n="dialog.add"
|
||||
class="button-control button-control_light">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
html = '';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for password (when room is locked)
|
||||
* @returns {string}
|
||||
*/
|
||||
getPasswordBlock() {
|
||||
let password = this.model.getPassword();
|
||||
let { isModerator } = this.model;
|
||||
|
||||
if (isModerator) {
|
||||
return (`
|
||||
<div class="form-control">
|
||||
<label class="form-control__label"
|
||||
data-i18n="dialog.passwordLabel"></label>
|
||||
<div class="form-control__container">
|
||||
<p>
|
||||
<span class="form-control__text"
|
||||
data-i18n="dialog.currentPassword"></span>
|
||||
<span id="inviteDialogPassword"
|
||||
class="form-control__em">
|
||||
${password}
|
||||
</span>
|
||||
</p>
|
||||
<a class="link form-control__right"
|
||||
id="inviteDialogRemovePassword"
|
||||
data-i18n="dialog.removePassword"></a>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
return (`
|
||||
<div class="form-control">
|
||||
<p>A participant protected this call with a password.</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Opening the dialog
|
||||
*/
|
||||
open() {
|
||||
let {
|
||||
submitFunction,
|
||||
loadedFunction,
|
||||
closeFunction
|
||||
} = this.dialog;
|
||||
|
||||
let states = this.getStates();
|
||||
let initial = this.model.roomLocked ? States.LOCKED : States.UNLOCKED;
|
||||
|
||||
APP.UI.messageHandler.openDialogWithStates(states, {
|
||||
submit: submitFunction,
|
||||
loaded: loadedFunction,
|
||||
close: closeFunction,
|
||||
size: 'medium'
|
||||
});
|
||||
$.prompt.goToState(initial);
|
||||
|
||||
this.registerListeners();
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting event handlers
|
||||
* used in dialog
|
||||
*/
|
||||
registerListeners() {
|
||||
const ENTER_KEY = 13;
|
||||
let addPasswordBtn = '#addPasswordBtn';
|
||||
let copyInviteLink = '.copyInviteLink';
|
||||
let newPasswordInput = '#newPasswordInput';
|
||||
let removePassword = '#inviteDialogRemovePassword';
|
||||
|
||||
$(document).on('click', copyInviteLink, this.copyToClipboard);
|
||||
$(removePassword).on('click', () => {
|
||||
this.model.setRoomUnlocked();
|
||||
});
|
||||
let boundSetPassword = this._setPassword.bind(this);
|
||||
$(document).on('click', addPasswordBtn, boundSetPassword);
|
||||
let boundDisablePass = this.disableAddPassIfInputEmpty.bind(this);
|
||||
$(document).on('keypress', newPasswordInput, boundDisablePass);
|
||||
|
||||
// We need to handle keydown event because impromptu
|
||||
// is listening to it too for closing the dialog
|
||||
$(newPasswordInput).on('keydown', (e) => {
|
||||
if (e.keyCode === ENTER_KEY) {
|
||||
e.stopPropagation();
|
||||
this._setPassword();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Marking room as locked
|
||||
* @private
|
||||
*/
|
||||
_setPassword() {
|
||||
let $passInput = $('#newPasswordInput');
|
||||
let newPass = $passInput.val();
|
||||
|
||||
if(newPass) {
|
||||
this.model.setRoomLocked(newPass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking input and if it's empty then
|
||||
* disable add pass button
|
||||
*/
|
||||
disableAddPassIfInputEmpty() {
|
||||
let $passInput = $('#newPasswordInput');
|
||||
let $addPassBtn = $('#addPasswordBtn');
|
||||
|
||||
if(!$passInput.val()) {
|
||||
$addPassBtn.prop('disabled', true);
|
||||
} else {
|
||||
$addPassBtn.prop('disabled', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copying text to clipboard
|
||||
*/
|
||||
copyToClipboard() {
|
||||
$('.inviteLink').each(function () {
|
||||
let $el = $(this).closest('.jqistate');
|
||||
|
||||
// TOFIX: We can select only visible elements
|
||||
if($el.css('display') === 'block') {
|
||||
this.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
this.blur();
|
||||
}
|
||||
catch (err) {
|
||||
logger.error('error when copy the text');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method syncing the view and the model
|
||||
*/
|
||||
updateView() {
|
||||
let pass = this.model.getPassword();
|
||||
let { isModerator } = this.model;
|
||||
if (this.model.getRoomLocker().lockedElsewhere || !pass) {
|
||||
$('#inviteDialogPassword').attr("data-i18n", "passwordSetRemotely");
|
||||
APP.translation.translateElement($('#inviteDialogPassword'));
|
||||
} else {
|
||||
$('#inviteDialogPassword').removeAttr("data-i18n");
|
||||
$('#inviteDialogPassword').text(pass);
|
||||
}
|
||||
|
||||
// if we are not moderator we cannot remove password
|
||||
if (isModerator)
|
||||
$('#inviteDialogRemovePassword').show();
|
||||
else
|
||||
$('#inviteDialogRemovePassword').hide();
|
||||
|
||||
$('#newPasswordInput').val('');
|
||||
this.disableAddPassIfInputEmpty();
|
||||
|
||||
this.updateInviteLink();
|
||||
|
||||
$.prompt.goToState(
|
||||
(this.model.isLocked())
|
||||
? States.LOCKED
|
||||
: States.UNLOCKED);
|
||||
|
||||
let roomLocked = `.${this.lockHint}`;
|
||||
let roomUnlocked = `.${this.unlockHint}`;
|
||||
|
||||
let showDesc = this.model.isLocked() ? roomLocked : roomUnlocked;
|
||||
let hideDesc = !this.model.isLocked() ? roomLocked : roomUnlocked;
|
||||
|
||||
$(showDesc).show();
|
||||
$(hideDesc).hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates invite link
|
||||
*/
|
||||
updateInviteLink() {
|
||||
// If the invite dialog has been already opened we update the
|
||||
// information.
|
||||
let inviteLink = document.querySelectorAll('.inviteLink');
|
||||
let list = Array.from(inviteLink);
|
||||
list.forEach((inviteLink) => {
|
||||
inviteLink.value = this.model.inviteUrl;
|
||||
inviteLink.select();
|
||||
});
|
||||
|
||||
$('#inviteLinkRef').parent()
|
||||
.find('button[value=true]').prop('disabled', false);
|
||||
}
|
||||
}
|
||||
162
modules/UI/invite/RequirePasswordDialog.js
Normal file
@@ -0,0 +1,162 @@
|
||||
/* global APP */
|
||||
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
/**
|
||||
* Show dialog which asks for required conference password.
|
||||
* @returns {Promise<string>} password or nothing if user canceled
|
||||
*/
|
||||
export default class RequirePasswordDialog {
|
||||
constructor() {
|
||||
this.titleKey = 'dialog.passwordRequired';
|
||||
this.labelKey = 'dialog.passwordLabel';
|
||||
this.errorKey = 'dialog.incorrectPassword';
|
||||
this.errorId = 'passwordRequiredError';
|
||||
this.inputId = 'passwordRequiredInput';
|
||||
this.inputErrorClass = 'error';
|
||||
this.isOpened = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registering dialog listeners
|
||||
* @private
|
||||
*/
|
||||
_registerListeners() {
|
||||
let el = document.getElementById(this.inputId);
|
||||
el.addEventListener('keypress', this._hideError.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method returning dialog body
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_getBodyMessage() {
|
||||
return (
|
||||
`<div class="form-control">
|
||||
<label class="input-control__label"
|
||||
data-i18n="${this.labelKey}"></label>
|
||||
<input class="input-control__input input-control"
|
||||
name="lockKey" type="text"
|
||||
data-i18n="[placeholder]dialog.password"
|
||||
autofocus id="${this.inputId}">
|
||||
<p class="form-control__hint form-control__hint_error hide"
|
||||
id="${this.errorId}"
|
||||
data-i18n="${this.errorKey}"></p>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asking for a password
|
||||
* @returns {Promise}
|
||||
*/
|
||||
askForPassword() {
|
||||
if (!this.isOpened) {
|
||||
return this.open();
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
this._showError();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the dialog
|
||||
* @returns {Promise}
|
||||
*/
|
||||
open() {
|
||||
let { titleKey } = this;
|
||||
let msgString = this._getBodyMessage();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
let submitFunction = this._submitFunction.bind(this);
|
||||
let closeFunction = this._closeFunction.bind(this);
|
||||
|
||||
this._dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
msgString,
|
||||
leftButtonKey: "dialog.Ok",
|
||||
submitFunction,
|
||||
closeFunction,
|
||||
focus: ':input:first'
|
||||
});
|
||||
|
||||
this._registerListeners();
|
||||
this.isOpened = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit dialog callback
|
||||
* @param e - event
|
||||
* @param v - value
|
||||
* @param m - message
|
||||
* @param f - form
|
||||
* @private
|
||||
*/
|
||||
_submitFunction(e, v, m, f) {
|
||||
e.preventDefault();
|
||||
this._processInput(v, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing input in dialog
|
||||
* @param v - value
|
||||
* @param f - form
|
||||
* @private
|
||||
*/
|
||||
_processInput(v, f) {
|
||||
if (v && f.lockKey) {
|
||||
this.resolve(UIUtil.escapeHtml(f.lockKey));
|
||||
} else {
|
||||
this.reject(APP.UI.messageHandler.CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close dialog callback
|
||||
* @private
|
||||
*/
|
||||
_closeFunction(e, v, m, f) {
|
||||
this._processInput(v, f);
|
||||
this._hideError();
|
||||
this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method showing error hint
|
||||
* @private
|
||||
*/
|
||||
_showError() {
|
||||
let className = this.inputErrorClass;
|
||||
let input = document.getElementById(this.inputId);
|
||||
document.getElementById(this.errorId).classList.remove('hide');
|
||||
input.classList.add(className);
|
||||
input.select();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method hiding error hint
|
||||
* @private
|
||||
*/
|
||||
_hideError() {
|
||||
let className = this.inputErrorClass;
|
||||
document.getElementById(this.errorId).classList.add('hide');
|
||||
document.getElementById(this.inputId).classList.remove(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the dialog
|
||||
*/
|
||||
close() {
|
||||
if (this._dialog) {
|
||||
this._dialog.close();
|
||||
}
|
||||
this.isOpened = false;
|
||||
}
|
||||
}
|
||||
132
modules/UI/invite/RoomLocker.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/* global APP, JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import RequirePasswordDialog from './RequirePasswordDialog';
|
||||
|
||||
/**
|
||||
* Show notification that user cannot set password for the conference
|
||||
* because server doesn't support that.
|
||||
*/
|
||||
function notifyPasswordNotSupported () {
|
||||
logger.warn('room passwords not supported');
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.warning", "dialog.passwordNotSupported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification that setting password for the conference failed.
|
||||
* @param {Error} err error
|
||||
*/
|
||||
function notifyPasswordFailed(err) {
|
||||
logger.warn('setting password failed', err);
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.lockTitle", "dialog.lockMessage");
|
||||
}
|
||||
|
||||
const ConferenceErrors = JitsiMeetJS.errors.conference;
|
||||
|
||||
/**
|
||||
* Create new RoomLocker for the conference.
|
||||
* It allows to set or remove password for the conference,
|
||||
* or ask for required password.
|
||||
* @returns {RoomLocker}
|
||||
*/
|
||||
export default function createRoomLocker (room) {
|
||||
let password;
|
||||
let requirePasswordDialog = new RequirePasswordDialog();
|
||||
/**
|
||||
* If the room was locked from someone other than us, we indicate it with
|
||||
* this property in order to have correct roomLocker state of isLocked.
|
||||
* @type {boolean} whether room is locked, but not from us.
|
||||
*/
|
||||
let lockedElsewhere = false;
|
||||
|
||||
/**
|
||||
* @class RoomLocker
|
||||
*/
|
||||
return {
|
||||
get isLocked () {
|
||||
return !!password || lockedElsewhere;
|
||||
},
|
||||
|
||||
get password () {
|
||||
return password;
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows to set new password
|
||||
* @param newPass
|
||||
* @returns {Promise.<TResult>}
|
||||
*/
|
||||
lock (newPass) {
|
||||
return room.lock(newPass).then(() => {
|
||||
password = newPass;
|
||||
// If the password is undefined this means that we're removing
|
||||
// it for everyone.
|
||||
if (!password)
|
||||
lockedElsewhere = false;
|
||||
}).catch(function (err) {
|
||||
logger.error(err);
|
||||
if (err === ConferenceErrors.PASSWORD_NOT_SUPPORTED) {
|
||||
notifyPasswordNotSupported();
|
||||
} else {
|
||||
notifyPasswordFailed(err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets that the room is locked from another user, not us.
|
||||
* @param {boolean} value locked/unlocked state
|
||||
*/
|
||||
set lockedElsewhere (value) {
|
||||
lockedElsewhere = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether room is locked from someone else.
|
||||
* @returns {boolean} whether room is not locked locally,
|
||||
* but it is still locked.
|
||||
*/
|
||||
get lockedElsewhere () {
|
||||
return lockedElsewhere;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the password. Can be useful when room
|
||||
* has been unlocked from elsewhere and we can use
|
||||
* this method for sync the pass
|
||||
*/
|
||||
resetPassword() {
|
||||
password = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Asks user for required conference password.
|
||||
*/
|
||||
requirePassword () {
|
||||
return requirePasswordDialog.askForPassword().then(
|
||||
newPass => { password = newPass; }
|
||||
).catch(
|
||||
reason => {
|
||||
// user canceled, no pass was entered.
|
||||
// clear, as if we use the same instance several times
|
||||
// pass stays between attempts
|
||||
password = null;
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides require password dialog
|
||||
*/
|
||||
hideRequirePasswordDialog() {
|
||||
if (requirePasswordDialog.isOpened) {
|
||||
requirePasswordDialog.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -20,8 +20,7 @@ import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from '../util/UIUtil';
|
||||
import VideoLayout from '../videolayout/VideoLayout';
|
||||
import Feedback from '../feedback/Feedback.js';
|
||||
|
||||
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
||||
import Toolbar from '../toolbars/Toolbar';
|
||||
|
||||
/**
|
||||
* The dialog for user input.
|
||||
@@ -35,10 +34,8 @@ let dialog = null;
|
||||
* @private
|
||||
*/
|
||||
function _isRecordingButtonEnabled() {
|
||||
return (
|
||||
interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
|
||||
&& config.enableRecording
|
||||
&& APP.conference.isRecordingSupported());
|
||||
return interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
|
||||
&& config.enableRecording && APP.conference.isRecordingSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +64,7 @@ function _requestLiveStreamId() {
|
||||
name="streamId" type="text"
|
||||
data-i18n="[placeholder]dialog.streamKey"
|
||||
autofocus><div style="text-align: right">
|
||||
<a class="helper-link" target="_new"
|
||||
<a class="helper-link" target="_new"
|
||||
href="${interfaceConfig.LIVE_STREAMING_HELP_LINK}">`
|
||||
+ streamIdHelp
|
||||
+ `</a></div>`,
|
||||
@@ -131,7 +128,7 @@ function _requestLiveStreamId() {
|
||||
* Request recording token from the user.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _requestRecordingToken() {
|
||||
function _requestRecordingToken () {
|
||||
let titleKey = "dialog.recordingToken";
|
||||
let messageString = (
|
||||
`<input name="recordingToken" type="text"
|
||||
@@ -166,7 +163,7 @@ function _requestRecordingToken() {
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
function _showStopRecordingPrompt(recordingType) {
|
||||
function _showStopRecordingPrompt (recordingType) {
|
||||
var title;
|
||||
var message;
|
||||
var buttonKey;
|
||||
@@ -181,13 +178,19 @@ function _showStopRecordingPrompt(recordingType) {
|
||||
buttonKey = "dialog.stopRecording";
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey: title,
|
||||
msgKey: message,
|
||||
leftButtonKey: buttonKey,
|
||||
submitFunction: (e, v) => (v ? resolve : reject)(),
|
||||
closeFunction: () => {
|
||||
submitFunction: function(e,v) {
|
||||
if (v) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
},
|
||||
closeFunction: function () {
|
||||
dialog = null;
|
||||
}
|
||||
});
|
||||
@@ -196,9 +199,6 @@ function _showStopRecordingPrompt(recordingType) {
|
||||
|
||||
/**
|
||||
* Moves the element given by {selector} to the top right corner of the screen.
|
||||
* Set additional classes that can be used to style the selector relative to the
|
||||
* state of the filmstrip.
|
||||
*
|
||||
* @param selector the selector for the element to move
|
||||
* @param move {true} to move the element, {false} to move it back to its intial
|
||||
* position
|
||||
@@ -211,20 +211,6 @@ function moveToCorner(selector, move) {
|
||||
selector.addClass(moveToCornerClass);
|
||||
else if (!move && containsClass)
|
||||
selector.removeClass(moveToCornerClass);
|
||||
|
||||
const {
|
||||
remoteVideosVisible,
|
||||
visible
|
||||
} = APP.store.getState()['features/filmstrip'];
|
||||
const filmstripWasHidden = selector.hasClass('without-filmstrip');
|
||||
const filmstipIsOpening = filmstripWasHidden && visible;
|
||||
selector.toggleClass('opening', filmstipIsOpening);
|
||||
|
||||
selector.toggleClass('with-filmstrip', visible);
|
||||
selector.toggleClass('without-filmstrip', !visible);
|
||||
|
||||
selector.toggleClass('with-remote-videos', remoteVideosVisible);
|
||||
selector.toggleClass('without-remote-videos', !remoteVideosVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,12 +249,35 @@ var Recording = {
|
||||
/**
|
||||
* Initializes the recording UI.
|
||||
*/
|
||||
init(eventEmitter, recordingType) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.recordingType = recordingType;
|
||||
init (emitter, recordingType) {
|
||||
this.eventEmitter = emitter;
|
||||
|
||||
this.updateRecordingState(APP.conference.getRecordingState());
|
||||
|
||||
this.initRecordingButton(recordingType);
|
||||
|
||||
// If I am a recorder then I publish my recorder custom role to notify
|
||||
// everyone.
|
||||
if (config.iAmRecorder) {
|
||||
VideoLayout.enableDeviceAvailabilityIcons(
|
||||
APP.conference.getMyUserId(), false);
|
||||
VideoLayout.setLocalVideoVisible(false);
|
||||
Feedback.enableFeedback(false);
|
||||
Toolbar.enable(false);
|
||||
APP.UI.messageHandler.enableNotifications(false);
|
||||
APP.UI.messageHandler.enablePopups(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise the recording button.
|
||||
*/
|
||||
initRecordingButton(recordingType) {
|
||||
let selector = $('#toolbar_button_record');
|
||||
|
||||
let button = selector.get(0);
|
||||
UIUtil.setTooltip(button, 'liveStreaming.buttonTooltip', 'right');
|
||||
|
||||
if (recordingType === 'jibri') {
|
||||
this.baseClass = "fa fa-play-circle";
|
||||
this.recordingTitle = "dialog.liveStreaming";
|
||||
@@ -294,48 +303,101 @@ var Recording = {
|
||||
this.recordingBusy = "liveStreaming.busy";
|
||||
}
|
||||
|
||||
// XXX Due to the React-ification of Toolbox, the HTMLElement with id
|
||||
// toolbar_button_record may not exist yet.
|
||||
$(document).on(
|
||||
'click',
|
||||
'#toolbar_button_record',
|
||||
ev => this._onToolbarButtonClick(ev));
|
||||
|
||||
// If I am a recorder then I publish my recorder custom role to notify
|
||||
// everyone.
|
||||
if (config.iAmRecorder) {
|
||||
VideoLayout.enableDeviceAvailabilityIcons(
|
||||
APP.conference.getMyUserId(), false);
|
||||
VideoLayout.setLocalVideoVisible(false);
|
||||
Feedback.enableFeedback(false);
|
||||
APP.store.dispatch(setToolboxEnabled(false));
|
||||
APP.UI.messageHandler.enableNotifications(false);
|
||||
APP.UI.messageHandler.enablePopups(false);
|
||||
}
|
||||
|
||||
this.eventEmitter.addListener(UIEvents.UPDATED_FILMSTRIP_DISPLAY, () =>{
|
||||
this._updateStatusLabel();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise the recording button.
|
||||
*/
|
||||
initRecordingButton() {
|
||||
const selector = $('#toolbar_button_record');
|
||||
|
||||
UIUtil.setTooltip(selector, 'liveStreaming.buttonTooltip', 'right');
|
||||
|
||||
selector.addClass(this.baseClass);
|
||||
selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
|
||||
APP.translation.translateElement(selector);
|
||||
|
||||
var self = this;
|
||||
selector.click(function () {
|
||||
if (dialog)
|
||||
return;
|
||||
JitsiMeetJS.analytics.sendEvent('recording.clicked');
|
||||
switch (self.currentState) {
|
||||
case Status.ON:
|
||||
case Status.RETRYING:
|
||||
case Status.PENDING: {
|
||||
_showStopRecordingPrompt(recordingType).then(
|
||||
() => {
|
||||
self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.stopped');
|
||||
},
|
||||
() => {});
|
||||
break;
|
||||
}
|
||||
case Status.AVAILABLE:
|
||||
case Status.OFF: {
|
||||
if (recordingType === 'jibri')
|
||||
_requestLiveStreamId().then((streamId) => {
|
||||
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
|
||||
{streamId: streamId});
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.started');
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.canceled');
|
||||
}
|
||||
);
|
||||
else {
|
||||
if (self.predefinedToken) {
|
||||
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
|
||||
{token: self.predefinedToken});
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.started');
|
||||
return;
|
||||
}
|
||||
|
||||
_requestRecordingToken().then((token) => {
|
||||
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
|
||||
{token: token});
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.started');
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.canceled');
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Status.BUSY: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
self.recordingTitle,
|
||||
self.recordingBusy,
|
||||
null,
|
||||
function () {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
self.recordingTitle,
|
||||
self.recordingUnavailable,
|
||||
null,
|
||||
function () {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows or hides the 'recording' button.
|
||||
* @param show {true} to show the recording button, {false} to hide it
|
||||
*/
|
||||
showRecordingButton(show) {
|
||||
showRecordingButton (show) {
|
||||
let shouldShow = show && _isRecordingButtonEnabled();
|
||||
let id = 'toolbar_button_record';
|
||||
|
||||
@@ -362,7 +424,7 @@ var Recording = {
|
||||
* Sets the state of the recording button.
|
||||
* @param recordingState gives us the current recording state
|
||||
*/
|
||||
updateRecordingUI(recordingState) {
|
||||
updateRecordingUI (recordingState) {
|
||||
|
||||
let oldState = this.currentState;
|
||||
this.currentState = recordingState;
|
||||
@@ -428,7 +490,7 @@ var Recording = {
|
||||
},
|
||||
// checks whether recording is enabled and whether we have params
|
||||
// to start automatically recording
|
||||
checkAutoRecord() {
|
||||
checkAutoRecord () {
|
||||
if (_isRecordingButtonEnabled && config.autoRecord) {
|
||||
this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
|
||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
|
||||
@@ -451,90 +513,6 @@ var Recording = {
|
||||
APP.translation.translateElement(labelSelector);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles {@code click} on {@code toolbar_button_record}.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToolbarButtonClick() {
|
||||
if (dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
JitsiMeetJS.analytics.sendEvent('recording.clicked');
|
||||
switch (this.currentState) {
|
||||
case Status.ON:
|
||||
case Status.RETRYING:
|
||||
case Status.PENDING: {
|
||||
_showStopRecordingPrompt(this.recordingType).then(
|
||||
() => {
|
||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
||||
JitsiMeetJS.analytics.sendEvent('recording.stopped');
|
||||
},
|
||||
() => {});
|
||||
break;
|
||||
}
|
||||
case Status.AVAILABLE:
|
||||
case Status.OFF: {
|
||||
if (this.recordingType === 'jibri')
|
||||
_requestLiveStreamId().then(streamId => {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ streamId });
|
||||
JitsiMeetJS.analytics.sendEvent('recording.started');
|
||||
}).catch(reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent('recording.canceled');
|
||||
});
|
||||
else {
|
||||
if (this.predefinedToken) {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token: this.predefinedToken });
|
||||
JitsiMeetJS.analytics.sendEvent('recording.started');
|
||||
return;
|
||||
}
|
||||
|
||||
_requestRecordingToken().then((token) => {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token });
|
||||
JitsiMeetJS.analytics.sendEvent('recording.started');
|
||||
}).catch(reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent('recording.canceled');
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Status.BUSY: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
this.recordingTitle,
|
||||
this.recordingBusy,
|
||||
null,
|
||||
() => {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
this.recordingTitle,
|
||||
this.recordingUnavailable,
|
||||
null,
|
||||
() => {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the toggled state of the recording toolbar button.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* global $, APP */
|
||||
|
||||
/* jshint -W101 */
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
|
||||
/**
|
||||
@@ -22,26 +22,22 @@ function onAvatarVisible(shown) {
|
||||
*/
|
||||
class RingOverlay {
|
||||
/**
|
||||
*
|
||||
* @param callee The callee (Object) as defined by the JWT support.
|
||||
* @param {boolean} disableRinging if true the ringing sound wont be played.
|
||||
* @param callee instance of User class from TokenData.js
|
||||
* @param {boolean} disableRingingSound if true the ringing sound wont be played.
|
||||
*/
|
||||
constructor(callee, disableRinging) {
|
||||
constructor(callee, disableRingingSound) {
|
||||
this._containerId = 'ringOverlay';
|
||||
this._audioContainerId = 'ringOverlayRinging';
|
||||
this.isRinging = true;
|
||||
this.callee = callee;
|
||||
this.disableRinging = disableRinging;
|
||||
this.disableRingingSound = disableRingingSound;
|
||||
this.render();
|
||||
if (!disableRinging)
|
||||
if(!disableRingingSound)
|
||||
this._initAudio();
|
||||
this._timeout
|
||||
= setTimeout(
|
||||
() => {
|
||||
this.destroy();
|
||||
this.render();
|
||||
},
|
||||
30000);
|
||||
this._timeout = setTimeout(() => {
|
||||
this.destroy();
|
||||
this.render();
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +46,7 @@ class RingOverlay {
|
||||
_initAudio() {
|
||||
this.audio = document.getElementById(this._audioContainerId);
|
||||
this.audio.play();
|
||||
this.interval = setInterval(() => this.audio.play(), 5000);
|
||||
this._setAudioTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,8 +57,7 @@ class RingOverlay {
|
||||
*/
|
||||
_changeBackground(solid) {
|
||||
const container = $("#" + this._containerId);
|
||||
|
||||
if (solid) {
|
||||
if(solid) {
|
||||
container.addClass("solidBG");
|
||||
} else {
|
||||
container.removeClass("solidBG");
|
||||
@@ -75,17 +70,16 @@ class RingOverlay {
|
||||
_getHtmlStr(callee) {
|
||||
let callingLabel = this.isRinging ? "<p>Calling...</p>" : "";
|
||||
let callerStateLabel = this.isRinging ? "" : " isn't available";
|
||||
let audioHTML = this.disableRinging ? ""
|
||||
let audioHTML = this.disableRingingSound ? ""
|
||||
: "<audio id=\"" + this._audioContainerId
|
||||
+ "\" src=\"./sounds/ring.ogg\" />";
|
||||
|
||||
return `
|
||||
<div id="${this._containerId}" class='ringing' >
|
||||
<div class='ringing__content'>
|
||||
${callingLabel}
|
||||
<img class='ringing__avatar' src="${callee.avatarUrl}" />
|
||||
<img class='ringing__avatar' src="${callee.getAvatarUrl()}" />
|
||||
<div class="ringing__caller-info">
|
||||
<p>${callee.name}${callerStateLabel}</p>
|
||||
<p>${callee.getName()}${callerStateLabel}</p>
|
||||
</div>
|
||||
</div>
|
||||
${audioHTML}
|
||||
@@ -125,28 +119,34 @@ class RingOverlay {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
if (this._timeout) {
|
||||
if(this._timeout) {
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interval that is going to play the ringing sound.
|
||||
*/
|
||||
_setAudioTimeout() {
|
||||
this.interval = setInterval( () => {
|
||||
this.audio.play();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Shows the ring overlay for the passed callee.
|
||||
*
|
||||
* @param {Object} callee - The callee. Object containing data about
|
||||
* callee.
|
||||
* @param {boolean} disableRinging - If true the ringing sound won't be
|
||||
* played.
|
||||
* @returns {void}
|
||||
* @param callee {class User} the callee. Instance of User class from
|
||||
* TokenData.js
|
||||
* @param {boolean} disableRingingSound if true the ringing sound wont be played.
|
||||
*/
|
||||
show(callee, disableRinging = false) {
|
||||
if (overlay) {
|
||||
show(callee, disableRingingSound = false) {
|
||||
if(overlay) {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
overlay = new RingOverlay(callee, disableRinging);
|
||||
overlay = new RingOverlay(callee, disableRingingSound);
|
||||
APP.UI.addListener(UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
|
||||
onAvatarVisible);
|
||||
},
|
||||
@@ -156,7 +156,7 @@ export default {
|
||||
* overlay.
|
||||
*/
|
||||
hide() {
|
||||
if (!overlay) {
|
||||
if(!overlay) {
|
||||
return false;
|
||||
}
|
||||
overlay.destroy();
|
||||
@@ -172,7 +172,7 @@ export default {
|
||||
* @returns {boolean} true if the ring overlay is currently displayed or
|
||||
* false otherwise.
|
||||
*/
|
||||
isVisible() {
|
||||
isVisible () {
|
||||
return overlay !== null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,9 +8,8 @@ import UIEvents from '../../../service/UI/UIEvents';
|
||||
import VideoLayout from "../videolayout/VideoLayout";
|
||||
import LargeContainer from '../videolayout/LargeContainer';
|
||||
import SmallVideo from '../videolayout/SmallVideo';
|
||||
import Filmstrip from '../videolayout/Filmstrip';
|
||||
|
||||
import { dockToolbox, showToolbox } from '../../../react/features/toolbox';
|
||||
import FilmStrip from '../videolayout/FilmStrip';
|
||||
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
||||
|
||||
export const SHARED_VIDEO_CONTAINER_TYPE = "sharedvideo";
|
||||
|
||||
@@ -456,9 +455,6 @@ export default class SharedVideoManager {
|
||||
// revert to original behavior (prevents pausing
|
||||
// for participants not sharing the video to pause it)
|
||||
$("#sharedVideo").css("pointer-events","auto");
|
||||
|
||||
this.emitter.emit(
|
||||
UIEvents.UPDATE_SHARED_VIDEO, null, 'removed');
|
||||
});
|
||||
|
||||
this.url = null;
|
||||
@@ -582,7 +578,7 @@ class SharedVideoContainer extends LargeContainer {
|
||||
self.bodyBackground = document.body.style.background;
|
||||
document.body.style.background = 'black';
|
||||
this.$iframe.css({opacity: 1});
|
||||
APP.store.dispatch(dockToolbox(true));
|
||||
ToolbarToggler.dockToolbar(true);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@@ -590,7 +586,7 @@ class SharedVideoContainer extends LargeContainer {
|
||||
|
||||
hide () {
|
||||
let self = this;
|
||||
APP.store.dispatch(dockToolbox(false));
|
||||
ToolbarToggler.dockToolbar(false);
|
||||
return new Promise(resolve => {
|
||||
this.$iframe.fadeOut(300, () => {
|
||||
document.body.style.background = self.bodyBackground;
|
||||
@@ -601,7 +597,7 @@ class SharedVideoContainer extends LargeContainer {
|
||||
}
|
||||
|
||||
onHoverIn () {
|
||||
APP.store.dispatch(showToolbox());
|
||||
ToolbarToggler.showToolbar();
|
||||
}
|
||||
|
||||
get id () {
|
||||
@@ -609,7 +605,7 @@ class SharedVideoContainer extends LargeContainer {
|
||||
}
|
||||
|
||||
resize (containerWidth, containerHeight) {
|
||||
let height = containerHeight - Filmstrip.getFilmstripHeight();
|
||||
let height = containerHeight - FilmStrip.getFilmStripHeight();
|
||||
|
||||
let width = containerWidth;
|
||||
|
||||
@@ -659,7 +655,7 @@ SharedVideoThumb.prototype.createContainer = function (spanId) {
|
||||
avatar.src = "https://img.youtube.com/vi/" + this.url + "/0.jpg";
|
||||
container.appendChild(avatar);
|
||||
|
||||
var remotes = document.getElementById('filmstripRemoteVideosContainer');
|
||||
var remotes = document.getElementById('remoteVideos');
|
||||
return remotes.appendChild(container);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,27 +16,17 @@ const SideContainerToggler = {
|
||||
init(eventEmitter) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
|
||||
// We may not have a side toolbar container, for example, in
|
||||
// filmstrip-only mode.
|
||||
const sideToolbarContainer
|
||||
= document.getElementById("sideToolbarContainer");
|
||||
|
||||
if (!sideToolbarContainer)
|
||||
return;
|
||||
|
||||
// Adds a listener for the animationend event that would take care of
|
||||
// hiding all internal containers when the extendedToolbarPanel is
|
||||
// Adds a listener for the animation end event that would take care
|
||||
// of hiding all internal containers when the extendedToolbarPanel is
|
||||
// closed.
|
||||
sideToolbarContainer.addEventListener(
|
||||
"animationend",
|
||||
function(e) {
|
||||
if (e.animationName === "slideOutExt")
|
||||
document.getElementById("sideToolbarContainer")
|
||||
.addEventListener("animationend", function(e) {
|
||||
if(e.animationName === "slideOutExt")
|
||||
$("#sideToolbarContainer").children().each(function() {
|
||||
if ($(this).hasClass("show"))
|
||||
SideContainerToggler.hideInnerContainer($(this));
|
||||
});
|
||||
},
|
||||
false);
|
||||
}, false);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -144,4 +134,4 @@ const SideContainerToggler = {
|
||||
}
|
||||
};
|
||||
|
||||
export default SideContainerToggler;
|
||||
export default SideContainerToggler;
|
||||
@@ -6,26 +6,19 @@ import UIUtil from '../util/UIUtil';
|
||||
|
||||
const SidePanels = {
|
||||
init (eventEmitter) {
|
||||
// Initialize chat
|
||||
if (UIUtil.isButtonEnabled('chat')) {
|
||||
//Initialize chat
|
||||
if (UIUtil.isButtonEnabled('chat'))
|
||||
Chat.init(eventEmitter);
|
||||
}
|
||||
|
||||
// Initialize settings
|
||||
if (UIUtil.isButtonEnabled('settings')) {
|
||||
//Initialize settings
|
||||
if (UIUtil.isButtonEnabled('settings'))
|
||||
SettingsMenu.init(eventEmitter);
|
||||
}
|
||||
|
||||
// Initialize profile
|
||||
if (UIUtil.isButtonEnabled('profile')) {
|
||||
//Initialize profile
|
||||
if (UIUtil.isButtonEnabled('profile'))
|
||||
Profile.init(eventEmitter);
|
||||
}
|
||||
|
||||
// Initialize contact list view
|
||||
if (UIUtil.isButtonEnabled('contacts')) {
|
||||
//Initialize contact list view
|
||||
if (UIUtil.isButtonEnabled('contacts'))
|
||||
ContactListView.init();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default SidePanels;
|
||||
export default SidePanels;
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import {processReplacements, linkify} from './Replacement';
|
||||
import CommandsProcessor from './Commands';
|
||||
import ToolbarToggler from '../../toolbars/ToolbarToggler';
|
||||
import VideoLayout from "../../videolayout/VideoLayout";
|
||||
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
@@ -9,8 +10,6 @@ import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import { smileys } from './smileys';
|
||||
|
||||
import { dockToolbox, setSubject } from '../../../../react/features/toolbox';
|
||||
|
||||
let unreadMessages = 0;
|
||||
const sidePanelsContainerId = 'sideToolbarContainer';
|
||||
const htmlStr = `
|
||||
@@ -25,12 +24,12 @@ const htmlStr = `
|
||||
</div>
|
||||
|
||||
<div id="chatconversation"></div>
|
||||
<audio id="chatNotification" src="sounds/incomingMessage.wav"
|
||||
<audio id="chatNotification" src="sounds/incomingMessage.wav"
|
||||
preload="auto"></audio>
|
||||
<textarea id="usermsg" autofocus
|
||||
<textarea id="usermsg" autofocus
|
||||
data-i18n="[placeholder]chat.messagebox"></textarea>
|
||||
<div id="smileysarea">
|
||||
<div id="smileys">
|
||||
<div id="smileys" id="toggle_smileys">
|
||||
<img src="images/smile.svg"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,41 +49,30 @@ var CHAT_CONTAINER_ID = "chat_container";
|
||||
* Updates visual notification, indicating that a message has arrived.
|
||||
*/
|
||||
function updateVisualNotification() {
|
||||
// XXX The rewrite of the toolbar in React delayed the availability of the
|
||||
// element unreadMessages. In order to work around the delay, I introduced
|
||||
// and utilized unreadMsgSelector in addition to unreadMsgElement.
|
||||
const unreadMsgSelector = $('#unreadMessages');
|
||||
const unreadMsgElement
|
||||
= unreadMsgSelector.length > 0 ? unreadMsgSelector[0] : undefined;
|
||||
var unreadMsgElement = document.getElementById('unreadMessages');
|
||||
|
||||
if (unreadMessages && unreadMsgElement) {
|
||||
if (unreadMessages) {
|
||||
unreadMsgElement.innerHTML = unreadMessages.toString();
|
||||
|
||||
APP.store.dispatch(dockToolbox(true));
|
||||
ToolbarToggler.dockToolbar(true);
|
||||
|
||||
const chatButtonElement
|
||||
var chatButtonElement
|
||||
= document.getElementById('toolbar_button_chat');
|
||||
const leftIndent
|
||||
= (UIUtil.getTextWidth(chatButtonElement)
|
||||
- UIUtil.getTextWidth(unreadMsgElement))
|
||||
/ 2;
|
||||
const topIndent
|
||||
= (UIUtil.getTextHeight(chatButtonElement)
|
||||
- UIUtil.getTextHeight(unreadMsgElement))
|
||||
/ 2
|
||||
- 5;
|
||||
var leftIndent = (UIUtil.getTextWidth(chatButtonElement) -
|
||||
UIUtil.getTextWidth(unreadMsgElement)) / 2;
|
||||
var topIndent = (UIUtil.getTextHeight(chatButtonElement) -
|
||||
UIUtil.getTextHeight(unreadMsgElement)) / 2 - 5;
|
||||
|
||||
unreadMsgElement.setAttribute(
|
||||
'style',
|
||||
'top:' + topIndent + '; left:' + leftIndent + ';');
|
||||
'style',
|
||||
'top:' + topIndent +
|
||||
'; left:' + leftIndent + ';');
|
||||
}
|
||||
else {
|
||||
unreadMsgSelector.html('');
|
||||
unreadMsgElement.innerHTML = '';
|
||||
}
|
||||
|
||||
if (unreadMsgElement) {
|
||||
unreadMsgSelector.parent()[unreadMessages > 0 ? 'show' : 'hide']();
|
||||
}
|
||||
$(unreadMsgElement).parent()[unreadMessages > 0 ? 'show' : 'hide']();
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +179,7 @@ var Chat = {
|
||||
Chat.setChatConversationMode(true);
|
||||
}
|
||||
|
||||
$("#smileys").click(function() {
|
||||
$("#toggle_smileys").click(function() {
|
||||
Chat.toggleSmileys();
|
||||
});
|
||||
|
||||
@@ -239,7 +227,7 @@ var Chat = {
|
||||
// Undock the toolbar when the chat is shown and if we're in a
|
||||
// video mode.
|
||||
if (VideoLayout.isLargeVideoVisible()) {
|
||||
APP.store.dispatch(dockToolbox(false));
|
||||
ToolbarToggler.dockToolbar(false);
|
||||
}
|
||||
|
||||
// if we are in conversation mode focus on the text input
|
||||
@@ -320,9 +308,10 @@ var Chat = {
|
||||
subject = subject.trim();
|
||||
}
|
||||
|
||||
const html = linkify(UIUtil.escapeHtml(subject));
|
||||
|
||||
APP.store.dispatch(setSubject(html));
|
||||
let subjectId = 'subject';
|
||||
let html = linkify(UIUtil.escapeHtml(subject));
|
||||
$(`#${subjectId}`).html(html);
|
||||
UIUtil.setVisible(subjectId, subject && subject.length > 0);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,12 +19,14 @@ class ContactList {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current conference is locked.
|
||||
* Is locked flag.
|
||||
* Delegates to Invite module
|
||||
* TO FIX: find a better way to access the IS LOCKED state of the invite.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isLocked() {
|
||||
return APP.store.getState()['features/base/conference'].locked;
|
||||
return APP.conference.invite.isLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,9 +36,9 @@ class ContactList {
|
||||
* @param isLocal
|
||||
*/
|
||||
addContact(id, isLocal) {
|
||||
const exists = this.contacts.some(el => el.id === id);
|
||||
let isExist = this.contacts.some((el) => el.id === id);
|
||||
|
||||
if (!exists) {
|
||||
if (!isExist) {
|
||||
let newContact = new Contact({ id, isLocal });
|
||||
this.contacts.push(newContact);
|
||||
APP.UI.emitEvent(UIEvents.CONTACT_ADDED, { id, isLocal });
|
||||
@@ -90,4 +92,4 @@ class ContactList {
|
||||
}
|
||||
}
|
||||
|
||||
export default ContactList;
|
||||
export default ContactList;
|
||||
@@ -1,13 +1,10 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
import { openInviteDialog } from '../../../../react/features/invite';
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import Avatar from '../../avatar/Avatar';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
let numberOfContacts = 0;
|
||||
const sidePanelsContainerId = 'sideToolbarContainer';
|
||||
const htmlStr = `
|
||||
@@ -99,7 +96,7 @@ var ContactListView = {
|
||||
this.model = model;
|
||||
this.addInviteButton();
|
||||
this.registerListeners();
|
||||
this.setLockDisplay(false);
|
||||
this.toggleLock();
|
||||
},
|
||||
/**
|
||||
* Adds layout for invite button
|
||||
@@ -111,9 +108,8 @@ var ContactListView = {
|
||||
.insertAdjacentHTML('afterend', this.getInviteButtonLayout());
|
||||
|
||||
APP.translation.translateElement($(container));
|
||||
|
||||
$(document).on('click', '#addParticipantsBtn', () => {
|
||||
APP.store.dispatch(openInviteDialog());
|
||||
APP.UI.emitEvent(UIEvents.INVITE_CLICKED);
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -129,8 +125,8 @@ var ContactListView = {
|
||||
|
||||
return (
|
||||
`<div class="sideToolbarBlock first">
|
||||
<button id="addParticipantsBtn"
|
||||
data-i18n="${key}"
|
||||
<button id="addParticipantsBtn"
|
||||
data-i18n="${key}"
|
||||
class="${classes}"></button>
|
||||
<div>
|
||||
${lockedHtml}
|
||||
@@ -162,7 +158,7 @@ var ContactListView = {
|
||||
let displayNameChange = this.onDisplayNameChange.bind(this);
|
||||
|
||||
APP.UI.addListener( UIEvents.TOGGLE_ROOM_LOCK,
|
||||
this.setLockDisplay.bind(this));
|
||||
this.toggleLock.bind(this));
|
||||
APP.UI.addListener( UIEvents.CONTACT_ADDED,
|
||||
this.onAddContact.bind(this));
|
||||
|
||||
@@ -170,28 +166,21 @@ var ContactListView = {
|
||||
APP.UI.addListener(UIEvents.USER_AVATAR_CHANGED, changeAvatar);
|
||||
APP.UI.addListener(UIEvents.DISPLAY_NAME_CHANGED, displayNameChange);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the view according to the passed in lock state.
|
||||
*
|
||||
* @param {boolean} locked - True to display the locked UI state or false to
|
||||
* display the unlocked UI state.
|
||||
* Updating the view according the model
|
||||
* @param type {String} type of change
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setLockDisplay(locked) {
|
||||
let hideKey, showKey;
|
||||
toggleLock() {
|
||||
let isLocked = this.model.isLocked();
|
||||
let showKey = isLocked ? this.lockKey : this.unlockKey;
|
||||
let hideKey = !isLocked ? this.lockKey : this.unlockKey;
|
||||
let showId = `contactList${showKey}`;
|
||||
let hideId = `contactList${hideKey}`;
|
||||
|
||||
if (locked) {
|
||||
hideKey = this.unlockKey;
|
||||
showKey = this.lockKey;
|
||||
} else {
|
||||
hideKey = this.lockKey;
|
||||
showKey = this.unlockKey;
|
||||
}
|
||||
|
||||
$(`#contactList${hideKey}`).hide();
|
||||
$(`#contactList${showKey}`).show();
|
||||
$(`#${showId}`).show();
|
||||
$(`#${hideId}`).hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates if the chat is currently visible.
|
||||
*
|
||||
@@ -286,4 +275,4 @@ var ContactListView = {
|
||||
}
|
||||
};
|
||||
|
||||
export default ContactListView;
|
||||
export default ContactListView;
|
||||