Compare commits
93 Commits
2518
...
reactions-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
988ce595bb | ||
|
|
8cc6e43b59 | ||
|
|
bd301403c4 | ||
|
|
d481c6f736 | ||
|
|
ba94ba30c5 | ||
|
|
5305557ce5 | ||
|
|
c9d8b5c827 | ||
|
|
0ad1c88cd2 | ||
|
|
78fbfba573 | ||
|
|
9e53d40b9c | ||
|
|
aa314c10ac | ||
|
|
62c9762793 | ||
|
|
d7dddb2509 | ||
|
|
83243d5980 | ||
|
|
6e05cab46e | ||
|
|
7954d5fd39 | ||
|
|
158cadf4f9 | ||
|
|
f35578c803 | ||
|
|
c087e90099 | ||
|
|
da0ae73d10 | ||
|
|
b4d44f367d | ||
|
|
083f6b400b | ||
|
|
dd5ae49217 | ||
|
|
6a9e6db3be | ||
|
|
c4468cb7b8 | ||
|
|
80c4205fb8 | ||
|
|
aa9efd6f69 | ||
|
|
6f8f64ba48 | ||
|
|
1c3cef1eed | ||
|
|
2720c76e4d | ||
|
|
4ab34589c8 | ||
|
|
ed36132e94 | ||
|
|
f43687944c | ||
|
|
dda0ea0ba9 | ||
|
|
e1f967869a | ||
|
|
8673083829 | ||
|
|
b52e584327 | ||
|
|
4c65262a87 | ||
|
|
7ce670df0c | ||
|
|
f5f341ca9e | ||
|
|
f29fbb6757 | ||
|
|
e5e3c6c6c4 | ||
|
|
dc92fb5073 | ||
|
|
2478176f23 | ||
|
|
12ec982067 | ||
|
|
c9e3e5052d | ||
|
|
22401614a7 | ||
|
|
762f529f1d | ||
|
|
1f6b743bec | ||
|
|
48f4317adb | ||
|
|
75f6786588 | ||
|
|
3ec4d67a99 | ||
|
|
a23eec55e8 | ||
|
|
bdf2ecfe4b | ||
|
|
fc36759114 | ||
|
|
9c2849a663 | ||
|
|
98ff20a026 | ||
|
|
b04661b40b | ||
|
|
112c856850 | ||
|
|
410dc132e1 | ||
|
|
b7f950f5f7 | ||
|
|
7ad875e735 | ||
|
|
41aa704e1f | ||
|
|
e00ea353e8 | ||
|
|
6f93424d7c | ||
|
|
292f3ab1bd | ||
|
|
a3cb081609 | ||
|
|
64c5ae1c48 | ||
|
|
259004b8bf | ||
|
|
4fea22676b | ||
|
|
05a492241f | ||
|
|
8ec4697a27 | ||
|
|
a357b0cf14 | ||
|
|
9eff669b0b | ||
|
|
c7714959e6 | ||
|
|
e898527294 | ||
|
|
bfcd34358b | ||
|
|
871ef9ff0e | ||
|
|
178c8e02ff | ||
|
|
43ac039fd6 | ||
|
|
090f2f9ccb | ||
|
|
d08bbae770 | ||
|
|
30b51ff384 | ||
|
|
c109199e06 | ||
|
|
0ed31f0ae8 | ||
|
|
006e6cc851 | ||
|
|
0680c086df | ||
|
|
698ec1e2d7 | ||
|
|
e217c172f8 | ||
|
|
6146c12533 | ||
|
|
eb64b74493 | ||
|
|
1255b3349b | ||
|
|
2ea5ad68a5 |
@@ -56,8 +56,8 @@ suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
@@ -76,4 +76,4 @@ module.file_ext=.jsx
|
||||
module.file_ext=.json
|
||||
|
||||
[version]
|
||||
^0.56.0
|
||||
^0.57.0
|
||||
|
||||
3
Makefile
@@ -35,7 +35,7 @@ deploy-appbundle:
|
||||
$(BUILD_DIR)/device_selection_popup_bundle.min.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.js \
|
||||
$(BUILD_DIR)/alwaysontop.min.map \
|
||||
$(OUTPUT_DIR)/analytics.js \
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
@@ -43,6 +43,7 @@ deploy-lib-jitsi-meet:
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.js \
|
||||
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.map \
|
||||
$(LIBJITSIMEET_DIR)/connection_optimization/external_connect.js \
|
||||
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-css:
|
||||
|
||||
17
README.md
@@ -1,6 +1,6 @@
|
||||
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, scalable video conferences. You can see [Jitsi Meet in action](http://youtu.be/7vFUVClsNh0) here at the session #482 of the VoIP Users Conference.
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](#security) and scalable video conferences. You can see [Jitsi Meet in action](http://youtu.be/7vFUVClsNh0) here at the session #482 of the VoIP Users Conference.
|
||||
|
||||
You can also try it out yourself at https://meet.jit.si .
|
||||
|
||||
@@ -98,6 +98,21 @@ see our [guidelines for contributing](CONTRIBUTING.md).
|
||||
|
||||
Jitsi Meet provides a very flexible way of embedding it in external applications by using the [Jitsi Meet API](doc/api.md).
|
||||
|
||||
## Security
|
||||
WebRTC today does not provide a way of conducting multiparty conversations with
|
||||
end-to-end encryption. As a matter of fact, unless you consistently vocally
|
||||
compare DTLS fingerprints with your peers, the same goes for one-to-one calls.
|
||||
As a result when using a Jitsi Meet instance, your stream is encrypted on the
|
||||
network but decrypted on the machine that hosts the bridge.
|
||||
|
||||
The Jitsi Meet architecture allows you to deploy your own version, including
|
||||
all server components, and in that case your security guarantees will be roughly
|
||||
equivalent to these of a direct one-to-one WebRTC call. This is what's unique to
|
||||
Jitsi Meet in terms of security.
|
||||
|
||||
The [meet.jit.si](https://meet.jit.si) service is maintained by the Jitsi team
|
||||
at [Atlassian](https://atlassian.com).
|
||||
|
||||
## Mobile app
|
||||
Jitsi Meet is also available as a React Native app for Android and iOS.
|
||||
Instructions on how to build it can be found [here](doc/mobile.md).
|
||||
|
||||
146
analytics-ga.js
Normal file
@@ -0,0 +1,146 @@
|
||||
/* global ga */
|
||||
|
||||
(function(ctx) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function Analytics() {
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Google Analytics
|
||||
*/
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-319188-14', 'jit.si');
|
||||
ga('send', 'pageview');
|
||||
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the integer to use for a Google Analytics event's value field
|
||||
* from a lib-jitsi-meet analytics event.
|
||||
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||
* Analytics event.
|
||||
* @private
|
||||
*/
|
||||
Analytics.prototype._extractAction = function(event) {
|
||||
// Page events have a single 'name' field.
|
||||
if (event.type === 'page') {
|
||||
return event.name;
|
||||
}
|
||||
|
||||
// All other events have action, actionSubject, and source fields. All
|
||||
// three fields are required, and the often jitsi-meet and
|
||||
// lib-jitsi-meet use the same value when separate values are not
|
||||
// necessary (i.e. event.action == event.actionSubject).
|
||||
// Here we concatenate these three fields, but avoid adding the same
|
||||
// value twice, because it would only make the GA event's action harder
|
||||
// to read.
|
||||
let action = event.action;
|
||||
|
||||
if (event.actionSubject && event.actionSubject !== event.action) {
|
||||
// Intentionally use string concatenation as analytics needs to
|
||||
// work on IE but this file does not go through babel. For some
|
||||
// reason disabling this globally for the file does not have an
|
||||
// effect.
|
||||
// eslint-disable-next-line prefer-template
|
||||
action = event.actionSubject + '.' + action;
|
||||
}
|
||||
if (event.source && event.source !== event.action
|
||||
&& event.source !== event.action) {
|
||||
// eslint-disable-next-line prefer-template
|
||||
action = event.source + '.' + action;
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the integer to use for a Google Analytics event's value field
|
||||
* from a lib-jitsi-meet analytics event.
|
||||
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||
* Analytics event, or NaN if the lib-jitsi-meet event doesn't contain a
|
||||
* suitable value.
|
||||
* @private
|
||||
*/
|
||||
Analytics.prototype._extractValue = function(event) {
|
||||
let value = event && event.attributes && event.attributes.value;
|
||||
|
||||
// Try to extract an integer from the "value" attribute.
|
||||
value = Math.round(parseFloat(value));
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the string to use for a Google Analytics event's label field
|
||||
* from a lib-jitsi-meet analytics event.
|
||||
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||
* @returns {string} - The string to use for the 'label' of a Google
|
||||
* Analytics event.
|
||||
* @private
|
||||
*/
|
||||
Analytics.prototype._extractLabel = function(event) {
|
||||
let label = '';
|
||||
|
||||
// The label field is limited to 500B. We will concatenate all
|
||||
// attributes of the event, except the user agent because it may be
|
||||
// lengthy and is probably included from elsewhere.
|
||||
for (const property in event.attributes) {
|
||||
if (property !== 'permanent_user_agent'
|
||||
&& event.attributes.hasOwnProperty(property)) {
|
||||
// eslint-disable-next-line prefer-template
|
||||
label += property + '=' + event.attributes[property] + '&';
|
||||
}
|
||||
}
|
||||
|
||||
if (label.length > 0) {
|
||||
label = label.slice(0, -1);
|
||||
}
|
||||
|
||||
return label;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the entry point of the API. The function sends an event to
|
||||
* google analytics. The format of the event is described in
|
||||
* AnalyticsAdapter in lib-jitsi-meet.
|
||||
* @param {Object} event - the event in the format specified by
|
||||
* lib-jitsi-meet.
|
||||
*/
|
||||
Analytics.prototype.sendEvent = function(event) {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gaEvent = {
|
||||
'eventCategory': 'jitsi-meet',
|
||||
'eventAction': this._extractAction(event),
|
||||
'eventLabel': this._extractLabel(event)
|
||||
};
|
||||
const value = this._extractValue(event);
|
||||
|
||||
if (!isNaN(value)) {
|
||||
gaEvent.eventValue = value;
|
||||
}
|
||||
|
||||
ga('send', 'event', gaEvent);
|
||||
};
|
||||
|
||||
if (typeof ctx.JitsiMeetJS === 'undefined') {
|
||||
ctx.JitsiMeetJS = {};
|
||||
}
|
||||
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
|
||||
ctx.JitsiMeetJS.app = {};
|
||||
}
|
||||
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
|
||||
ctx.JitsiMeetJS.app.analyticsHandlers = [];
|
||||
}
|
||||
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
|
||||
})(window);
|
||||
/* eslint-enable prefer-template */
|
||||
47
analytics.js
@@ -1,47 +0,0 @@
|
||||
/* global ga */
|
||||
|
||||
(function(ctx) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function Analytics() {
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Google Analytics
|
||||
*/
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-319188-14', 'jit.si');
|
||||
ga('send', 'pageview');
|
||||
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
Analytics.prototype.sendEvent = function(action, data) {
|
||||
// empty label if missing value for it and add the value,
|
||||
// the value should be integer or null
|
||||
let value = data.value;
|
||||
|
||||
value = value ? Math.round(parseFloat(value)) : null;
|
||||
const label = data.label || '';
|
||||
|
||||
// Intentionally use string concatenation as analytics needs to work on
|
||||
// IE but this file does not go through babel.
|
||||
// eslint-disable-next-line prefer-template
|
||||
ga('send', 'event', 'jit.si', action + '.' + data.browserName,
|
||||
label, value);
|
||||
};
|
||||
|
||||
if (typeof ctx.JitsiMeetJS === 'undefined') {
|
||||
ctx.JitsiMeetJS = {};
|
||||
}
|
||||
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
|
||||
ctx.JitsiMeetJS.app = {};
|
||||
}
|
||||
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
|
||||
ctx.JitsiMeetJS.app.analyticsHandlers = [];
|
||||
}
|
||||
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
|
||||
})(window);
|
||||
@@ -43,6 +43,16 @@ Add the Maven repository
|
||||
`https://github.com/jitsi/jitsi-maven-repository/raw/master/releases` and the
|
||||
dependency `org.jitsi.react:jitsi-meet-sdk:1.9.0` into your `build.gradle`.
|
||||
|
||||
Add Java 1.8 compatibility support to your project by adding the following lines
|
||||
into your `build.gradle` file:
|
||||
|
||||
```
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet
|
||||
|
||||
@@ -31,6 +31,10 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
@@ -25,7 +27,7 @@ class AndroidSettingsModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void open() {
|
||||
public void open(Promise promise) {
|
||||
Context context = getReactApplicationContext();
|
||||
Intent intent = new Intent();
|
||||
|
||||
@@ -34,6 +36,15 @@ class AndroidSettingsModule extends ReactContextBaseJavaModule {
|
||||
intent.setData(
|
||||
Uri.fromParts("package", context.getPackageName(), null));
|
||||
|
||||
context.startActivity(intent);
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
// Some devices may give an error here.
|
||||
// https://developer.android.com/reference/android/provider/Settings.html#ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
promise.reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
134
conference.js
@@ -16,19 +16,13 @@ import UIUtil from './modules/UI/util/UIUtil';
|
||||
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import {
|
||||
CONFERENCE_AUDIO_INITIALLY_MUTED,
|
||||
CONFERENCE_SHARING_DESKTOP_START,
|
||||
CONFERENCE_SHARING_DESKTOP_STOP,
|
||||
CONFERENCE_VIDEO_INITIALLY_MUTED,
|
||||
DEVICE_LIST_CHANGED_AUDIO_MUTED,
|
||||
DEVICE_LIST_CHANGED_VIDEO_MUTED,
|
||||
SELECT_PARTICIPANT_FAILED,
|
||||
SETTINGS_CHANGE_DEVICE_AUDIO_OUT,
|
||||
SETTINGS_CHANGE_DEVICE_AUDIO_IN,
|
||||
SETTINGS_CHANGE_DEVICE_VIDEO,
|
||||
STREAM_SWITCH_DELAY,
|
||||
createDeviceChangedEvent,
|
||||
createScreenSharingEvent,
|
||||
createSelectParticipantFailedEvent,
|
||||
createStreamSwitchDelayEvent,
|
||||
createTrackMutedEvent,
|
||||
initAnalytics,
|
||||
sendAnalyticsEvent
|
||||
sendAnalytics
|
||||
} from './react/features/analytics';
|
||||
|
||||
import EventEmitter from 'events';
|
||||
@@ -97,7 +91,10 @@ import {
|
||||
import { statsEmitter } from './react/features/connection-indicator';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
import { maybeOpenFeedbackDialog } from './react/features/feedback';
|
||||
import {
|
||||
maybeOpenFeedbackDialog,
|
||||
submitFeedback
|
||||
} from './react/features/feedback';
|
||||
import {
|
||||
mediaPermissionPromptVisibilityChanged,
|
||||
suspendDetected
|
||||
@@ -741,14 +738,13 @@ export default {
|
||||
})
|
||||
.then(([ tracks, con ]) => {
|
||||
tracks.forEach(track => {
|
||||
if (track.isAudioTrack() && this.isLocalAudioMuted()) {
|
||||
sendAnalyticsEvent(CONFERENCE_AUDIO_INITIALLY_MUTED);
|
||||
logger.log('Audio mute: initially muted');
|
||||
track.mute();
|
||||
} else if (track.isVideoTrack()
|
||||
&& this.isLocalVideoMuted()) {
|
||||
sendAnalyticsEvent(CONFERENCE_VIDEO_INITIALLY_MUTED);
|
||||
logger.log('Video mute: initially muted');
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
|
||||
const mediaType = track.getType();
|
||||
|
||||
sendAnalytics(
|
||||
createTrackMutedEvent(mediaType, 'initial mute'));
|
||||
logger.log(`${mediaType} mute: initially muted.`);
|
||||
track.mute();
|
||||
}
|
||||
});
|
||||
@@ -1453,8 +1449,9 @@ export default {
|
||||
promise = createLocalTracksF({ devices: [ 'video' ] })
|
||||
.then(([ stream ]) => this.useVideoStream(stream))
|
||||
.then(() => {
|
||||
sendAnalyticsEvent(CONFERENCE_SHARING_DESKTOP_STOP);
|
||||
logger.log('switched back to local video');
|
||||
sendAnalytics(createScreenSharingEvent('stopped'));
|
||||
logger.log('Screen sharing stopped, switching to video.');
|
||||
|
||||
if (!this.localVideo && wasVideoMuted) {
|
||||
return Promise.reject('No local video to be muted!');
|
||||
} else if (wasVideoMuted && this.localVideo) {
|
||||
@@ -1609,7 +1606,7 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Tries to switch to the screenshairng mode by disposing camera stream and
|
||||
* Tries to switch to the screensharing mode by disposing camera stream and
|
||||
* replacing it with a desktop one.
|
||||
*
|
||||
* @param {Object} [options] - Screen sharing options that will be passed to
|
||||
@@ -1632,8 +1629,8 @@ export default {
|
||||
.then(stream => this.useVideoStream(stream))
|
||||
.then(() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
sendAnalyticsEvent(CONFERENCE_SHARING_DESKTOP_START);
|
||||
logger.log('sharing local desktop');
|
||||
sendAnalytics(createScreenSharingEvent('started'));
|
||||
logger.log('Screen sharing started');
|
||||
})
|
||||
.catch(error => {
|
||||
this.videoSwitchInProgress = false;
|
||||
@@ -1928,7 +1925,7 @@ export default {
|
||||
|
||||
room.selectParticipant(id);
|
||||
} catch (e) {
|
||||
sendAnalyticsEvent(SELECT_PARTICIPANT_FAILED);
|
||||
sendAnalytics(createSelectParticipantFailedEvent(e));
|
||||
reportError(e);
|
||||
}
|
||||
});
|
||||
@@ -2152,22 +2149,12 @@ export default {
|
||||
APP.UI.addListener(
|
||||
UIEvents.RESOLUTION_CHANGED,
|
||||
(id, oldResolution, newResolution, delay) => {
|
||||
const logObject = {
|
||||
id: 'resolution_change',
|
||||
participant: id,
|
||||
oldValue: oldResolution,
|
||||
newValue: newResolution,
|
||||
delay
|
||||
};
|
||||
|
||||
room.sendApplicationLog(JSON.stringify(logObject));
|
||||
|
||||
// We only care about the delay between simulcast streams.
|
||||
// Longer delays will be caused by something else and will just
|
||||
// poison the data.
|
||||
if (delay < 2000) {
|
||||
sendAnalyticsEvent(STREAM_SWITCH_DELAY, { value: delay });
|
||||
}
|
||||
sendAnalytics(createStreamSwitchDelayEvent(
|
||||
{
|
||||
'old_resolution': oldResolution,
|
||||
'new_resolution': newResolution,
|
||||
value: delay
|
||||
}));
|
||||
});
|
||||
|
||||
/* eslint-enable max-params */
|
||||
@@ -2193,7 +2180,7 @@ export default {
|
||||
cameraDeviceId => {
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
|
||||
sendAnalyticsEvent(SETTINGS_CHANGE_DEVICE_VIDEO);
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'video' ],
|
||||
cameraDeviceId,
|
||||
@@ -2232,7 +2219,7 @@ export default {
|
||||
micDeviceId => {
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
|
||||
sendAnalyticsEvent(SETTINGS_CHANGE_DEVICE_AUDIO_IN);
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
cameraDeviceId: null,
|
||||
@@ -2262,7 +2249,7 @@ export default {
|
||||
APP.UI.addListener(
|
||||
UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
|
||||
audioOutputDeviceId => {
|
||||
sendAnalyticsEvent(SETTINGS_CHANGE_DEVICE_AUDIO_OUT);
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'output'));
|
||||
APP.settings.setAudioOutputDeviceId(audioOutputDeviceId)
|
||||
.then(() => logger.log('changed audio output device'))
|
||||
.catch(err => {
|
||||
@@ -2528,7 +2515,9 @@ export default {
|
||||
// If audio was muted before, or we unplugged current device
|
||||
// and selected new one, then mute new audio track.
|
||||
if (audioWasMuted) {
|
||||
sendAnalyticsEvent(DEVICE_LIST_CHANGED_AUDIO_MUTED);
|
||||
sendAnalytics(createTrackMutedEvent(
|
||||
'audio',
|
||||
'device list changed'));
|
||||
logger.log('Audio mute: device list changed');
|
||||
muteLocalAudio(true);
|
||||
}
|
||||
@@ -2536,7 +2525,9 @@ export default {
|
||||
// If video was muted before, or we unplugged current device
|
||||
// and selected new one, then mute new video track.
|
||||
if (!this.isSharingScreen && videoWasMuted) {
|
||||
sendAnalyticsEvent(DEVICE_LIST_CHANGED_VIDEO_MUTED);
|
||||
sendAnalytics(createTrackMutedEvent(
|
||||
'video',
|
||||
'device list changed'));
|
||||
logger.log('Video mute: device list changed');
|
||||
muteLocalVideo(true);
|
||||
}
|
||||
@@ -2622,37 +2613,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Log event to callstats and analytics.
|
||||
* @param {string} name the event name
|
||||
* @param {int} value the value (it's int because google analytics supports
|
||||
* only int).
|
||||
* @param {string} label short text which provides more info about the event
|
||||
* which allows to distinguish between few event cases of the same name
|
||||
* NOTE: Should be used after conference.init
|
||||
*/
|
||||
logEvent(name, value, label) {
|
||||
sendAnalyticsEvent(name, {
|
||||
value,
|
||||
label
|
||||
});
|
||||
if (room) {
|
||||
room.sendApplicationLog(JSON.stringify({ name,
|
||||
value,
|
||||
label }));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Methods logs an application event given in the JSON format.
|
||||
* @param {string} logJSON an event to be logged in JSON format
|
||||
*/
|
||||
logJSON(logJSON) {
|
||||
if (room) {
|
||||
room.sendApplicationLog(logJSON);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect from the conference and optionally request user feedback.
|
||||
* @param {boolean} [requestFeedback=false] if user feedback should be
|
||||
@@ -2841,5 +2801,21 @@ export default {
|
||||
setAudioMuteStatus(muted) {
|
||||
APP.UI.setAudioMuted(this.getMyUserId(), muted);
|
||||
APP.API.notifyAudioMutedStatusChanged(muted);
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches the passed in feedback for submission. The submitted score
|
||||
* should be a number inclusively between 1 through 5, or -1 for no score.
|
||||
*
|
||||
* @param {number} score - a number between 1 and 5 (inclusive) or -1 for no
|
||||
* score.
|
||||
* @param {string} message - An optional message to attach to the feedback
|
||||
* in addition to the score.
|
||||
* @returns {void}
|
||||
*/
|
||||
submitFeedback(score = -1, message = '') {
|
||||
if (score === -1 || (score >= 1 && score <= 5)) {
|
||||
APP.store.dispatch(submitFeedback(score, message, room));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
11
config.js
@@ -94,7 +94,7 @@ var config = {
|
||||
|
||||
// w3c spec-compliant video constraints to use for video capture. Currently
|
||||
// used by browsers that return true from lib-jitsi-meet's
|
||||
// RTCBrowserType#usesNewGumFlow. The constraints are independency from
|
||||
// util#browser#usesNewGumFlow. The constraints are independency from
|
||||
// this config's resolution value. Defaults to requesting an ideal aspect
|
||||
// ratio of 16:9 with an ideal resolution of 1080p.
|
||||
// constraints: {
|
||||
@@ -307,21 +307,24 @@ var config = {
|
||||
// backToP2PDelay: 5
|
||||
},
|
||||
|
||||
// A list of scripts to load as lib-jitsi-meet "analytics handlers".
|
||||
// analyticsScriptUrls: [
|
||||
// "libs/analytics-ga.js", // google-analytics
|
||||
// "https://example.com/my-custom-analytics.js"
|
||||
// ],
|
||||
|
||||
// Information about the jitsi-meet instance we are connecting to, including
|
||||
// the user region as seen by the server.
|
||||
//
|
||||
|
||||
deploymentInfo: {
|
||||
// shard: "shard1",
|
||||
// region: "europe",
|
||||
// userRegion: "asia"
|
||||
}
|
||||
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
alwaysVisibleToolbar
|
||||
analyticsScriptUrls
|
||||
autoEnableDesktopSharing
|
||||
autoRecord
|
||||
autoRecordToken
|
||||
|
||||
@@ -94,12 +94,15 @@ function connect(id, password, roomName) {
|
||||
JitsiConnectionEvents.CONNECTION_FAILED,
|
||||
connectionFailedHandler);
|
||||
|
||||
/* eslint-disable max-params */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function connectionFailedHandler(error, message, credentials) {
|
||||
function connectionFailedHandler(error, message, credentials, details) {
|
||||
/* eslint-enable max-params */
|
||||
APP.store.dispatch(
|
||||
connectionFailed(connection, error, message, credentials));
|
||||
connectionFailed(
|
||||
connection, error, message, credentials, details));
|
||||
|
||||
if (isFatalJitsiConnectionError(error)) {
|
||||
connection.removeEventListener(
|
||||
|
||||
101
css/_emoji-icons.scss
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
.emoji-icon-background {
|
||||
background-size: contain;
|
||||
background-position: 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.emoji-icon {
|
||||
background-size: contain;
|
||||
background-position: 50%;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
line-height: 3em;
|
||||
z-index: 1000;
|
||||
}
|
||||
.emoji-icon:before {
|
||||
content: "\00a0";
|
||||
}
|
||||
.emoji-icon-bomb {
|
||||
background-image: url(../images/emojis/bomb.png);
|
||||
}
|
||||
.emoji-icon-bulb {
|
||||
background-image: url(../images/emojis/bulb.png);
|
||||
}
|
||||
.emoji-icon-clap {
|
||||
background-image: url(../images/emojis/clap.png);
|
||||
}
|
||||
.emoji-icon-fistbump {
|
||||
background-image: url(../images/emojis/fistbump.png);
|
||||
}
|
||||
.emoji-icon-heart {
|
||||
background-image: url(../images/emojis/heart.png);
|
||||
}
|
||||
.emoji-icon-highfive {
|
||||
background-image: url(../images/emojis/highfive.png);
|
||||
}
|
||||
|
||||
.emoji-icon-perfect {
|
||||
background-image: url(../images/emojis/perfect.png);
|
||||
}
|
||||
|
||||
.emoji-icon-sleep {
|
||||
background-image: url(../images/emojis/sleep.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley1 {
|
||||
background-image: url(../images/emojis/smiley1.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley2 {
|
||||
background-image: url(../images/emojis/smiley2.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley3 {
|
||||
background-image: url(../images/emojis/smiley3.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley4 {
|
||||
background-image: url(../images/emojis/smiley4.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley5 {
|
||||
background-image: url(../images/emojis/smiley5.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley6 {
|
||||
background-image: url(../images/emojis/smiley6.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley7 {
|
||||
background-image: url(../images/emojis/smiley7.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley8 {
|
||||
background-image: url(../images/emojis/smiley8.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley9 {
|
||||
background-image: url(../images/emojis/smiley9.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley10 {
|
||||
background-image: url(../images/emojis/smiley10.png);
|
||||
}
|
||||
|
||||
.emoji-icon-smiley11 {
|
||||
background-image: url(../images/emojis/smiley11.png);
|
||||
}
|
||||
|
||||
.emoji-icon-star {
|
||||
background-image: url(../images/emojis/star.png);
|
||||
}
|
||||
|
||||
.emoji-icon-thumbsdown {
|
||||
background-image: url(../images/emojis/thumbsdown.png);
|
||||
}
|
||||
|
||||
.emoji-icon-thumbsup {
|
||||
background-image: url(../images/emojis/thumbsup.png);
|
||||
}
|
||||
@@ -24,10 +24,15 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-arrow_back:before {
|
||||
content: "\e5c4";
|
||||
}
|
||||
.icon-event_note:before {
|
||||
content: "\e616";
|
||||
}
|
||||
.icon-navigate_before:before {
|
||||
content: "\e408";
|
||||
}
|
||||
.icon-public:before {
|
||||
content: "\e80b";
|
||||
}
|
||||
|
||||
@@ -153,10 +153,13 @@
|
||||
|
||||
@include transform(translateX(-50%));
|
||||
|
||||
> a:first-child.button,
|
||||
> div:first-child .button {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
> a:last-child.button,
|
||||
> div:last-child .button {
|
||||
border-bottom-right-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
|
||||
@@ -517,6 +517,14 @@
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.emotionsCanvas {
|
||||
bottom: 0;
|
||||
height: 50%;
|
||||
max-height: 200px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#videoNotAvailableScreen {
|
||||
text-align: center;
|
||||
#avatarContainer {
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
@import 'flag-icon';
|
||||
|
||||
@import 'emoji-icons';
|
||||
|
||||
/* Modules BEGIN */
|
||||
|
||||
@import 'dial-out';
|
||||
|
||||
@@ -27,6 +27,7 @@ Its constructor gets a number of options:
|
||||
* **interfaceConfigOverwrite**: (optional) JS object with overrides for options defined in [interface_config.js].
|
||||
* **noSSL**: (optional, defaults to true) Boolean indicating if the server should be contacted using HTTP or HTTPS.
|
||||
* **jwt**: (optional) [JWT](https://jwt.io/) token.
|
||||
* **onload**: (optional) handler for the iframe onload event.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Server Installation for Jitsi Meet
|
||||
|
||||
|
||||
:warning: **WARNING:** Manual installation is not recommended. We recommend following the [quick-install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document sometimes is not updated to latest changes.
|
||||
|
||||
|
||||
|
||||
This describes configuring a server `jitsi.example.com` running Debian or a Debian Derivative. You will need to
|
||||
change references to that to match your host, and generate some passwords for
|
||||
`YOURSECRET1`, `YOURSECRET2` and `YOURSECRET3`.
|
||||
@@ -93,8 +98,9 @@ prosodyctl cert generate auth.jitsi.example.com
|
||||
Add auth.jitsi.example.com to the trusted certificates on the local machine:
|
||||
```sh
|
||||
ln -sf /var/lib/prosody/auth.jitsi.example.com.crt /usr/local/share/ca-certificates/auth.jitsi.example.com.crt
|
||||
update-ca-certificates
|
||||
update-ca-certificates -f
|
||||
```
|
||||
Note that the `-f` flag is necessary if there are symlinks left from a previous installation.
|
||||
|
||||
Create conference focus user:
|
||||
```sh
|
||||
|
||||
BIN
fonts/jitsi.eot
@@ -11,7 +11,9 @@
|
||||
<glyph unicode="" glyph-name="add" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
|
||||
<glyph unicode="" glyph-name="bluetooth" d="M550 328l-80 82v-162zM470 776v-162l80 82zM670 696l-184-184 184-184-244-242h-42v324l-196-196-60 60 238 238-238 238 60 60 196-196v324h42zM834 738c40-64 62-142 62-222 0-84-24-160-66-226l-50 50c26 52 42 110 42 172s-16 120-42 172zM608 512l98 98c12-30 20-64 20-98s-8-70-20-100z" />
|
||||
<glyph unicode="" glyph-name="headset" d="M512 982c212 0 384-172 384-384v-300c0-70-58-128-128-128h-128v342h170v86c0 166-132 298-298 298s-298-132-298-298v-86h170v-342h-128c-70 0-128 58-128 128v300c0 212 172 384 384 384z" />
|
||||
<glyph unicode="" glyph-name="navigate_before" d="M658 708l-196-196 196-196-60-60-256 256 256 256z" />
|
||||
<glyph unicode="" glyph-name="timer" d="M512 170c166 0 298 134 298 300s-132 298-298 298-298-132-298-298 132-300 298-300zM812 708c52-66 84-148 84-238 0-212-172-384-384-384s-384 172-384 384 172 384 384 384c90 0 174-34 240-86l60 62c22-18 42-38 60-60zM470 426v256h84v-256h-84zM640 982v-86h-256v86h256z" />
|
||||
<glyph unicode="" glyph-name="arrow_back" d="M854 554v-84h-520l238-240-60-60-342 342 342 342 60-60-238-240h520z" />
|
||||
<glyph unicode="" glyph-name="thumb-menu" d="M512 342c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 598c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 682c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
|
||||
<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" />
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.woff
@@ -1,6 +1,60 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M854 470v84h-520l238 240-60 60-342-342 342-342 60 60-238 240h520z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"arrow_back"
|
||||
],
|
||||
"defaultCode": 58820,
|
||||
"grid": 24
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"ligatures": "arrow_back",
|
||||
"id": 45,
|
||||
"order": 924,
|
||||
"prevSize": 24,
|
||||
"code": 58820,
|
||||
"name": "arrow_back"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 45
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M658 316l-196 196 196 196-60 60-256-256 256-256z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"navigate_before"
|
||||
],
|
||||
"defaultCode": 58376,
|
||||
"grid": 24
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"ligatures": "chevron_left, navigate_before",
|
||||
"id": 152,
|
||||
"order": 923,
|
||||
"prevSize": 24,
|
||||
"code": 58376,
|
||||
"name": "navigate_before"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 152
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -24,9 +78,9 @@
|
||||
"code": 59403,
|
||||
"name": "public"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 605
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -53,7 +107,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
"iconIdx": 1
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -80,7 +134,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 1
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -107,7 +161,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 2
|
||||
"iconIdx": 3
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -134,7 +188,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 3
|
||||
"iconIdx": 4
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -161,7 +215,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
"iconIdx": 5
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -188,7 +242,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 5
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -217,7 +271,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 6
|
||||
"iconIdx": 7
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -244,7 +298,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 7
|
||||
"iconIdx": 8
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -271,7 +325,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 8
|
||||
"iconIdx": 9
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -300,7 +354,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 9
|
||||
"iconIdx": 10
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -329,7 +383,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 10
|
||||
"iconIdx": 11
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -358,7 +412,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 11
|
||||
"iconIdx": 12
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -387,7 +441,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
"iconIdx": 13
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -416,7 +470,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
"iconIdx": 14
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -442,7 +496,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 14
|
||||
"iconIdx": 15
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -468,7 +522,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 15
|
||||
"iconIdx": 16
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -494,7 +548,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 16
|
||||
"iconIdx": 17
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -520,7 +574,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 17
|
||||
"iconIdx": 18
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -546,7 +600,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 18
|
||||
"iconIdx": 19
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -572,7 +626,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 19
|
||||
"iconIdx": 20
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -598,7 +652,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 20
|
||||
"iconIdx": 21
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -616,7 +670,7 @@
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 10,
|
||||
"order": 900,
|
||||
"order": 922,
|
||||
"ligatures": "expand_less",
|
||||
"prevSize": 32,
|
||||
"code": 59679,
|
||||
@@ -624,7 +678,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 21
|
||||
"iconIdx": 22
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -650,7 +704,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
"iconIdx": 23
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -676,7 +730,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 23
|
||||
"iconIdx": 24
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -702,7 +756,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 24
|
||||
"iconIdx": 25
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -728,7 +782,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 25
|
||||
"iconIdx": 26
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -754,7 +808,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 26
|
||||
"iconIdx": 27
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -780,7 +834,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 27
|
||||
"iconIdx": 28
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -806,7 +860,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 28
|
||||
"iconIdx": 29
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -832,7 +886,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 29
|
||||
"iconIdx": 30
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -858,7 +912,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 30
|
||||
"iconIdx": 31
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -884,7 +938,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 31
|
||||
"iconIdx": 32
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -910,7 +964,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 32
|
||||
"iconIdx": 33
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -936,7 +990,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 33
|
||||
"iconIdx": 34
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -962,7 +1016,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 34
|
||||
"iconIdx": 35
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -988,7 +1042,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 35
|
||||
"iconIdx": 36
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1014,7 +1068,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 36
|
||||
"iconIdx": 37
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1040,7 +1094,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 37
|
||||
"iconIdx": 38
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1066,7 +1120,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 38
|
||||
"iconIdx": 39
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1092,7 +1146,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 39
|
||||
"iconIdx": 40
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1118,7 +1172,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 40
|
||||
"iconIdx": 41
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1144,7 +1198,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 41
|
||||
"iconIdx": 42
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1170,7 +1224,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 42
|
||||
"iconIdx": 43
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1199,7 +1253,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 43
|
||||
"iconIdx": 44
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1229,7 +1283,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 44
|
||||
"iconIdx": 45
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1259,7 +1313,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 45
|
||||
"iconIdx": 46
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1285,7 +1339,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 46
|
||||
"iconIdx": 47
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1311,7 +1365,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 47
|
||||
"iconIdx": 48
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1337,7 +1391,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 48
|
||||
"iconIdx": 49
|
||||
}
|
||||
],
|
||||
"height": 1024,
|
||||
|
||||
BIN
images/emojis/bomb.png
Executable file
|
After Width: | Height: | Size: 631 B |
BIN
images/emojis/bulb.png
Executable file
|
After Width: | Height: | Size: 715 B |
BIN
images/emojis/clap.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
images/emojis/fistbump.png
Executable file
|
After Width: | Height: | Size: 816 B |
BIN
images/emojis/heart.png
Executable file
|
After Width: | Height: | Size: 513 B |
BIN
images/emojis/highfive.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
images/emojis/perfect.png
Executable file
|
After Width: | Height: | Size: 812 B |
BIN
images/emojis/sleep.png
Executable file
|
After Width: | Height: | Size: 390 B |
BIN
images/emojis/smiley1.png
Executable file
|
After Width: | Height: | Size: 839 B |
BIN
images/emojis/smiley10.png
Executable file
|
After Width: | Height: | Size: 1002 B |
BIN
images/emojis/smiley11.png
Executable file
|
After Width: | Height: | Size: 759 B |
BIN
images/emojis/smiley2.png
Executable file
|
After Width: | Height: | Size: 931 B |
BIN
images/emojis/smiley3.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
images/emojis/smiley4.png
Executable file
|
After Width: | Height: | Size: 924 B |
BIN
images/emojis/smiley5.png
Executable file
|
After Width: | Height: | Size: 728 B |
BIN
images/emojis/smiley6.png
Executable file
|
After Width: | Height: | Size: 923 B |
BIN
images/emojis/smiley7.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
images/emojis/smiley8.png
Executable file
|
After Width: | Height: | Size: 683 B |
BIN
images/emojis/smiley9.png
Executable file
|
After Width: | Height: | Size: 678 B |
BIN
images/emojis/star.png
Executable file
|
After Width: | Height: | Size: 900 B |
BIN
images/emojis/thumbsdown.png
Executable file
|
After Width: | Height: | Size: 830 B |
BIN
images/emojis/thumbsup.png
Executable file
|
After Width: | Height: | Size: 840 B |
@@ -1,6 +1,6 @@
|
||||
PODS:
|
||||
- React (0.50.4):
|
||||
- React/Core (= 0.50.4)
|
||||
- React (0.51.0):
|
||||
- React/Core (= 0.51.0)
|
||||
- react-native-background-timer (2.0.0):
|
||||
- React
|
||||
- react-native-fetch-blob (0.10.6):
|
||||
@@ -10,40 +10,40 @@ PODS:
|
||||
- react-native-locale-detector (1.0.0):
|
||||
- React
|
||||
- react-native-webrtc (1.58.2)
|
||||
- React/BatchedBridge (0.50.4):
|
||||
- React/BatchedBridge (0.51.0):
|
||||
- React/Core
|
||||
- React/cxxreact_legacy
|
||||
- React/Core (0.50.4):
|
||||
- yoga (= 0.50.4.React)
|
||||
- React/cxxreact_legacy (0.50.4):
|
||||
- React/Core (0.51.0):
|
||||
- yoga (= 0.51.0.React)
|
||||
- React/cxxreact_legacy (0.51.0):
|
||||
- React/jschelpers_legacy
|
||||
- React/DevSupport (0.50.4):
|
||||
- React/DevSupport (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTWebSocket
|
||||
- React/fishhook (0.50.4)
|
||||
- React/jschelpers_legacy (0.50.4)
|
||||
- React/RCTActionSheet (0.50.4):
|
||||
- React/fishhook (0.51.0)
|
||||
- React/jschelpers_legacy (0.51.0)
|
||||
- React/RCTActionSheet (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTAnimation (0.50.4):
|
||||
- React/RCTAnimation (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTBlob (0.50.4):
|
||||
- React/RCTBlob (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTImage (0.50.4):
|
||||
- React/RCTImage (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTNetwork
|
||||
- React/RCTLinkingIOS (0.50.4):
|
||||
- React/RCTLinkingIOS (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTNetwork (0.50.4):
|
||||
- React/RCTNetwork (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTText (0.50.4):
|
||||
- React/RCTText (0.51.0):
|
||||
- React/Core
|
||||
- React/RCTWebSocket (0.50.4):
|
||||
- React/RCTWebSocket (0.51.0):
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNVectorIcons (4.4.2):
|
||||
- React
|
||||
- yoga (0.50.4.React)
|
||||
- yoga (0.51.0.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
|
||||
@@ -83,15 +83,15 @@ EXTERNAL SOURCES:
|
||||
:path: ../node_modules/react-native/ReactCommon/yoga
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
React: 6d7efef18e0241eb832cf4c085405169a15080ed
|
||||
react-native-background-timer: 10063c04bf85d7f8811dff8c74399f0aa715245f
|
||||
react-native-fetch-blob: 2bef9be702de8726f4d7bf58d2345579aaaee60d
|
||||
react-native-keep-awake: bb4dbb6fd21a7879432f9538b0b7d71398fe9f81
|
||||
react-native-locale-detector: bb60716189d4c69c459355d0a4804c9b13f65e84
|
||||
react-native-webrtc: 6fd0b3aa890d7a9b9b4d01d30f958d17ae88a785
|
||||
RNVectorIcons: c1821d56c775cc5a3bca66c77dfc8cb4a90d27e2
|
||||
yoga: b9aebf996711e50fc31f5608c10aa108a5a0c29e
|
||||
React: 541ba768b9855e10cdc76f55427a5cd0653ca806
|
||||
react-native-background-timer: 63dcbf37dbcf294b5c6c071afcdc661fa06a7594
|
||||
react-native-fetch-blob: 63394b1d7b0781547b3e4463b3195790177b1222
|
||||
react-native-keep-awake: 0de4bd66de0c23178107dce0c2fcc3354b2a8e94
|
||||
react-native-locale-detector: d1b2c6fe5abb56e3a1efb6c2d6f308c05c4251f1
|
||||
react-native-webrtc: bc044ca9530fc802e7533f247aa08fe1b6bf8dc5
|
||||
RNVectorIcons: c0dbfbf6068fefa240c37b0f71bd03b45dddac44
|
||||
yoga: 17521bbb0dd54a47c0b3ac43253e78cdac7488e0
|
||||
|
||||
PODFILE CHECKSUM: fabd6b6c27f8e1849f0668db3f403bf536ac8903
|
||||
|
||||
COCOAPODS: 1.3.1
|
||||
COCOAPODS: 1.4.0
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; };
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */; };
|
||||
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C2CFC200F51D60060D076 /* LaunchOptions.m */; };
|
||||
0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
|
||||
@@ -34,6 +35,7 @@
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetView.m; sourceTree = "<group>"; };
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewDelegate.h; sourceTree = "<group>"; };
|
||||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPVolumeViewManager.m; sourceTree = "<group>"; };
|
||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchOptions.m; sourceTree = "<group>"; };
|
||||
0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
|
||||
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
|
||||
@@ -106,6 +108,7 @@
|
||||
0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
@@ -276,7 +279,17 @@
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@@ -295,6 +308,7 @@
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
|
||||
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
|
||||
@@ -32,6 +32,12 @@ RCT_EXPORT_MODULE();
|
||||
NSString *name = infoDictionary[@"CFBundleDisplayName"];
|
||||
NSString *version = infoDictionary[@"CFBundleShortVersionString"];
|
||||
|
||||
if (name == nil) {
|
||||
name = infoDictionary[@"CFBundleName"];
|
||||
if (name == nil) {
|
||||
name = @"";
|
||||
}
|
||||
}
|
||||
if (version == nil) {
|
||||
version = infoDictionary[@"CFBundleVersion"];
|
||||
if (version == nil) {
|
||||
|
||||
83
ios/sdk/src/LaunchOptions.m
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Intents/Intents.h>
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
@interface LaunchOptions : NSObject<RCTBridgeModule>
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *bridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LaunchOptions
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
- (dispatch_queue_t)methodQueue {
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve
|
||||
reject:(__unused RCTPromiseRejectBlock)reject) {
|
||||
id initialURL = nil;
|
||||
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL *url = self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
initialURL = url.absoluteString;
|
||||
} else {
|
||||
NSDictionary *userActivityDictionary
|
||||
= self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
NSUserActivity *userActivity
|
||||
= [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
if (userActivity != nil) {
|
||||
NSString *activityType = userActivity.activityType;
|
||||
|
||||
if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
// App was started by opening a URL in the browser
|
||||
initialURL = userActivity.webpageURL.absoluteString;
|
||||
} else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
|
||||
|| [activityType isEqualToString:@"INStartVideoCallIntent"]) {
|
||||
// App was started by a CallKit Intent
|
||||
INIntent *intent = userActivity.interaction.intent;
|
||||
NSArray<INPerson *> *contacts;
|
||||
NSString *url;
|
||||
BOOL startAudioOnly = NO;
|
||||
|
||||
if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
|
||||
contacts = ((INStartAudioCallIntent *) intent).contacts;
|
||||
startAudioOnly = YES;
|
||||
} else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
|
||||
contacts = ((INStartVideoCallIntent *) intent).contacts;
|
||||
}
|
||||
|
||||
if (contacts && (url = contacts.firstObject.personHandle.value)) {
|
||||
initialURL
|
||||
= @{
|
||||
@"config": @{@"startAudioOnly":@(startAudioOnly)},
|
||||
@"url": url
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve(initialURL != nil ? initialURL : (id)kCFNull);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
20
lang/languages-az.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
}
|
||||
20
lang/languages-fa.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"zhCN": "",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
}
|
||||
486
lang/main-az.json
Normal file
@@ -0,0 +1,486 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"contactlist_plural": "",
|
||||
"passwordSetRemotely": "",
|
||||
"connectionsettings": "",
|
||||
"poweredby": "",
|
||||
"feedback": {
|
||||
"average": "",
|
||||
"bad": "",
|
||||
"good": "",
|
||||
"rateExperience": "",
|
||||
"veryBad": "",
|
||||
"veryGood": ""
|
||||
},
|
||||
"inviteUrlDefaultMsg": "",
|
||||
"me": "",
|
||||
"speaker": "",
|
||||
"raisedHand": "",
|
||||
"defaultNickname": "",
|
||||
"defaultLink": "",
|
||||
"callingName": "",
|
||||
"audioOnly": {
|
||||
"audioOnly": "",
|
||||
"featureToggleDisabled": ""
|
||||
},
|
||||
"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": {
|
||||
"addPeople": "",
|
||||
"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": {
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"kick": "",
|
||||
"muted": "",
|
||||
"domute": "",
|
||||
"flip": "",
|
||||
"remoteControl": ""
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "",
|
||||
"bitrate": "",
|
||||
"packetloss": "",
|
||||
"resolution": "",
|
||||
"framerate": "",
|
||||
"less": "",
|
||||
"more": "",
|
||||
"address": "",
|
||||
"remoteport": "",
|
||||
"remoteport_plural": "",
|
||||
"localport": "",
|
||||
"localport_plural": "",
|
||||
"localaddress": "",
|
||||
"localaddress_plural": "",
|
||||
"remoteaddress": "",
|
||||
"remoteaddress_plural": "",
|
||||
"transport": "",
|
||||
"transport_plural": "",
|
||||
"bandwidth": "",
|
||||
"na": "",
|
||||
"turn": "",
|
||||
"quality": {
|
||||
"good": "",
|
||||
"inactive": "",
|
||||
"lost": "",
|
||||
"nonoptimal": "",
|
||||
"poor": ""
|
||||
},
|
||||
"status": ""
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": "",
|
||||
"muted": "",
|
||||
"mutedTitle": "",
|
||||
"raisedHand": ""
|
||||
},
|
||||
"dialog": {
|
||||
"add": "",
|
||||
"allow": "",
|
||||
"kickMessage": "",
|
||||
"popupErrorTitle": "",
|
||||
"popupError": "",
|
||||
"passwordErrorTitle": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"connectError": "",
|
||||
"connectErrorWithMsg": "",
|
||||
"incorrectPassword": "",
|
||||
"connecting": "",
|
||||
"copy": "",
|
||||
"contactSupport": "",
|
||||
"error": "",
|
||||
"createPassword": "",
|
||||
"detectext": "",
|
||||
"failedpermissions": "",
|
||||
"conferenceReloadTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"dismiss": "",
|
||||
"rejoinNow": "",
|
||||
"maxUsersLimitReachedTitle": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupportedTitle": "",
|
||||
"passwordNotSupported": "",
|
||||
"internalErrorTitle": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"currentPassword": "",
|
||||
"passwordLabel": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"done": "",
|
||||
"Remove": "",
|
||||
"removePassword": "",
|
||||
"shareVideoTitle": "",
|
||||
"shareVideoLinkError": "",
|
||||
"removeSharedVideoTitle": "",
|
||||
"removeSharedVideoMsg": "",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"alreadySharedVideoTitle": "",
|
||||
"WaitingForHost": "",
|
||||
"WaitForHostMsg": "",
|
||||
"IamHost": "",
|
||||
"Cancel": "",
|
||||
"Submit": "",
|
||||
"retry": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"Save": "",
|
||||
"recording": "",
|
||||
"recordingToken": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"shareLink": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": "",
|
||||
"tokenAuthFailedTitle": "",
|
||||
"tokenAuthFailed": "",
|
||||
"displayNameRequired": "",
|
||||
"enterDisplayName": "",
|
||||
"extensionRequired": "",
|
||||
"firefoxExtensionPrompt": "",
|
||||
"feedbackHelp": "",
|
||||
"feedbackQuestion": "",
|
||||
"thankYou": "",
|
||||
"sorryFeedback": "",
|
||||
"liveStreaming": "",
|
||||
"streamKey": "",
|
||||
"startLiveStreaming": "",
|
||||
"stopStreamingWarning": "",
|
||||
"stopRecordingWarning": "",
|
||||
"stopLiveStreaming": "",
|
||||
"stopRecording": "",
|
||||
"doNotShowWarningAgain": "",
|
||||
"doNotShowMessageAgain": "",
|
||||
"permissionDenied": "",
|
||||
"screenSharingFailedToInstall": "",
|
||||
"screenSharingFailedToInstallTitle": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"micErrorPresent": "",
|
||||
"cameraErrorPresent": "",
|
||||
"cameraUnsupportedResolutionError": "",
|
||||
"cameraUnknownError": "",
|
||||
"cameraPermissionDeniedError": "",
|
||||
"cameraNotFoundError": "",
|
||||
"cameraConstraintFailedError": "",
|
||||
"micUnknownError": "",
|
||||
"micPermissionDeniedError": "",
|
||||
"micNotFoundError": "",
|
||||
"micConstraintFailedError": "",
|
||||
"micNotSendingDataTitle": "",
|
||||
"micNotSendingData": "",
|
||||
"cameraNotSendingDataTitle": "",
|
||||
"cameraNotSendingData": "",
|
||||
"goToStore": "",
|
||||
"externalInstallationTitle": "",
|
||||
"externalInstallationMsg": "",
|
||||
"inlineInstallationMsg": "",
|
||||
"inlineInstallExtension": "",
|
||||
"muteParticipantTitle": "",
|
||||
"muteParticipantBody": "",
|
||||
"muteParticipantButton": "",
|
||||
"remoteControlTitle": "",
|
||||
"remoteControlRequestMessage": "",
|
||||
"remoteControlShareScreenWarning": "",
|
||||
"remoteControlDeniedMessage": "",
|
||||
"remoteControlAllowedMessage": "",
|
||||
"remoteControlErrorMessage": "",
|
||||
"startRemoteControlErrorMessage": "",
|
||||
"remoteControlStopMessage": "",
|
||||
"close": "",
|
||||
"shareYourScreen": "",
|
||||
"yourEntireScreen": "",
|
||||
"applicationWindow": ""
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"and": ""
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "",
|
||||
"CONNECTING": "",
|
||||
"RECONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ATTACHED": ""
|
||||
},
|
||||
"recording": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"failedToStart": "",
|
||||
"off": "",
|
||||
"on": "",
|
||||
"pending": "",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"failedToStart": "",
|
||||
"off": "",
|
||||
"on": "",
|
||||
"pending": "",
|
||||
"streamIdRequired": "",
|
||||
"streamIdHelp": "",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"name": "",
|
||||
"seconds": "",
|
||||
"speakerStats": "",
|
||||
"speakerTime": ""
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "",
|
||||
"noPermission": "",
|
||||
"previewUnavailable": "",
|
||||
"selectADevice": "",
|
||||
"testAudio": ""
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "",
|
||||
"callNumber": "",
|
||||
"enterID": "",
|
||||
"howToDialIn": "",
|
||||
"hidePassword": "",
|
||||
"inviteTo": "",
|
||||
"invitedYouTo": "",
|
||||
"invitePeople": "",
|
||||
"locked": "",
|
||||
"showPassword": "",
|
||||
"unlocked": ""
|
||||
},
|
||||
"videoStatus": {
|
||||
"callQuality": "",
|
||||
"hd": "",
|
||||
"highDefinition": "",
|
||||
"labelTooltipVideo": "",
|
||||
"labelTooltipAudioOnly": "",
|
||||
"ld": "",
|
||||
"lowDefinition": "",
|
||||
"p2pEnabled": "",
|
||||
"p2pVideoQualityDescription": "",
|
||||
"recHighDefinitionOnly": "",
|
||||
"sd": "",
|
||||
"standardDefinition": "",
|
||||
"qualityButtonTip": ""
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "",
|
||||
"dialOut": "",
|
||||
"statusMessage": "",
|
||||
"enterPhone": "",
|
||||
"phoneNotAllowed": ""
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "",
|
||||
"noResults": "",
|
||||
"searchPlaceholder": "",
|
||||
"title": "",
|
||||
"failedToAdd": ""
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "",
|
||||
"retry": "",
|
||||
"support": "",
|
||||
"supportMsg": ""
|
||||
},
|
||||
"deviceError": {
|
||||
"cameraError": "",
|
||||
"microphoneError": "",
|
||||
"cameraPermission": "",
|
||||
"microphonePermission": ""
|
||||
},
|
||||
"info": {
|
||||
"copy": "",
|
||||
"invite": "",
|
||||
"title": "",
|
||||
"tooltip": ""
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"contactlist_plural": "",
|
||||
"passwordSetRemotely": "добавена от друг участник",
|
||||
"contactlist": "__count__ участник",
|
||||
"contactlist_plural": "__count__ участници",
|
||||
"passwordSetRemotely": "зададена от друг участник",
|
||||
"connectionsettings": "Настройки на връзката",
|
||||
"poweredby": "powered by",
|
||||
"poweredby": "с подкрепата на",
|
||||
"feedback": {
|
||||
"average": "Средно",
|
||||
"bad": "Лошо",
|
||||
@@ -16,48 +16,47 @@
|
||||
"me": "аз",
|
||||
"speaker": "Говорител",
|
||||
"raisedHand": "Иска думата",
|
||||
"defaultNickname": "Яна Розова",
|
||||
"defaultLink": "например __url__",
|
||||
"callingName": "",
|
||||
"defaultNickname": "напр. Иван Иванов",
|
||||
"defaultLink": "напр. __url__",
|
||||
"callingName": "__name__",
|
||||
"audioOnly": {
|
||||
"audioOnly": "Само звук",
|
||||
"featureToggleDisabled": ""
|
||||
"featureToggleDisabled": "Превключването на „__feature__“ не е възможно в режим само със звук"
|
||||
},
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "",
|
||||
"chromeGrantPermissions": "",
|
||||
"androidGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "",
|
||||
"operaGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "Изберете <b><i>Споделяне на избраното устройство</i></b>, когато браузърът Ви помоли за разрешение.",
|
||||
"operaGrantPermissions": "Изберете <b><i>Разрешаване</i></b>, когато браузърът Ви помоли за разрешение.",
|
||||
"iexplorerGrantPermissions": "",
|
||||
"safariGrantPermissions": "",
|
||||
"nwjsGrantPermissions": "",
|
||||
"edgeGrantPermissions": ""
|
||||
"safariGrantPermissions": "Изберете <b><i>Добре</i></b>, когато браузърът Ви помоли за разрешение.",
|
||||
"nwjsGrantPermissions": "Моля, разрешете използването на камерата и микрофона",
|
||||
"edgeGrantPermissions": "Изберете <b><i>Да</i></b>, когато браузърът Ви помоли за разрешение."
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"keyboardShortcuts": "Клавишни комбинации:",
|
||||
"raiseHand": "",
|
||||
"pushToTalk": "Push to talk.",
|
||||
"toggleScreensharing": "Смяна между камера и споделен екран.",
|
||||
"toggleFilmstrip": "",
|
||||
"toggleShortcuts": "Показване/скриване на това меню.",
|
||||
"focusLocal": "",
|
||||
"focusRemote": "",
|
||||
"toggleChat": "Отваряне/скриване на чат панела.",
|
||||
"mute": "Спиране/пускане на микрофона.",
|
||||
"fullScreen": "Влез / Излез от Пълен екран",
|
||||
"videoMute": "",
|
||||
"showSpeakerStats": ""
|
||||
"keyboardShortcuts": "Клавишни комбинации",
|
||||
"raiseHand": "Вдигнете или свалете ръка",
|
||||
"pushToTalk": "Натиснете, за да говорите",
|
||||
"toggleScreensharing": "Смяна между камера и споделен екран",
|
||||
"toggleFilmstrip": "Показване/скриване на видеата",
|
||||
"toggleShortcuts": "Показване/скриване на помощното меню",
|
||||
"focusLocal": "Фокусиране върху Вашето видео",
|
||||
"focusRemote": "Фокусиране върху видеото на друг участник",
|
||||
"toggleChat": "Отваряне/скриване на текстовите съобщения",
|
||||
"mute": "Спиране/пускане на микрофона",
|
||||
"fullScreen": "Влизане/излизане от режим на цял екран",
|
||||
"videoMute": "Пускане/спиране на камерата",
|
||||
"showSpeakerStats": "Показване на статистика за говорителя"
|
||||
},
|
||||
"\u0005keyboardShortcuts": {},
|
||||
"welcomepage": {
|
||||
"disable": "Не показвай повече тази страница",
|
||||
"feature1": {
|
||||
"content": "Не е необходимо да сваляте нищо. __app__ работи директно във вашия браузър. Просто споделете адреса на вашата конференция с другите за да започнете.",
|
||||
"content": "Не е необходимо да сваляте нищо. „__app__“ работи директно във браузъра Ви. Просто споделете адреса на беседата си с другите, за да започнете.",
|
||||
"title": "Лесен за употреба"
|
||||
},
|
||||
"feature2": {
|
||||
"content": "Видео конференциите могат да работят с по-малко от 128Kbps, а аудио конференциите и конференциите с споделен екран дори с по-малко.",
|
||||
"content": "Видео беседите могат да работят и при скорост от 128кб/сек. А тези само със звук или за споделяне на екрана – дори и при по-ниска.",
|
||||
"title": "Нисък дебит"
|
||||
},
|
||||
"feature3": {
|
||||
@@ -65,7 +64,7 @@
|
||||
"title": "Отворен код"
|
||||
},
|
||||
"feature4": {
|
||||
"content": "",
|
||||
"content": "Няма изкуствени ограничения на броя потребители или участници в беседата. Единствените ограничения са мощността на сървъра и скоростта на мрежовата връзка.",
|
||||
"title": "Неограничен брой потребители"
|
||||
},
|
||||
"feature5": {
|
||||
@@ -84,64 +83,63 @@
|
||||
"content": "Научете повече за своите потребители като интегрирате лесно Piwik, Google Analytics и други статистики за изполването.",
|
||||
"title": "Статистики за използване"
|
||||
},
|
||||
"go": "Влез",
|
||||
"join": "",
|
||||
"privacy": "",
|
||||
"go": "НАПРЕД",
|
||||
"join": "ПРИСЪЕДИНЯВАНЕ",
|
||||
"privacy": "Поверителност",
|
||||
"roomname": "Въведете име на стаята",
|
||||
"roomnamePlaceHolder": "",
|
||||
"sendFeedback": "",
|
||||
"terms": ""
|
||||
"roomnamePlaceHolder": "име на стаята",
|
||||
"sendFeedback": "Изпращане на отзиви",
|
||||
"terms": "Условия"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
"title": ""
|
||||
"policyText": " ",
|
||||
"title": "„__app__“ се нуждае от достъп до микрофона и камерата."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"title": "",
|
||||
"text": "",
|
||||
"rejoinKeyTitle": ""
|
||||
"title": "Вашият видео разговор беше прекъснат, тъй като компютърът Ви заспа.",
|
||||
"text": "Натиснете бутона <i>Повторно присъединяване</i>, за да се свържете отново.",
|
||||
"rejoinKeyTitle": "Повторно присъединяване"
|
||||
},
|
||||
"toolbar": {
|
||||
"addPeople": "",
|
||||
"audioonly": "",
|
||||
"mute": "Включи / Изключи микрофона",
|
||||
"videomute": "Включи / Изключи камерата",
|
||||
"audioonly": "Включване/изключване на режима само със звук (пести мрежовата връзка)",
|
||||
"mute": "Спиране/пускане на микрофона",
|
||||
"videomute": "Пускане/спиране на камерата",
|
||||
"authenticate": "Идентификация",
|
||||
"lock": "Заключи / отключи стаята",
|
||||
"invite": "",
|
||||
"chat": "Отвори / затвори чат",
|
||||
"etherpad": "",
|
||||
"sharedvideo": "Сподели YouTube видео",
|
||||
"sharescreen": "",
|
||||
"fullscreen": "Влез / Излез от Пълен екран",
|
||||
"sip": "Обади се на SIP номер",
|
||||
"lock": "Заключване/отключване на стаята",
|
||||
"invite": "Споделете връзката",
|
||||
"chat": "Отваряне/затваряне на текстовите съобщения",
|
||||
"etherpad": "Отваряне/затваряне на споделен документ",
|
||||
"sharedvideo": "Пускане/спиране на споделянето на екрана",
|
||||
"sharescreen": "Пускане/спиране на споделянето на екрана",
|
||||
"fullscreen": "Влизане/излизане от режим на цял екран",
|
||||
"sip": "Обаждане на SIP-номер",
|
||||
"Settings": "Настройки",
|
||||
"hangup": "",
|
||||
"hangup": "Напускане",
|
||||
"login": "Влез",
|
||||
"logout": "Изход",
|
||||
"dialpad": "",
|
||||
"sharedVideoMutedPopup": "",
|
||||
"micMutedPopup": "",
|
||||
"talkWhileMutedPopup": "",
|
||||
"unableToUnmutePopup": "Не се говори по време на филм!",
|
||||
"dialpad": "Отваряне/затваряне на клавиатурата за набиране",
|
||||
"sharedVideoMutedPopup": "Вашето споделено видео беше заглушено, за да можете да говорите с другите участници.",
|
||||
"micMutedPopup": "Микрофонът Ви беше заглушен, за да можете да се насладите на споделеното видео.",
|
||||
"talkWhileMutedPopup": "Опитвате се да говорите? В момента микрофонът Ви е заглушен.",
|
||||
"unableToUnmutePopup": "Не можете да пуснете микрофона докато гледате споделено видео.",
|
||||
"cameraDisabled": "Не е налична камера",
|
||||
"micDisabled": "Не е наличен микрофон",
|
||||
"filmstrip": "",
|
||||
"profile": "",
|
||||
"raiseHand": ""
|
||||
"filmstrip": "Показване/скриване на видеата",
|
||||
"profile": "Редактиране на профила",
|
||||
"raiseHand": "Вдигане/сваляне на ръка"
|
||||
},
|
||||
"\u0005toolbar": {},
|
||||
"unsupportedBrowser": {
|
||||
"appInstalled": "",
|
||||
"appNotInstalled": "",
|
||||
"downloadApp": "",
|
||||
"joinConversation": "",
|
||||
"startConference": ""
|
||||
"appInstalled": "или ако вече имате това приложение<br /><strong>тогава</strong>",
|
||||
"appNotInstalled": "Трябва да имате <strong>__app__</strong>, за да можете да се присъедините към беседата от мобилното си устройство",
|
||||
"downloadApp": "Сваляне не приложението",
|
||||
"joinConversation": "Присъединяване",
|
||||
"startConference": "Започване на беседа"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Отвори / затвори чат",
|
||||
"filmstrip": "",
|
||||
"contactlist": ""
|
||||
"chat": "Отваряне/затваряне на текстовите съобщения",
|
||||
"filmstrip": "Показване/скриване на видеата",
|
||||
"contactlist": "Преглед и покана на участници"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
@@ -154,42 +152,42 @@
|
||||
"title": "Настройки",
|
||||
"update": "Актуализиране",
|
||||
"name": "Име",
|
||||
"startAudioMuted": "",
|
||||
"startVideoMuted": "",
|
||||
"selectCamera": "",
|
||||
"selectMic": "",
|
||||
"selectAudioOutput": "",
|
||||
"followMe": "",
|
||||
"startAudioMuted": "Всички започват заглушени",
|
||||
"startVideoMuted": "Всички започват скрити",
|
||||
"selectCamera": "Камера",
|
||||
"selectMic": "Микрофон",
|
||||
"selectAudioOutput": "Звуков изход",
|
||||
"followMe": "Всички ме следват",
|
||||
"noDevice": "Няма",
|
||||
"cameraAndMic": "",
|
||||
"moderator": "",
|
||||
"password": "",
|
||||
"audioVideo": ""
|
||||
"cameraAndMic": "Камера и микрофон",
|
||||
"moderator": "МОДЕРАТОР",
|
||||
"password": "ЗАДАВАНЕ НА ПАРОЛА",
|
||||
"audioVideo": "ЗВУК И ВИДЕО"
|
||||
},
|
||||
"profile": {
|
||||
"title": "",
|
||||
"setDisplayNameLabel": "",
|
||||
"setEmailLabel": "",
|
||||
"setEmailInput": ""
|
||||
"title": "Профил",
|
||||
"setDisplayNameLabel": "Задайте екранното си име",
|
||||
"setEmailLabel": "Задайте е-пощата си в „gravatar“",
|
||||
"setEmailInput": "Въведете е-поща"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"moderator": "Модератор",
|
||||
"videomute": "Участникът спря камерата си",
|
||||
"mute": "Участникът е заглушен",
|
||||
"kick": "Изгони",
|
||||
"muted": "Изключен микрофон",
|
||||
"domute": "Изключи микрофона",
|
||||
"flip": "Огледално",
|
||||
"remoteControl": ""
|
||||
"remoteControl": "Отдалечено управление"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "",
|
||||
"bitrate": "Дебит:",
|
||||
"header": "Данни за връзката",
|
||||
"bitrate": "Скорост:",
|
||||
"packetloss": "Загуба на пакети:",
|
||||
"resolution": "Резолюция:",
|
||||
"framerate": "",
|
||||
"less": "Скрий",
|
||||
"more": "Покажи",
|
||||
"framerate": "Кадри в секунда:",
|
||||
"less": "Скриване",
|
||||
"more": "Показване",
|
||||
"address": "Адрес:",
|
||||
"remoteport": "Отдалечен порт:",
|
||||
"remoteport_plural": "Отдалечени портове:",
|
||||
@@ -200,21 +198,21 @@
|
||||
"remoteaddress": "Отдалечен адрес:",
|
||||
"remoteaddress_plural": "Отдалечени адреси:",
|
||||
"transport": "Транспорт:",
|
||||
"bandwidth": "Оценка макс. дебит:",
|
||||
"na": "Върнете се тук за информацията относно вашата връзка, когато започне конференцията",
|
||||
"turn": "",
|
||||
"transport_plural": "Транспорти:",
|
||||
"bandwidth": "Предполагаема скорост:",
|
||||
"na": "Върнете се тук за информацията относно връзката си, след като започне беседата",
|
||||
"turn": " (обръщане)",
|
||||
"quality": {
|
||||
"good": "",
|
||||
"inactive": "",
|
||||
"lost": "",
|
||||
"nonoptimal": "",
|
||||
"poor": ""
|
||||
"good": "Добра",
|
||||
"inactive": "Няма",
|
||||
"lost": "Разкачена",
|
||||
"nonoptimal": "Неоптимална",
|
||||
"poor": "Лоша"
|
||||
},
|
||||
"status": "Свързване"
|
||||
"status": "Връзка:"
|
||||
},
|
||||
"\u0005connectionindicator": {},
|
||||
"notify": {
|
||||
"disconnected": "връзката е прекъсната",
|
||||
"disconnected": "Връзка:",
|
||||
"moderator": "Придобихте права на модератор!",
|
||||
"connected": "свързан",
|
||||
"somebody": "Някой",
|
||||
@@ -222,73 +220,72 @@
|
||||
"focus": "Конферентен фокус",
|
||||
"focusFail": "__component__ не е на раположения - следващ опит след __ms__ секунди",
|
||||
"grantedTo": "Даване на роля модератор на __to__!",
|
||||
"grantedToUnknown": "Даване на роля модератор на $t(somebody)!",
|
||||
"grantedToUnknown": "$t(notify.somebody) получи ролята на модератор!",
|
||||
"muted": "Започвате разговора без звук.",
|
||||
"mutedTitle": "Звукът ви е спрян!",
|
||||
"raisedHand": ""
|
||||
"raisedHand": "Иска думата."
|
||||
},
|
||||
"\u0005notify": {},
|
||||
"dialog": {
|
||||
"add": "",
|
||||
"allow": "",
|
||||
"kickMessage": "Бяхте изгонен от срещата!",
|
||||
"popupErrorTitle": "",
|
||||
"popupError": "",
|
||||
"passwordErrorTitle": "",
|
||||
"allow": "Разрешаване",
|
||||
"kickMessage": "Ох! Бяхте изритан(а) от срещата!",
|
||||
"popupErrorTitle": "Блокиран изскачащ прозорец",
|
||||
"popupError": "Браузърът Ви блокира изскачащите прозорци от този уеб сайт. Моля, разрешете изскачащите прозорци от настройките за сигурност на браузъра си и след това опитайте отново.",
|
||||
"passwordErrorTitle": "Грешка с паролата",
|
||||
"passwordError": "Стаята е защитена с парола. Само собственикът може да я променя или премахва.",
|
||||
"passwordError2": "Стаята ви не е защитена с парола. Собственикът би могъл да добави парола.",
|
||||
"connectError": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията.",
|
||||
"connectErrorWithMsg": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията: __msg__",
|
||||
"incorrectPassword": "",
|
||||
"incorrectPassword": "Неправилно потребителско име или парола",
|
||||
"connecting": "Свързване",
|
||||
"copy": "",
|
||||
"contactSupport": "",
|
||||
"copy": "Копиране",
|
||||
"contactSupport": "Връзка с отдела по поддръжка",
|
||||
"error": "Грешка",
|
||||
"createPassword": "",
|
||||
"createPassword": "Създаване на парола",
|
||||
"detectext": "Възникна грешка при опит да бъде намерено разширението за споделяне на екран.",
|
||||
"failedpermissions": "Неуспешен опит за получаване на права за използване на микрофон и/или камера.",
|
||||
"conferenceReloadTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"dismiss": "",
|
||||
"rejoinNow": "",
|
||||
"maxUsersLimitReachedTitle": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"conferenceReloadTitle": "За съжаление, нещо се обърка.",
|
||||
"conferenceReloadMsg": "Опитваме се да оправим нещата. Повторно свързване след __seconds__ сек…",
|
||||
"conferenceDisconnectTitle": "Връзката се разпадна.",
|
||||
"conferenceDisconnectMsg": "Може би трябва да проверите мрежовата си връзка. Повторно свързване след __seconds__ сек…",
|
||||
"dismiss": "Отхвърляне",
|
||||
"rejoinNow": "Повторно присъединяване сега",
|
||||
"maxUsersLimitReachedTitle": "Достигнато е ограничението за максимален брой участници",
|
||||
"maxUsersLimitReached": "Достигнато е ограничението за максимален брой участници. Беседата е пълна. Моля, свържете се с организатора на срещата или опитайте отново по-късно!",
|
||||
"lockTitle": "Неуспешно заключване",
|
||||
"lockMessage": "Неуспешно заключване на конференцията.",
|
||||
"warning": "Внимание",
|
||||
"passwordNotSupportedTitle": "",
|
||||
"passwordNotSupported": "",
|
||||
"internalErrorTitle": "",
|
||||
"internalError": "",
|
||||
"passwordNotSupportedTitle": "Не се поддържа парола",
|
||||
"passwordNotSupported": "Задаването на парола за срещата не се поддържа.",
|
||||
"internalErrorTitle": "Вътрешна грешка",
|
||||
"internalError": "Опа! Нещо се обърка. Възникна следната грешка: __error__",
|
||||
"unableToSwitch": "Неуспешен опит за смяна на видеото.",
|
||||
"SLDFailure": "Опа! Нещо се обърка и не успяхме да спрем микрофона! (SLD Failure)",
|
||||
"SRDFailure": "Опа! Нещо се обърка и не успяхме да спрем камерата! (SRD Failure)",
|
||||
"oops": "Опа!",
|
||||
"currentPassword": "",
|
||||
"passwordLabel": "",
|
||||
"currentPassword": "Текущата парола е",
|
||||
"passwordLabel": "Парола",
|
||||
"defaultError": "Възникна грешка",
|
||||
"passwordRequired": "Изисква се парола",
|
||||
"Ok": "ОК",
|
||||
"done": "Няма",
|
||||
"Remove": "DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE ",
|
||||
"removePassword": "",
|
||||
"Ok": "Готово",
|
||||
"done": "Готово",
|
||||
"Remove": "Премахване",
|
||||
"removePassword": "Премахване на паролата",
|
||||
"shareVideoTitle": "Сподели видео",
|
||||
"shareVideoLinkError": "Моля въведете правилен Prezi линк.",
|
||||
"shareVideoLinkError": "Моля въведете правилна връзка към YouTube.",
|
||||
"removeSharedVideoTitle": "Край на споделянето на видео",
|
||||
"removeSharedVideoMsg": "Сигурни ли сте, че искате да премахнете Prezi?",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"alreadySharedVideoTitle": "",
|
||||
"removeSharedVideoMsg": "Наистина ли искате да премахнете споделеното си видео?",
|
||||
"alreadySharedVideoMsg": "Друг участник вече споделя видео. Тази беседа позволява споделянето само на едно видео в даден момент.",
|
||||
"alreadySharedVideoTitle": "Разрешено е споделянето само на едно видео в даден момент",
|
||||
"WaitingForHost": "Чакаме домакина ...",
|
||||
"WaitForHostMsg": "<b>__room__ </b> още не е започнал. Ако вие сте домакина моля идентифицирайте се. В противен случай ще изчакаме домакина.",
|
||||
"WaitForHostMsg": "Беседата <b>__room__ </b> още не е започнала. Ако Вие сте домакинът, моля, идентифицирайте се. В противен случай, моля, изчакайте домакина.",
|
||||
"IamHost": "Аз съм домакина",
|
||||
"Cancel": "Отказ",
|
||||
"Submit": "",
|
||||
"Submit": "Изпращане",
|
||||
"retry": "Повторен опит",
|
||||
"logoutTitle": "Изход",
|
||||
"logoutQuestion": "Сигурни ли сте, че искате да излезете и да прекъснете конференцията?",
|
||||
"sessTerminated": "",
|
||||
"sessTerminated": "Разговорът приключи",
|
||||
"hungUp": "Затворихте",
|
||||
"joinAgain": "Присъединете се отново",
|
||||
"Share": "Споделяне",
|
||||
@@ -297,75 +294,75 @@
|
||||
"recordingToken": "Въведете код за достъп за запис на конференцията",
|
||||
"passwordCheck": "Сигурни ли сте, че искате да махнете паролата?",
|
||||
"passwordMsg": "Въведете парола, за да заключите стаята",
|
||||
"shareLink": "",
|
||||
"yourPassword": "потребителска парола",
|
||||
"shareLink": "Споделете връзката към разговора",
|
||||
"yourPassword": "Въведете нова парола",
|
||||
"Back": "Назад",
|
||||
"serviceUnavailable": "Услугата не е налична",
|
||||
"gracefulShutdown": "Услугата временно не е достъпна поради профилактика. Моля опитайте по-късно.",
|
||||
"Yes": "Да",
|
||||
"reservationError": "Грешка в системата за резервации",
|
||||
"reservationErrorMsg": "Грешка номер: __code__, съобщение: __msg__",
|
||||
"password": "потребителска парола",
|
||||
"password": "Въведете парола",
|
||||
"userPassword": "потребителска парола",
|
||||
"token": "код за достъп",
|
||||
"tokenAuthFailedTitle": "Неуспешна идентификация",
|
||||
"tokenAuthFailed": "",
|
||||
"displayNameRequired": "",
|
||||
"enterDisplayName": "Въведете името си:",
|
||||
"extensionRequired": "Изисква се разширение: ",
|
||||
"tokenAuthFailed": "Съжаляваме, но не можете да се присъедините към този разговор.",
|
||||
"displayNameRequired": "Изисква се екранно име",
|
||||
"enterDisplayName": "Въведете екранното си име",
|
||||
"extensionRequired": "Нужно е разширение:",
|
||||
"firefoxExtensionPrompt": "За да споделите екрана си, трябва да инсталирате Firefox разширение. <a href='__url__'>свалете го тук</a> и пробвайте пак!",
|
||||
"feedbackHelp": "",
|
||||
"feedbackQuestion": "",
|
||||
"feedbackHelp": "Вашето мнение ще ни помогне да подобрим видео услугата.",
|
||||
"feedbackQuestion": "Кажете ни какво мислите за разговора!",
|
||||
"thankYou": "Благодарим, че използвахте __appName__!",
|
||||
"sorryFeedback": "Много съжаляваме! Бихте ли ни казали повече?",
|
||||
"liveStreaming": "Пряко предаване",
|
||||
"streamKey": "Имке/ключ на потока",
|
||||
"startLiveStreaming": "Пряко предаване",
|
||||
"stopStreamingWarning": "Наистина ли искате да спрем прякото предаване?",
|
||||
"startLiveStreaming": "Започване на излъчване на живо",
|
||||
"stopStreamingWarning": "Наистина ли искате да спрете излъчването на живо?",
|
||||
"stopRecordingWarning": "Наистина ли искате да спрем записа?",
|
||||
"stopLiveStreaming": "Край на прякото предаване",
|
||||
"stopLiveStreaming": "Спиране на излъчването на живо",
|
||||
"stopRecording": "Край на записа",
|
||||
"doNotShowWarningAgain": "Занапред не ме предупреждавай пак",
|
||||
"doNotShowMessageAgain": "Не показвай повече тази страница",
|
||||
"doNotShowMessageAgain": "Да не се показва повече това съобщение",
|
||||
"permissionDenied": "Достъпът е отказан",
|
||||
"screenSharingFailedToInstall": "",
|
||||
"screenSharingFailedToInstallTitle": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"micErrorPresent": "Грешка при достъп до микрофона",
|
||||
"screenSharingFailedToInstall": "Опа! Разширението за споделяне на екрана не успя да се инсталира.",
|
||||
"screenSharingFailedToInstallTitle": "Разширението за споделяне на екрана не успя да се инсталира",
|
||||
"screenSharingPermissionDeniedError": "Опа! Нещо се обърка с разрешенията на разширението за споделяне на екрана. Моля, презаредете и опитайте отново.",
|
||||
"micErrorPresent": "Възникна грешка при свързването с микрофона Ви.",
|
||||
"cameraErrorPresent": "Грешка при достъп до камерата.",
|
||||
"cameraUnsupportedResolutionError": "Камерата ви не поддържа тази резолюция",
|
||||
"cameraUnsupportedResolutionError": "Камерата Ви не поддържа нужната резолюция.",
|
||||
"cameraUnknownError": "Невъзможен достъп до камерата по неясна причина.",
|
||||
"cameraPermissionDeniedError": "",
|
||||
"cameraNotFoundError": "",
|
||||
"cameraConstraintFailedError": "Камерата ви не задоволява зададените условия.",
|
||||
"cameraPermissionDeniedError": "Не сте дали разрешение за използване на камерата. Ще можете да се присъедините в беседата, но другите няма да Ви виждат. Използвайте бутона с камерата в адресната лента, за да оправите това.",
|
||||
"cameraNotFoundError": "Не е открита камера.",
|
||||
"cameraConstraintFailedError": "Камерата Ви не покрива някои от изискванията.",
|
||||
"micUnknownError": "Не възможен достъп до микрофона по неясна причина.",
|
||||
"micPermissionDeniedError": "",
|
||||
"micNotFoundError": "",
|
||||
"micConstraintFailedError": "Микрофонът не задоволява зададените условия.",
|
||||
"micNotSendingDataTitle": "",
|
||||
"micNotSendingData": "",
|
||||
"cameraNotSendingDataTitle": "",
|
||||
"cameraNotSendingData": "",
|
||||
"goToStore": "",
|
||||
"externalInstallationTitle": "Изисква се разширение: ",
|
||||
"micPermissionDeniedError": "Не сте дали разрешение за използване на микрофона. Ще можете да се присъедините в беседата, но другите няма да Ви чуват. Използвайте бутона с камерата в адресната лента, за да оправите това.",
|
||||
"micNotFoundError": "Не е открит микрофон.",
|
||||
"micConstraintFailedError": "Микрофонът Ви не покрива някои от изискванията.",
|
||||
"micNotSendingDataTitle": "Микрофонът е недостъпен",
|
||||
"micNotSendingData": "Микрофонът Ви е недостъпен. Моля, изберете друго устройство от менюто с настройките или опитайте да презаредите приложението.",
|
||||
"cameraNotSendingDataTitle": "Камерата е недостъпна",
|
||||
"cameraNotSendingData": "Камерата е недостъпна. Моля, проверете дали друго приложение не използва това устройство, изберете друго устройство от менюто с настройките, или презаредете приложението.",
|
||||
"goToStore": "Към магазина в Интернет",
|
||||
"externalInstallationTitle": "Нужно е разширение",
|
||||
"externalInstallationMsg": "Неуспешна инсталация на разширението за споделяне на екрана.",
|
||||
"inlineInstallationMsg": "Неуспешна инсталация на разширението за споделяне на екрана.",
|
||||
"inlineInstallExtension": "",
|
||||
"muteParticipantTitle": "",
|
||||
"muteParticipantBody": "",
|
||||
"inlineInstallationMsg": "Трябва да инсталирате разширението за споделяне на екрана.",
|
||||
"inlineInstallExtension": "Инсталиране сега",
|
||||
"muteParticipantTitle": "Заглушаване на този участник?",
|
||||
"muteParticipantBody": "Вие няма да можете да спрете заглушаването на участника, но той ще може да го направи по всяко време.",
|
||||
"muteParticipantButton": "Изключи микрофона",
|
||||
"remoteControlTitle": "",
|
||||
"remoteControlRequestMessage": "",
|
||||
"remoteControlShareScreenWarning": "",
|
||||
"remoteControlDeniedMessage": "",
|
||||
"remoteControlAllowedMessage": "",
|
||||
"remoteControlErrorMessage": "",
|
||||
"startRemoteControlErrorMessage": "",
|
||||
"remoteControlStopMessage": "",
|
||||
"close": "",
|
||||
"shareYourScreen": "",
|
||||
"yourEntireScreen": "",
|
||||
"applicationWindow": ""
|
||||
"remoteControlTitle": "Отдалечено управление на компютъра",
|
||||
"remoteControlRequestMessage": "Ще позволите ли на __user__ да управлява отдалечено компютъра Ви?",
|
||||
"remoteControlShareScreenWarning": "Ако натиснете „Разрешаване“, ще споделите екрана си!",
|
||||
"remoteControlDeniedMessage": "__user__ отказа заявката Ви за отдалечено управление!",
|
||||
"remoteControlAllowedMessage": "__user__ прие заявката Ви за отдалечено управление!",
|
||||
"remoteControlErrorMessage": "Възникна грешка при опита за искана на разрешение за отдалечено управление от __user__!",
|
||||
"startRemoteControlErrorMessage": "Възникна грешка при опита за започване на сесията за отдалечено управление!",
|
||||
"remoteControlStopMessage": "Сесията за отдалечено управление приключи!",
|
||||
"close": "Затваряне",
|
||||
"shareYourScreen": "Споделяне на екрана",
|
||||
"yourEntireScreen": "Целия екран",
|
||||
"applicationWindow": "Прозореца на програмата"
|
||||
},
|
||||
"\u0005dialog": {},
|
||||
"email": {
|
||||
@@ -379,17 +376,20 @@
|
||||
],
|
||||
"subject": "Покана за __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Здравей, Бих искал да те поканя в една __appName__ конференция, която създадох.",
|
||||
"Здравей! Каня те в беседата в „__appName__“, която създадох.",
|
||||
"",
|
||||
"",
|
||||
"Кликни на следния линк за да се присъединиш в конференцията.",
|
||||
"Последвай следната връзка, за да се присъединиш!",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
"__appName__ поддържа __supportedBrowsers__, така че трябва да използваш един от тези браузъри.",
|
||||
"В момента „__appName__“ поддържа следните браузъри: __supportedBrowsers__, така че трябва да използваш един от тях.",
|
||||
"",
|
||||
"",
|
||||
"Ще се видим след секунда!"
|
||||
"Ще се видим след малко!"
|
||||
],
|
||||
"and": "и"
|
||||
},
|
||||
@@ -406,105 +406,104 @@
|
||||
"ATTACHED": "Прикрепен"
|
||||
},
|
||||
"recording": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "Пускане / спиране на запис",
|
||||
"busy": "Работим върху това да освободим ресурси за запис. Моля, опитайте отново след няколко минути.",
|
||||
"busyTitle": "Всички възможности за запис в момента са заети",
|
||||
"buttonTooltip": "Пускане/спиране на записа",
|
||||
"error": "Грешка при опит за запис. Моля опитайте отново.",
|
||||
"failedToStart": "Неуспешен опит за записване",
|
||||
"off": "Записът спрян",
|
||||
"on": "Запис",
|
||||
"pending": "Записът изчаква участник.",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
"pending": "Записът изчаква присъединяването на участник…",
|
||||
"unavailable": "Опа! Услугата за запис в момента е недостъпна. Работим върху отстраняването на проблема. Моля, опитайте отново по-късно.",
|
||||
"unavailableTitle": "Записът е невъзможен"
|
||||
},
|
||||
"\u0005recording": {},
|
||||
"liveStreaming": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"failedToStart": "",
|
||||
"off": "",
|
||||
"on": "Пряко предаване",
|
||||
"pending": "Започва пряко предаване...",
|
||||
"streamIdRequired": "",
|
||||
"streamIdHelp": "",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
"busy": "Работим върху това да освободим ресурси за излъчване. Моля, опитайте отново след няколко минути.",
|
||||
"busyTitle": "Всички излъчватели в момента са заети.",
|
||||
"buttonTooltip": "Пускане/спиране на излъчването на живо",
|
||||
"error": "Излъчването на живо беше неуспешно. Моля, опитайте отново.",
|
||||
"failedToStart": "Излъчването на живо не успя да започне",
|
||||
"off": "Край на излъчването на живо",
|
||||
"on": "Излъчване на живо",
|
||||
"pending": "Започване на излъчването на живо…",
|
||||
"streamIdRequired": "Моля, попълнете идентификатора на потока, за да може да започне излъчването на живо.",
|
||||
"streamIdHelp": "Къде да намеря това?",
|
||||
"unavailable": "Опа! Услугата за излъчване на живо в момента е недостъпна. Работим върху отстраняването на проблема. Моля, опитайте отново по-късно.",
|
||||
"unavailableTitle": "Излъчването на живо е недостъпно"
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"hours": "__count__ч",
|
||||
"minutes": "__count__мин",
|
||||
"name": "Име",
|
||||
"seconds": "",
|
||||
"speakerStats": "",
|
||||
"speakerTime": ""
|
||||
"seconds": "__count__сек",
|
||||
"speakerStats": "Статистика на говорителя",
|
||||
"speakerTime": "Време на говорене"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "",
|
||||
"noPermission": "",
|
||||
"previewUnavailable": "",
|
||||
"selectADevice": "",
|
||||
"testAudio": ""
|
||||
"deviceSettings": "Настройки на устройството",
|
||||
"noPermission": "Не е получено разрешение",
|
||||
"previewUnavailable": "Няма възможност за преглед",
|
||||
"selectADevice": "Изберете устройство",
|
||||
"testAudio": "Проба на звука"
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "",
|
||||
"callNumber": "",
|
||||
"enterID": "",
|
||||
"howToDialIn": "",
|
||||
"hidePassword": "",
|
||||
"inviteTo": "",
|
||||
"invitedYouTo": "",
|
||||
"invitePeople": "",
|
||||
"locked": "",
|
||||
"showPassword": "",
|
||||
"unlocked": ""
|
||||
"addPassword": "Добавяне на парола",
|
||||
"callNumber": "Обаждане на __number__",
|
||||
"enterID": "Въведете ид. на срещата: __conferenceID__ и след това # за набиране от телефон",
|
||||
"howToDialIn": "За да се включите, използвайте един от следните номера и ид. на срещата",
|
||||
"hidePassword": "Скриване на паролата",
|
||||
"inviteTo": "Поканете участници в __conferenceName__",
|
||||
"invitedYouTo": "__userName__ Ви покани в беседата __inviteURL__",
|
||||
"invitePeople": "Поканете участници",
|
||||
"locked": "Този разговор е заключен. Новите участници трябва да разполагат с връзката и да въведат паролата, за да се включат.",
|
||||
"showPassword": "Показване на паролата",
|
||||
"unlocked": "Този разговор е отключен. Всеки, който разполага с връзката, може да се присъедини."
|
||||
},
|
||||
"videoStatus": {
|
||||
"callQuality": "",
|
||||
"hd": "",
|
||||
"highDefinition": "",
|
||||
"labelTooltipVideo": "",
|
||||
"labelTooltipAudioOnly": "",
|
||||
"ld": "",
|
||||
"lowDefinition": "",
|
||||
"p2pEnabled": "",
|
||||
"p2pVideoQualityDescription": "",
|
||||
"recHighDefinitionOnly": "",
|
||||
"sd": "",
|
||||
"standardDefinition": "",
|
||||
"qualityButtonTip": ""
|
||||
"callQuality": "Качество на разговора",
|
||||
"hd": "ВК",
|
||||
"highDefinition": "Високо качество",
|
||||
"labelTooltipVideo": "Текущо качество на видеото",
|
||||
"labelTooltipAudioOnly": "Включен е режим само със звук",
|
||||
"ld": "НК",
|
||||
"lowDefinition": "Ниско качество",
|
||||
"p2pEnabled": "Вкл. директно свързване",
|
||||
"p2pVideoQualityDescription": "В режима на директно свързване качеството на разговора може да бъде превключено само между ниско и високо качество на звука. Всички други настройки ще бъдат пренебрегнати до излизане от режима на директно свързване.",
|
||||
"recHighDefinitionOnly": "Ще се предпочита високо качество.",
|
||||
"sd": "СК",
|
||||
"standardDefinition": "Стандартно качество",
|
||||
"qualityButtonTip": "Промяна на качеството на получаваното видео"
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "Набиране",
|
||||
"dialOut": "",
|
||||
"statusMessage": "",
|
||||
"enterPhone": "",
|
||||
"phoneNotAllowed": ""
|
||||
"dialOut": "Обаждане на #",
|
||||
"statusMessage": "в момента е __status__",
|
||||
"enterPhone": "Въведете телефонен номер",
|
||||
"phoneNotAllowed": "Все още не поддържаме това местоназначение! Съжаляваме!"
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "",
|
||||
"noResults": "",
|
||||
"searchPlaceholder": "",
|
||||
"title": "",
|
||||
"failedToAdd": ""
|
||||
"add": "Добавяне",
|
||||
"noResults": "Няма резултати",
|
||||
"searchPlaceholder": "Потърсете хора или стаи, които да добавите",
|
||||
"title": "Добавяне на участници в разговора",
|
||||
"failedToAdd": "Добавянето на участници беше неуспешно"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "",
|
||||
"retry": "",
|
||||
"support": "",
|
||||
"supportMsg": ""
|
||||
"msg": "Имаше грешка.",
|
||||
"retry": "Опитайте отново",
|
||||
"support": "Поддръжка",
|
||||
"supportMsg": "Ако това се случва често, свържете се с нашата"
|
||||
},
|
||||
"deviceError": {
|
||||
"cameraError": "",
|
||||
"microphoneError": "",
|
||||
"cameraPermission": "",
|
||||
"microphonePermission": ""
|
||||
"cameraError": "Камерата е недостъпна",
|
||||
"microphoneError": "Микрофонът е недостъпен",
|
||||
"cameraPermission": "Грешка при получаване на разрешение за достъп до камерата",
|
||||
"microphonePermission": "Грешка при получаване на разрешение за достъп до микрофона"
|
||||
},
|
||||
"info": {
|
||||
"copy": "",
|
||||
"invite": "",
|
||||
"title": "",
|
||||
"tooltip": ""
|
||||
"copy": "Копиране на връзката",
|
||||
"invite": "Покана в „__app__“",
|
||||
"title": "Информация за достъп до разговора",
|
||||
"tooltip": "Получете информация за достъп до срещата"
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,19 @@
|
||||
"contactlist": "__count__ Teilnehmer",
|
||||
"contactlist_plural": "",
|
||||
"passwordSetRemotely": "von einem anderen Teilnehmer gesetzt",
|
||||
"connectionsettings": "Verbindungseinstellungen",
|
||||
"poweredby": "Betrieben von",
|
||||
"feedback": {
|
||||
"average": "Durschnittlich",
|
||||
"bad": "Schlecht",
|
||||
"good": "Gut",
|
||||
"rateExperience": "Bitte bewerten Sie diese Konferenz.",
|
||||
"veryBad": "Sehr schlecht",
|
||||
"veryGood": "Sehr gut"
|
||||
},
|
||||
"inviteUrlDefaultMsg": "Die Konferenz wird erstellt...",
|
||||
"me": "ich",
|
||||
"speaker": "Sprecher",
|
||||
"raisedHand": "Möchte sprechen",
|
||||
"defaultNickname": "Bsp: Heidi Blau",
|
||||
"defaultLink": "Bsp.: __url__",
|
||||
"callingName": "__name__",
|
||||
"audioDevices": {
|
||||
"bluetooth": "Bluetooth",
|
||||
"headphones": "Kopfhörer",
|
||||
"phone": "Telefon",
|
||||
"speaker": "Sprecher"
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "Nur Audio",
|
||||
"featureToggleDisabled": "Umschalten zwischen __feature__ ist im Audio-Modus deaktiviert"
|
||||
@@ -130,11 +126,9 @@
|
||||
"raiseHand": "Hand erheben"
|
||||
},
|
||||
"unsupportedBrowser": {
|
||||
"appInstalled": "oder wenn die App bereits installiert ist<br/>, <strong>dann</strong>",
|
||||
"appNotInstalled": "Sie benötigen <strong>__app__</strong> um der Konferenz mit dem Mobilgerät beizutreten",
|
||||
"appNotInstalled": "Mit __app__ auf dem Mobiltelefon teilnehmen.",
|
||||
"downloadApp": "App herunterladen",
|
||||
"joinConversation": "Der Konferenz beitreten",
|
||||
"startConference": "Konferenz starten"
|
||||
"openApp": "In __app__ fortfahren"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Chat öffnen / schliessen",
|
||||
@@ -213,7 +207,9 @@
|
||||
"notify": {
|
||||
"disconnected": "getrennt",
|
||||
"moderator": "Moderatorenrechte vergeben",
|
||||
"connected": "verbunden",
|
||||
"connectedOneMember": "__name__ ist beigetreten",
|
||||
"connectedTwoMembers": "__first__ und __second__ sind beigetreten",
|
||||
"connectedThreePlusMembers": "__name__ und __count__ sind beigetreten",
|
||||
"somebody": "Jemand",
|
||||
"me": "Ich",
|
||||
"focus": "Konferenz-Organisator",
|
||||
@@ -321,14 +317,11 @@
|
||||
"stopRecordingWarning": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?",
|
||||
"stopLiveStreaming": "Live-Streaming stoppen",
|
||||
"stopRecording": "Aufnahme stoppen",
|
||||
"doNotShowWarningAgain": "Diesen Hinweis nicht mehr anzeigen",
|
||||
"doNotShowMessageAgain": "Diesen Hinweis nicht mehr anzeigen",
|
||||
"permissionDenied": "Zugriff verweigert",
|
||||
"screenSharingFailedToInstall": "Oh! Die Erweiterung für die Bildschirmfreigabe konnte nicht installiert werden.",
|
||||
"screenSharingFailedToInstallTitle": "Bildschirmfreigabe-Erweiterung konnte nicht installiert werden",
|
||||
"screenSharingPermissionDeniedError": "Oh! Beim Anfordern der Bildschirmfreigabe-Berechtigungen hat etwas nicht funktioniert. Bitte aktualisieren und erneut versuchen.",
|
||||
"micErrorPresent": "Fehler beim Verbinden zum Mikrofon.",
|
||||
"cameraErrorPresent": "Fehler beim Verbinden zur Kamera.",
|
||||
"cameraUnsupportedResolutionError": "Die Kamera unterstützt die erforderliche Auflösung nicht.",
|
||||
"cameraUnknownError": "Die Kamera kann aus einem unbekannten Grund nicht verwendet werden.",
|
||||
"cameraPermissionDeniedError": "Die Berechtigung zur Verwendung der Kamera wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht sehen. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste um die Berechtigungen zu erteilen.",
|
||||
@@ -416,7 +409,8 @@
|
||||
"off": "Aufnahme gestoppt",
|
||||
"on": "Aufnahme",
|
||||
"pending": "Die Aufnahme wartet auf den Beitritt eines Teilnehmers...",
|
||||
"unavailable": "Oh! Der Aufnahme-Service ist momentan nicht verfügbar. Wir arbeiten der Behebung des Problems. Bitte später wieder versuchen.",
|
||||
"serviceName": "Aufnahmedienst",
|
||||
"unavailable": "Oh! Der __serviceName__ ist aktuell nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
|
||||
"unavailableTitle": "Aufnahme nicht verfügbar"
|
||||
},
|
||||
"liveStreaming": {
|
||||
@@ -428,11 +422,23 @@
|
||||
"off": "Live-Streaming gestoppt",
|
||||
"on": "Live-Streaming",
|
||||
"pending": "Live-Stream wird gestartet...",
|
||||
"serviceName": "Live Streaming-Dienst",
|
||||
"streamIdRequired": "Bitte Stream-ID eingeben um das Live-Streaming zu starten.",
|
||||
"streamIdHelp": "Wo ist die Stream-ID zu finden?",
|
||||
"unavailable": "Oh! Der Live-Streaming Dienst ist momentan nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte später wieder versuchen.",
|
||||
"unavailableTitle": "Live-Streaming nicht verfügbar"
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "Es stehen keine freien Ressourcen zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
"busyTitle": "Keine freien Ressourcen",
|
||||
"errorInvite": "Die Konferenz konnte nicht gestartet werden. Bitte versuchen Sie es später noch einmal.",
|
||||
"errorInviteTitle": "Fehler beim Erstellen der Konferenz",
|
||||
"errorAlreadyInvited": "__displayName__ ist bereits eingeladen",
|
||||
"errorInviteFailedTitle": "__displayName__ konnte nicht eingeladen werden",
|
||||
"errorInviteFailed": "Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
|
||||
"pending": "__displayName__ wurde eingeladen",
|
||||
"serviceName": "Raumkonferenz-Dienst",
|
||||
"unavailableTitle": "Raumkonferenz-Dienst nicht verfügbar"
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "__count__h",
|
||||
"minutes": "__count__m",
|
||||
@@ -456,7 +462,7 @@
|
||||
"hidePassword": "Passwort verstecken",
|
||||
"inviteTo": "Teilnehmer zu __conferenceName__ einladen",
|
||||
"invitedYouTo": "Sie wurden von __userName__ zur Konferenz __inviteURL__ eingeladen",
|
||||
"invitePeople": "Teilnehmer einladen",
|
||||
"invitePeople": "Einladen",
|
||||
"locked": "Diese Konferenz ist gesperrt. Neue Teilnehmer müssen das Passwort eingeben um beizutreten.",
|
||||
"showPassword": "Passwort anzeigen",
|
||||
"unlocked": "Die Konferenz ist nicht geschützt. Jeder mit dem Link kann der Konferenz beitreten."
|
||||
@@ -469,6 +475,8 @@
|
||||
"labelTooltipAudioOnly": "Nur-Audio Modus aktiv",
|
||||
"ld": "LD",
|
||||
"lowDefinition": "Niedrige Auflösung",
|
||||
"onlyAudioAvailable": "Nur Ton",
|
||||
"onlyAudioSupported": "In diesem Browser wird nur Audio unterstützt.",
|
||||
"p2pEnabled": "Ende-zu-Ende aktiviert",
|
||||
"p2pVideoQualityDescription": "Im Ende-zu-Ende Modus kann die Konferenzqualität nur zwischen hoch und nur-Audio gewählt werden. Andere Einstellungen werden nicht beachtet.",
|
||||
"recHighDefinitionOnly": "Hohe Qualität wird bevorzugt.",
|
||||
@@ -502,6 +510,14 @@
|
||||
"cameraPermission": "Fehler beim Bezug der Kamera-Zugriffsberechtigungen",
|
||||
"microphonePermission": "Fehler beim Bezug der Mikrofon-Zugriffsberechtigungen"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Durschnittlich",
|
||||
"bad": "Schlecht",
|
||||
"good": "Gut",
|
||||
"rateExperience": "Bitte bewerten Sie diese Konferenz.",
|
||||
"veryBad": "Sehr schlecht",
|
||||
"veryGood": "Sehr gut"
|
||||
},
|
||||
"info": {
|
||||
"copy": "Link kopieren",
|
||||
"invite": "In __app__ einladen",
|
||||
|
||||
486
lang/main-fa.json
Normal file
@@ -0,0 +1,486 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"contactlist_plural_undefined": "",
|
||||
"passwordSetRemotely": "",
|
||||
"connectionsettings": "",
|
||||
"poweredby": "",
|
||||
"feedback": {
|
||||
"average": "",
|
||||
"bad": "",
|
||||
"good": "",
|
||||
"rateExperience": "",
|
||||
"veryBad": "",
|
||||
"veryGood": ""
|
||||
},
|
||||
"inviteUrlDefaultMsg": "",
|
||||
"me": "",
|
||||
"speaker": "",
|
||||
"raisedHand": "",
|
||||
"defaultNickname": "",
|
||||
"defaultLink": "",
|
||||
"callingName": "",
|
||||
"audioOnly": {
|
||||
"audioOnly": "",
|
||||
"featureToggleDisabled": ""
|
||||
},
|
||||
"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": {
|
||||
"addPeople": "",
|
||||
"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": {
|
||||
"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": "",
|
||||
"turn": "",
|
||||
"quality": {
|
||||
"good": "",
|
||||
"inactive": "",
|
||||
"lost": "",
|
||||
"nonoptimal": "",
|
||||
"poor": ""
|
||||
},
|
||||
"status": ""
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": "",
|
||||
"muted": "",
|
||||
"mutedTitle": "",
|
||||
"raisedHand": ""
|
||||
},
|
||||
"dialog": {
|
||||
"add": "",
|
||||
"allow": "",
|
||||
"kickMessage": "",
|
||||
"popupErrorTitle": "",
|
||||
"popupError": "",
|
||||
"passwordErrorTitle": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"connectError": "",
|
||||
"connectErrorWithMsg": "",
|
||||
"incorrectPassword": "",
|
||||
"connecting": "",
|
||||
"copy": "",
|
||||
"contactSupport": "",
|
||||
"error": "",
|
||||
"createPassword": "",
|
||||
"detectext": "",
|
||||
"failedpermissions": "",
|
||||
"conferenceReloadTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"dismiss": "",
|
||||
"rejoinNow": "",
|
||||
"maxUsersLimitReachedTitle": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupportedTitle": "",
|
||||
"passwordNotSupported": "",
|
||||
"internalErrorTitle": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"currentPassword": "",
|
||||
"passwordLabel": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"done": "",
|
||||
"Remove": "",
|
||||
"removePassword": "",
|
||||
"shareVideoTitle": "",
|
||||
"shareVideoLinkError": "",
|
||||
"removeSharedVideoTitle": "",
|
||||
"removeSharedVideoMsg": "",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"alreadySharedVideoTitle": "",
|
||||
"WaitingForHost": "",
|
||||
"WaitForHostMsg": "",
|
||||
"IamHost": "",
|
||||
"Cancel": "",
|
||||
"Submit": "",
|
||||
"retry": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"Save": "",
|
||||
"recording": "",
|
||||
"recordingToken": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"shareLink": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": "",
|
||||
"tokenAuthFailedTitle": "",
|
||||
"tokenAuthFailed": "",
|
||||
"displayNameRequired": "",
|
||||
"enterDisplayName": "",
|
||||
"extensionRequired": "",
|
||||
"firefoxExtensionPrompt": "",
|
||||
"feedbackHelp": "",
|
||||
"feedbackQuestion": "",
|
||||
"thankYou": "",
|
||||
"sorryFeedback": "",
|
||||
"liveStreaming": "",
|
||||
"streamKey": "",
|
||||
"startLiveStreaming": "",
|
||||
"stopStreamingWarning": "",
|
||||
"stopRecordingWarning": "",
|
||||
"stopLiveStreaming": "",
|
||||
"stopRecording": "",
|
||||
"doNotShowWarningAgain": "",
|
||||
"doNotShowMessageAgain": "",
|
||||
"permissionDenied": "",
|
||||
"screenSharingFailedToInstall": "",
|
||||
"screenSharingFailedToInstallTitle": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"micErrorPresent": "",
|
||||
"cameraErrorPresent": "",
|
||||
"cameraUnsupportedResolutionError": "",
|
||||
"cameraUnknownError": "",
|
||||
"cameraPermissionDeniedError": "",
|
||||
"cameraNotFoundError": "",
|
||||
"cameraConstraintFailedError": "",
|
||||
"micUnknownError": "",
|
||||
"micPermissionDeniedError": "",
|
||||
"micNotFoundError": "",
|
||||
"micConstraintFailedError": "",
|
||||
"micNotSendingDataTitle": "",
|
||||
"micNotSendingData": "",
|
||||
"cameraNotSendingDataTitle": "",
|
||||
"cameraNotSendingData": "",
|
||||
"goToStore": "",
|
||||
"externalInstallationTitle": "",
|
||||
"externalInstallationMsg": "",
|
||||
"inlineInstallationMsg": "",
|
||||
"inlineInstallExtension": "",
|
||||
"muteParticipantTitle": "",
|
||||
"muteParticipantBody": "",
|
||||
"muteParticipantButton": "",
|
||||
"remoteControlTitle": "",
|
||||
"remoteControlRequestMessage": "",
|
||||
"remoteControlShareScreenWarning": "",
|
||||
"remoteControlDeniedMessage": "",
|
||||
"remoteControlAllowedMessage": "",
|
||||
"remoteControlErrorMessage": "",
|
||||
"startRemoteControlErrorMessage": "",
|
||||
"remoteControlStopMessage": "",
|
||||
"close": "",
|
||||
"shareYourScreen": "",
|
||||
"yourEntireScreen": "",
|
||||
"applicationWindow": ""
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"and": ""
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "",
|
||||
"CONNECTING": "",
|
||||
"RECONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ATTACHED": ""
|
||||
},
|
||||
"recording": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"failedToStart": "",
|
||||
"off": "",
|
||||
"on": "",
|
||||
"pending": "",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"failedToStart": "",
|
||||
"off": "",
|
||||
"on": "",
|
||||
"pending": "",
|
||||
"streamIdRequired": "",
|
||||
"streamIdHelp": "",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"name": "",
|
||||
"seconds": "",
|
||||
"speakerStats": "",
|
||||
"speakerTime": ""
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "",
|
||||
"noPermission": "",
|
||||
"previewUnavailable": "",
|
||||
"selectADevice": "",
|
||||
"testAudio": ""
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "",
|
||||
"callNumber": "",
|
||||
"enterID": "",
|
||||
"howToDialIn": "",
|
||||
"hidePassword": "",
|
||||
"inviteTo": "",
|
||||
"invitedYouTo": "",
|
||||
"invitePeople": "",
|
||||
"locked": "",
|
||||
"showPassword": "",
|
||||
"unlocked": ""
|
||||
},
|
||||
"videoStatus": {
|
||||
"callQuality": "",
|
||||
"hd": "",
|
||||
"highDefinition": "",
|
||||
"labelTooltipVideo": "",
|
||||
"labelTooltipAudioOnly": "",
|
||||
"ld": "",
|
||||
"lowDefinition": "",
|
||||
"p2pEnabled": "",
|
||||
"p2pVideoQualityDescription": "",
|
||||
"recHighDefinitionOnly": "",
|
||||
"sd": "",
|
||||
"standardDefinition": "",
|
||||
"qualityButtonTip": ""
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "",
|
||||
"dialOut": "",
|
||||
"statusMessage": "",
|
||||
"enterPhone": "",
|
||||
"phoneNotAllowed": ""
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "",
|
||||
"noResults": "",
|
||||
"searchPlaceholder": "",
|
||||
"title": "",
|
||||
"failedToAdd": ""
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "",
|
||||
"retry": "",
|
||||
"support": "",
|
||||
"supportMsg": ""
|
||||
},
|
||||
"deviceError": {
|
||||
"cameraError": "",
|
||||
"microphoneError": "",
|
||||
"cameraPermission": "",
|
||||
"microphonePermission": ""
|
||||
},
|
||||
"info": {
|
||||
"copy": "",
|
||||
"invite": "",
|
||||
"title": "",
|
||||
"tooltip": ""
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,20 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"contactlist": "__count__ Membres",
|
||||
"contactlist_plural": "",
|
||||
"passwordSetRemotely": "défini par un autre utilisateur",
|
||||
"connectionsettings": "Paramètres de connexion",
|
||||
"passwordSetRemotely": "défini par un autre membre",
|
||||
"poweredby": "Produit par",
|
||||
"feedback": {
|
||||
"average": "Moyen",
|
||||
"bad": "Mauvais",
|
||||
"good": "",
|
||||
"rateExperience": "Veuillez évaluer votre réunion.",
|
||||
"veryBad": "Très mauvais",
|
||||
"veryGood": "Très bon"
|
||||
},
|
||||
"inviteUrlDefaultMsg": "Votre conférence est en cours de création...",
|
||||
"me": "moi",
|
||||
"speaker": "Haut-parleur",
|
||||
"raisedHand": "Aimerait prendre la parole",
|
||||
"defaultNickname": "ex. Jean Dupont",
|
||||
"defaultLink": "ex. __url__",
|
||||
"callingName": "__name__",
|
||||
"audioDevices": {
|
||||
"bluetooth": "Bluetooth",
|
||||
"headphones": "Écouteurs",
|
||||
"phone": "Téléphone",
|
||||
"speaker": "Haut-parleur"
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "Audio uniquement",
|
||||
"featureToggleDisabled": "Le basculement de __feature__ est désactivé en mode audio uniquement"
|
||||
@@ -45,7 +41,7 @@
|
||||
"focusRemote": "Épingler la vidéo des autres",
|
||||
"toggleChat": "Ouvrir ou fermer le panneau de conversation",
|
||||
"mute": "Activer ou désactiver le microphone",
|
||||
"fullScreen": "Activer / Désactiver le plein écran",
|
||||
"fullScreen": "Activer ou Désactiver le plein écran",
|
||||
"videoMute": "Démarrer ou arrêter votre caméra",
|
||||
"showSpeakerStats": "Afficher les statistiques de l'interlocuteur"
|
||||
},
|
||||
@@ -64,7 +60,7 @@
|
||||
"title": "Open source"
|
||||
},
|
||||
"feature4": {
|
||||
"content": "Il n'y a pas de limitation sur le nombre d'utilisateurs ou de conférences. Seuls la puissance et la bande passante du serveur sont des facteurs limitants.",
|
||||
"content": "Il n'y a pas de limitation sur le nombre d'utilisateurs ou de conférences. Seules la puissance et la bande passante du serveur sont des facteurs limitants.",
|
||||
"title": "Nombre d'utilisateurs illimité"
|
||||
},
|
||||
"feature5": {
|
||||
@@ -92,16 +88,16 @@
|
||||
"terms": "Termes"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
"policyText": " ",
|
||||
"title": " __app__ a besoin d'accéder à votre microphone et votre caméra."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"title": "Votre visioconférence a été arrêté parce que votre ordinateur s'est mis en veille.",
|
||||
"title": "Votre visioconférence s'est interrompue parce que votre ordinateur s'est mis en veille.",
|
||||
"text": "Cliquez sur le bouton <i>Rejoindre</i> pour se reconnecter.",
|
||||
"rejoinKeyTitle": "Rejoindre"
|
||||
},
|
||||
"toolbar": {
|
||||
"addPeople": "",
|
||||
"addPeople": "Ajouter des personnes à votre appel",
|
||||
"audioonly": "Activer / Désactiver le mode audio uniquement (économiser de la bande passante)",
|
||||
"mute": "Muet / Actif",
|
||||
"videomute": "Démarrer / Arrêter la caméra",
|
||||
@@ -120,7 +116,7 @@
|
||||
"logout": "Déconnexion",
|
||||
"dialpad": "Ouvrir / Fermer le pavé numérique",
|
||||
"sharedVideoMutedPopup": "Votre vidéo a été coupée pour que vous puissiez parler aux autres participants.",
|
||||
"micMutedPopup": "Votre microphone est désormais en muet pour pouvoir profiter de la votre vidéo.",
|
||||
"micMutedPopup": "Votre microphone a été coupé afin que vous puissiez profiter de la vidéo partagée",
|
||||
"talkWhileMutedPopup": "Vous voulez parler? Vous êtes en muet.",
|
||||
"unableToUnmutePopup": "Vous ne pouvez pas réactiver votre microphone pendant que la vidéo partagée est activée.",
|
||||
"cameraDisabled": "La camera n'est pas disponible",
|
||||
@@ -129,13 +125,10 @@
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever / Baisser la main"
|
||||
},
|
||||
"\u0005toolbar": {},
|
||||
"unsupportedBrowser": {
|
||||
"appInstalled": "ou si vous l'avez déjà installé<br /><strong></strong>",
|
||||
"appNotInstalled": "Vous devez installer <strong>__app__</strong> pour rejoindre une conversation sur votre mobile",
|
||||
"downloadApp": "Télécharger l'Application",
|
||||
"joinConversation": "Rejoindre la discussion de groupe",
|
||||
"startConference": "Démarrer une conférence"
|
||||
"appNotInstalled": "Rejoignez cette réunion avec __app__ sur votre téléphone.",
|
||||
"downloadApp": "Télécharger l'application",
|
||||
"openApp": "Continuer sur __app__"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Ouvrir / fermer le chat",
|
||||
@@ -178,7 +171,7 @@
|
||||
"kick": "Exclure",
|
||||
"muted": "Muet",
|
||||
"domute": "Couper le micro",
|
||||
"flip": "",
|
||||
"flip": "Balancer",
|
||||
"remoteControl": "Contrôle à distance"
|
||||
},
|
||||
"connectionindicator": {
|
||||
@@ -189,34 +182,35 @@
|
||||
"framerate": "Images par seconde",
|
||||
"less": "Cacher le détail",
|
||||
"more": "Montrer le détail",
|
||||
"address": "Adresse:",
|
||||
"address": "Adresse :",
|
||||
"remoteport": "Port distant:",
|
||||
"remoteport_plural": "Ports distants:",
|
||||
"localport": "Port local:",
|
||||
"localport_plural": "Ports locaux:",
|
||||
"localaddress": "Adresse locale:",
|
||||
"localaddress_plural": "Adresses locales:",
|
||||
"remoteaddress": "Adresse distante:",
|
||||
"remoteaddress_plural": "Adresses distantes:",
|
||||
"transport": "Transport:",
|
||||
"transport_plural": "",
|
||||
"localport": "Port local :",
|
||||
"localport_plural": "Ports locaux :",
|
||||
"localaddress": "Adresse locale :",
|
||||
"localaddress_plural": "Adresses locales :",
|
||||
"remoteaddress": "Adresse distante :",
|
||||
"remoteaddress_plural": "Adresses distantes :",
|
||||
"transport": "Transport :",
|
||||
"transport_plural": "Transports :",
|
||||
"bandwidth": "Bande passante estimée :",
|
||||
"na": "Revenez ici pour afficher les informations de connexion une fois la conférence démarrée",
|
||||
"turn": "",
|
||||
"turn": "(tour)",
|
||||
"quality": {
|
||||
"good": "",
|
||||
"inactive": "",
|
||||
"lost": "",
|
||||
"nonoptimal": "",
|
||||
"poor": ""
|
||||
"good": "Bien",
|
||||
"inactive": "Inactif",
|
||||
"lost": "Perdu",
|
||||
"nonoptimal": "Non-optimale",
|
||||
"poor": "Mauvaise"
|
||||
},
|
||||
"status": "Connexion en cours"
|
||||
"status": "Connexion:"
|
||||
},
|
||||
"\u0005connectionindicator": {},
|
||||
"notify": {
|
||||
"disconnected": "déconnecté",
|
||||
"moderator": "Droits modérateur accordés !",
|
||||
"connected": "connecté",
|
||||
"connectedOneMember": "__name__ connecté",
|
||||
"connectedTwoMembers": "__first__ et __second__ connectés",
|
||||
"connectedThreePlusMembers": "__name__ and __count__ others connectés",
|
||||
"somebody": "Quelqu'un",
|
||||
"me": "Moi",
|
||||
"focus": "Focus de conférence",
|
||||
@@ -225,23 +219,23 @@
|
||||
"grantedToUnknown": "Droits modérateur accordés à $t(notify.somebody)!",
|
||||
"muted": "Vous avez commencé la conversation en muet.",
|
||||
"mutedTitle": "Vous êtes en muet !",
|
||||
"raisedHand": "Aimerais prendre la parole."
|
||||
"raisedHand": "Aimerait prendre la parole."
|
||||
},
|
||||
"dialog": {
|
||||
"add": "Ajouter",
|
||||
"allow": "Autoriser",
|
||||
"kickMessage": "Oups! Vous avez été renvoyé de la réunion !",
|
||||
"popupErrorTitle": "",
|
||||
"popupError": "",
|
||||
"popupErrorTitle": "Pop-up bloquée",
|
||||
"popupError": "Votre navigateur bloque les fenêtres pop-up. Veuillez autoriser les fenêtres pop-up dans les paramètres de votre navigateur.",
|
||||
"passwordErrorTitle": "Problème avec le mot de passe",
|
||||
"passwordError": "Cette conversation est actuellement protégée par un mot de passe. Seul le propriétaire de la conférence peut définir un mot de passe.",
|
||||
"passwordError2": "Cette conversation n'est actuellement pas protégé par un mot de passe. Seul le propriétaire de la conférence peut définir un mot de passe.",
|
||||
"connectError": "Oups! Un problème est survenu et la connexion à la conférence est impossible.",
|
||||
"connectErrorWithMsg": "Oups! Un problème est survenu et la connexion à la conférence est impossible: __msg__",
|
||||
"incorrectPassword": "",
|
||||
"incorrectPassword": "Nom d'utilisateur ou mot de passe incorrect",
|
||||
"connecting": "Connexion en cours",
|
||||
"copy": "Copier",
|
||||
"contactSupport": "",
|
||||
"contactSupport": "Contacter le support",
|
||||
"error": "Erreur",
|
||||
"createPassword": "Créer un mot de passe",
|
||||
"detectext": "Une erreur est survenue pendant la détection de l'extension de partage d'écran.",
|
||||
@@ -250,17 +244,17 @@
|
||||
"conferenceReloadMsg": "Nous somme en train de régler cela. Reconnexion dans __seconds__ sec...",
|
||||
"conferenceDisconnectTitle": "Vous avez été déconnecté.",
|
||||
"conferenceDisconnectMsg": "Veuillez vérifier votre connexion réseau. Reconnexion dans __seconds__ sec...",
|
||||
"dismiss": "",
|
||||
"dismiss": "Rejeter",
|
||||
"rejoinNow": "Rejoindre maintenant",
|
||||
"maxUsersLimitReachedTitle": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"maxUsersLimitReachedTitle": "Le nombre maximal de participants est atteint",
|
||||
"maxUsersLimitReached": "Le nombre maximal de participants est atteint. La conférence est complète. Merci de contacter le propriétaire du salon ou réessayer plus tard.",
|
||||
"lockTitle": "Échec du verrouillage",
|
||||
"lockMessage": "Impossible de verrouiller la conférence.",
|
||||
"warning": "Avertissement",
|
||||
"passwordNotSupportedTitle": "",
|
||||
"passwordNotSupported": "",
|
||||
"passwordNotSupportedTitle": "Mot de passe non supporté",
|
||||
"passwordNotSupported": "La définition d'un mot de passe de conférence n'est pas prise en charge.",
|
||||
"internalErrorTitle": "Erreur interne",
|
||||
"internalError": "",
|
||||
"internalError": "Oups! Quelque chose s'est mal passée. L'erreur suivante s'est produite: __error__",
|
||||
"unableToSwitch": "Impossible de changer le flux vidéo.",
|
||||
"SLDFailure": "Oups! Un problème est survenu et le micro n'a pas été coupé! (Échec SLD)",
|
||||
"SRDFailure": "Oups! Un problème est survenu et la caméra n'a pas été coupée! (Échec SRD)",
|
||||
@@ -277,8 +271,8 @@
|
||||
"shareVideoLinkError": "Fournissez s'il vous plaît un lien Youtube fonctionnel.",
|
||||
"removeSharedVideoTitle": "Supprimer la vidéo partagée",
|
||||
"removeSharedVideoMsg": "Voulez-vous vraiment supprimer votre vidéo partagée ?",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"alreadySharedVideoTitle": "",
|
||||
"alreadySharedVideoMsg": "Un autre membre partage déjà une vidéo. Cette conférence n'autorise qu'une seule vidéo partagée à la fois.",
|
||||
"alreadySharedVideoTitle": "Une seule vidéo partagée est autorisée à la fois",
|
||||
"WaitingForHost": "En attente de l'hôte ...",
|
||||
"WaitForHostMsg": "La conférence <b>__room__</b> n'a pas encore commencé. Si vous êtes l'hôte, merci de vous authentifier. Sinon, merci d'attendre que l'hôte arrive.",
|
||||
"IamHost": "Je suis l’hôte",
|
||||
@@ -287,7 +281,7 @@
|
||||
"retry": "Réessayer",
|
||||
"logoutTitle": "Déconnexion",
|
||||
"logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence ?",
|
||||
"sessTerminated": "",
|
||||
"sessTerminated": "Appel terminé",
|
||||
"hungUp": "Vous avez raccroché",
|
||||
"joinAgain": "Rejoignez à nouveau la conférence",
|
||||
"Share": "Partager",
|
||||
@@ -324,14 +318,11 @@
|
||||
"stopRecordingWarning": "Désirez-vous vraiment arrêter l'enregistrement?",
|
||||
"stopLiveStreaming": "Arrêter le direct",
|
||||
"stopRecording": "Arrêter l'enregistrement",
|
||||
"doNotShowWarningAgain": "Ne plus afficher cet avertissement",
|
||||
"doNotShowMessageAgain": "Ne plus afficher ce message",
|
||||
"permissionDenied": "Permission refusée",
|
||||
"screenSharingFailedToInstall": "",
|
||||
"screenSharingFailedToInstallTitle": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"micErrorPresent": "Une erreur est survenue lors de la connexion à votre microphone.",
|
||||
"cameraErrorPresent": "Votre caméra ne satisfait pas certaines des contraintes nécessaires.",
|
||||
"screenSharingFailedToInstall": "Oups! Votre extension de partage d'écran n'a pas pu être installée.",
|
||||
"screenSharingFailedToInstallTitle": "L'extension de partage d'écran n'a pas pu être installée",
|
||||
"screenSharingPermissionDeniedError": "Oups! Une erreur s'est produite avec vos autorisations d'extension de partage d'écran. Veuillez rafraîchir et réessayer.",
|
||||
"cameraUnsupportedResolutionError": "Votre appareil ne prend pas en charge la résolution vidéo requise.",
|
||||
"cameraUnknownError": "Vous ne pouvez pas utiliser la caméra pour une raison inconnue.",
|
||||
"cameraPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre caméra. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous voir. Utilisez le bouton de la caméra dans la barre d'adresse pour résoudre ce problème.",
|
||||
@@ -341,16 +332,16 @@
|
||||
"micPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre microphone. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous entendre. Utilisez le bouton du microphone dans la barre d'adresse pour résoudre ce problème.",
|
||||
"micNotFoundError": "Le microphone n'a pas été détecté.",
|
||||
"micConstraintFailedError": "Votre microphone ne satisfait pas certaines des contraintes nécessaires.",
|
||||
"micNotSendingDataTitle": "",
|
||||
"micNotSendingData": "",
|
||||
"cameraNotSendingDataTitle": "",
|
||||
"cameraNotSendingData": "",
|
||||
"micNotSendingDataTitle": "Impossible d'accéder au microphone",
|
||||
"micNotSendingData": "Nous sommes incapables d'accéder à votre microphone. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page.",
|
||||
"cameraNotSendingDataTitle": "Impossible d'accéder à votre caméra",
|
||||
"cameraNotSendingData": "Nous sommes incapables d'accéder à votre caméra. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page",
|
||||
"goToStore": "Aller sur le webstore",
|
||||
"externalInstallationTitle": "Extension requise : ",
|
||||
"externalInstallationMsg": "Vous devez installer notre extension de partage de bureau.",
|
||||
"inlineInstallationMsg": "Vous devez installer notre extension de partage de bureau.",
|
||||
"inlineInstallExtension": "",
|
||||
"muteParticipantTitle": "",
|
||||
"inlineInstallExtension": "Installer maintenant",
|
||||
"muteParticipantTitle": "Couper le micro de ce participant?",
|
||||
"muteParticipantBody": "Vous ne pourrez plus réactiver leurs micros, mais ils peuvent l'activer par eux-même à tout moment.",
|
||||
"muteParticipantButton": "Couper le micro",
|
||||
"remoteControlTitle": "Contrôle de bureau à distance",
|
||||
@@ -359,7 +350,7 @@
|
||||
"remoteControlDeniedMessage": "__user__ a refusé votre demande de prise en main à distance!",
|
||||
"remoteControlAllowedMessage": "Une erreur s'est produite lors de la demande d’autorisation de prise en main à distance avec __user__!",
|
||||
"remoteControlErrorMessage": "Une erreur s'est produite lors de la demande d’autorisation de prise en main à distance avec __user__!",
|
||||
"startRemoteControlErrorMessage": "",
|
||||
"startRemoteControlErrorMessage": "Une erreur est survenue lors de la tentative de démarrage de la session de contrôle à distance!",
|
||||
"remoteControlStopMessage": "La prise en main à distance est terminée!",
|
||||
"close": "Fermer",
|
||||
"shareYourScreen": "Partagez votre écran",
|
||||
@@ -407,31 +398,43 @@
|
||||
"ATTACHED": "Attachée"
|
||||
},
|
||||
"recording": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"busy": "Nous sommes en train de libérer les ressources d'enregistrement. Réessayez dans quelques minutes.",
|
||||
"busyTitle": "Tous les enregistreurs sont actuellement occupés",
|
||||
"buttonTooltip": "Démarrer / Arrêter l'enregistrement",
|
||||
"error": "Échec de l'enregistrement. Veuillez réessayer.",
|
||||
"failedToStart": "L'enregistrement n'as pas réussi à démarrer",
|
||||
"off": "Enregistrement arrêter",
|
||||
"off": "Enregistrement arrêté",
|
||||
"on": "Enregistrement",
|
||||
"pending": "Enregistrement en attente de participant...",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
"serviceName": "Service d'enregistrement",
|
||||
"unavailable": "Oups! Le __serviceName__ est actuellement indisponible. Nous travaillons sur la résolution du problème. Veuillez réessayer plus tard.",
|
||||
"unavailableTitle": "Enregistrement indisponible"
|
||||
},
|
||||
"\u0005recording": {},
|
||||
"liveStreaming": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"failedToStart": "",
|
||||
"off": "",
|
||||
"busy": "Nous travaillons sur la libération des ressources de Streaming. Veuillez réessayez dans quelques minutes.",
|
||||
"busyTitle": "Tous les streamers sont actuellement occupés",
|
||||
"buttonTooltip": "Démarrer / Arrêter le Stream",
|
||||
"error": "Le Streaming a échoué. Veuillez réessayer.",
|
||||
"failedToStart": "Le Streaming n'as pas réussi à démarrer",
|
||||
"off": "Le Streaming a été arrêter",
|
||||
"on": "Direct",
|
||||
"pending": "Commencer le direct...",
|
||||
"streamIdRequired": "",
|
||||
"serviceName": "Service de diffusion en direct",
|
||||
"streamIdRequired": "Merci de renseigner le stream id pour lancer le streaming.",
|
||||
"streamIdHelp": "Où puis-je trouver ceci?",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
"unavailableTitle": "Le Streaming est indisponible"
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "Nous travaillons sur la libération des ressources. Veuillez réessayez dans quelques minutes.",
|
||||
"busyTitle": "Le service du Salon est actuellement occupé",
|
||||
"errorInvite": "La conférence n'est pas encore établie. Veuillez réessayer plus tard.",
|
||||
"errorInviteTitle": "Erreur lors de l'invitation",
|
||||
"errorAlreadyInvited": "__displayName__ est déjà invité(e)",
|
||||
"errorInviteFailedTitle": "l'invitation de __displayName__ a échoué",
|
||||
"errorInviteFailed": "Nous travaillons sur la résolution du problème. Veuillez réessayer plus tard.",
|
||||
"pending": "__displayName__ a été invité(e)",
|
||||
"serviceName": "Service de salon",
|
||||
"unavailableTitle": "Service indisponible"
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "__count__h",
|
||||
@@ -442,7 +445,7 @@
|
||||
"speakerTime": "Temps de l'interlocuteur"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "Paramètres du périphérique",
|
||||
"deviceSettings": "Paramètres",
|
||||
"noPermission": "Permission non accordée",
|
||||
"previewUnavailable": "Aperçu non disponible",
|
||||
"selectADevice": "Sélectionner un périphérique",
|
||||
@@ -456,56 +459,66 @@
|
||||
"hidePassword": "Cacher le mot de passe",
|
||||
"inviteTo": "inviter des participants à __conferenceName__",
|
||||
"invitedYouTo": "__userName__ vous a invité(e) à la conférence __inviteURL__",
|
||||
"invitePeople": "",
|
||||
"invitePeople": "Inviter",
|
||||
"locked": "Cet appel est verrouillé. les nouveaux interlocuteurs devraient avoir le lien et saisir le mot de passe pour rejoindre.",
|
||||
"showPassword": "Afficher le mot de passe",
|
||||
"unlocked": "Cet appel est verrouillé. Tout nouveau participant avec un lien peut rejoindre l'appel."
|
||||
},
|
||||
"videoStatus": {
|
||||
"callQuality": "",
|
||||
"callQuality": "Qualité de l'appel",
|
||||
"hd": "HD",
|
||||
"highDefinition": "",
|
||||
"labelTooltipVideo": "",
|
||||
"labelTooltipAudioOnly": "",
|
||||
"ld": "",
|
||||
"lowDefinition": "",
|
||||
"p2pEnabled": "",
|
||||
"p2pVideoQualityDescription": "",
|
||||
"recHighDefinitionOnly": "",
|
||||
"sd": "SD",
|
||||
"standardDefinition": "",
|
||||
"qualityButtonTip": ""
|
||||
"highDefinition": "Haute définition",
|
||||
"labelTooltipVideo": "Qualité vidéo actuelle",
|
||||
"labelTooltipAudioOnly": "Mode audio uniquement activé",
|
||||
"ld": "BD",
|
||||
"lowDefinition": "Basse définition",
|
||||
"onlyAudioAvailable": "Seul l'audio est disponible",
|
||||
"onlyAudioSupported": "Nous ne supportons que l'audio sur ce navigateur.",
|
||||
"p2pEnabled": "Peer to Peer activé",
|
||||
"p2pVideoQualityDescription": "En mode peer to peer, la qualité d'appel reçue ne peut être basculée qu'entre haut et audio. Les autres paramètres ne seront pas respectés tant que l'on n'aura pas quitté peer to peer.",
|
||||
"recHighDefinitionOnly": "Va préférer la haute définition",
|
||||
"sd": "MD",
|
||||
"standardDefinition": "Moyenne Définition",
|
||||
"qualityButtonTip": "Changer la qualité de vidéo reçue"
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "Composer",
|
||||
"dialOut": "",
|
||||
"dialOut": "Appeler #",
|
||||
"statusMessage": "est maintenant __status__",
|
||||
"enterPhone": "Saisissez un numéro de téléphone",
|
||||
"phoneNotAllowed": "Désolé, nous ne supportons pas encore cette destination!"
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "Ajouter",
|
||||
"noResults": "",
|
||||
"searchPlaceholder": "",
|
||||
"title": "",
|
||||
"failedToAdd": ""
|
||||
"noResults": "Aucun résultat de recherche correspondant",
|
||||
"searchPlaceholder": "Rechercher des personnes et des salons à ajouter",
|
||||
"title": "Ajouter des personnes à votre appel",
|
||||
"failedToAdd": "Échec de l'ajout de membres"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "",
|
||||
"retry": "",
|
||||
"support": "",
|
||||
"supportMsg": ""
|
||||
"msg": "Nous avons trébuché un peu.",
|
||||
"retry": "Réessayer",
|
||||
"support": "Support",
|
||||
"supportMsg": "Si cela continue, prenez contact avec"
|
||||
},
|
||||
"deviceError": {
|
||||
"cameraError": "",
|
||||
"microphoneError": "",
|
||||
"cameraPermission": "",
|
||||
"microphonePermission": ""
|
||||
"cameraError": "Impossible d'accéder à votre caméra",
|
||||
"microphoneError": "Impossible d'accéder à votre microphone",
|
||||
"cameraPermission": "Erreur lors de l'obtention de la permission de la caméra ",
|
||||
"microphonePermission": "Erreur lors de l'obtention de la permission du microphone"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Moyen",
|
||||
"bad": "Mauvais",
|
||||
"good": "Bien",
|
||||
"rateExperience": "Veuillez évaluer votre réunion.",
|
||||
"veryBad": "Très mauvais",
|
||||
"veryGood": "Très bon"
|
||||
},
|
||||
"info": {
|
||||
"copy": "",
|
||||
"invite": "",
|
||||
"title": "",
|
||||
"tooltip": ""
|
||||
"copy": "Copier le lien",
|
||||
"invite": "Inviter à __app__",
|
||||
"title": "Informations sur la conférence",
|
||||
"tooltip": "Obtenir des informations d'accès"
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,9 @@
|
||||
"grantedToUnknown": "Moderator rights granted to $t(notify.somebody)!",
|
||||
"muted": "You have started the conversation muted.",
|
||||
"mutedTitle": "You're muted!",
|
||||
"raisedHand": "Would like to speak."
|
||||
"raisedHand": "Would like to speak.",
|
||||
"suboptimalExperienceTitle": "Browser Warning",
|
||||
"suboptimalExperienceDescription": "Eer... we are afraid your experience with __appName__ isn't going to be that great here. We are looking for ways to improve this but, until then, please try using one of the <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>."
|
||||
},
|
||||
"dialog": {
|
||||
"add": "Add",
|
||||
@@ -535,5 +537,18 @@
|
||||
"invite": "Invite in __app__",
|
||||
"title": "Call access info",
|
||||
"tooltip": "Get access info about the meeting"
|
||||
},
|
||||
"profileModal": {
|
||||
"alertOk": "OK",
|
||||
"alertTitle": "Warning",
|
||||
"alertURLText": "The entered server URL is invalid",
|
||||
"conferenceSection": "Conference",
|
||||
"displayName": "Display name",
|
||||
"email": "Email",
|
||||
"header": "Settings",
|
||||
"profileSection": "Profile",
|
||||
"serverURL": "Server URL",
|
||||
"startWithAudioMuted": "Start with audio muted",
|
||||
"startWithVideoMuted": "Start with video muted"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* eslint-disable no-unused-vars, no-var */
|
||||
// Logging configuration
|
||||
// XXX When making any changes to this file make sure to also update it's React
|
||||
// version at ./react/features/base/logging/reducer.js !!!
|
||||
var loggingConfig = {
|
||||
// default log level for the app and lib-jitsi-meet
|
||||
defaultLogLevel: 'trace',
|
||||
@@ -7,9 +9,11 @@ var loggingConfig = {
|
||||
// Option to disable LogCollector (which stores the logs on CallStats)
|
||||
// disableLogCollector: true,
|
||||
|
||||
// Logging level adjustments for verbose modules:
|
||||
// The following are too verbose in their logging with the
|
||||
// {@link #defaultLogLevel}:
|
||||
'modules/statistics/CallStats.js': 'info',
|
||||
'modules/xmpp/strophe.util.js': 'log',
|
||||
'modules/statistics/CallStats.js': 'info'
|
||||
'modules/RTC/TraceablePeerConnection.js': 'info'
|
||||
};
|
||||
|
||||
/* eslint-enable no-unused-vars, no-var */
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
||||
import {
|
||||
API_TOGGLE_AUDIO,
|
||||
API_TOGGLE_VIDEO,
|
||||
sendAnalyticsEvent
|
||||
createApiEvent,
|
||||
sendAnalytics
|
||||
} from '../../react/features/analytics';
|
||||
import { getJitsiMeetTransport } from '../transport';
|
||||
|
||||
@@ -56,25 +55,52 @@ let videoAvailable = true;
|
||||
*/
|
||||
function initCommands() {
|
||||
commands = {
|
||||
'display-name':
|
||||
APP.conference.changeLocalDisplayName.bind(APP.conference),
|
||||
'display-name': displayName => {
|
||||
sendAnalytics(createApiEvent('display.name.changed'));
|
||||
APP.conference.changeLocalDisplayName(displayName);
|
||||
},
|
||||
'submit-feedback': feedback => {
|
||||
sendAnalytics(createApiEvent('submit.feedback'));
|
||||
APP.conference.submitFeedback(feedback.score, feedback.message);
|
||||
},
|
||||
'toggle-audio': () => {
|
||||
sendAnalyticsEvent(API_TOGGLE_AUDIO);
|
||||
sendAnalytics(createApiEvent('toggle-audio'));
|
||||
logger.log('Audio toggle: API command received');
|
||||
APP.conference.toggleAudioMuted(false /* no UI */);
|
||||
},
|
||||
'toggle-video': () => {
|
||||
sendAnalyticsEvent(API_TOGGLE_VIDEO);
|
||||
sendAnalytics(createApiEvent('toggle-video'));
|
||||
logger.log('Video toggle: API command received');
|
||||
APP.conference.toggleVideoMuted(false /* no UI */);
|
||||
},
|
||||
'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(true),
|
||||
'email': APP.conference.changeLocalEmail,
|
||||
'avatar-url': APP.conference.changeLocalAvatarUrl
|
||||
'toggle-film-strip': () => {
|
||||
sendAnalytics(createApiEvent('film.strip.toggled'));
|
||||
APP.UI.toggleFilmstrip();
|
||||
},
|
||||
'toggle-chat': () => {
|
||||
sendAnalytics(createApiEvent('chat.toggled'));
|
||||
APP.UI.toggleChat();
|
||||
},
|
||||
'toggle-contact-list': () => {
|
||||
sendAnalytics(createApiEvent('contact.list.toggled'));
|
||||
APP.UI.toggleContactList();
|
||||
},
|
||||
'toggle-share-screen': () => {
|
||||
sendAnalytics(createApiEvent('screen.sharing.toggled'));
|
||||
toggleScreenSharing();
|
||||
},
|
||||
'video-hangup': () => {
|
||||
sendAnalytics(createApiEvent('video.hangup'));
|
||||
APP.conference.hangup(true);
|
||||
},
|
||||
'email': email => {
|
||||
sendAnalytics(createApiEvent('email.changed'));
|
||||
APP.conference.changeLocalEmail(email);
|
||||
},
|
||||
'avatar-url': avatarUrl => {
|
||||
sendAnalytics(createApiEvent('avatar.url.changed'));
|
||||
APP.conference.changeLocalAvatarUrl(avatarUrl);
|
||||
}
|
||||
};
|
||||
transport.on('event', ({ data, name }) => {
|
||||
if (name && commands[name]) {
|
||||
@@ -434,6 +460,16 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that conference feedback
|
||||
* has been submitted. Intended to be used in conjunction with the
|
||||
* submit-feedback command to get notified if feedback was submitted.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyFeedbackSubmitted() {
|
||||
this._sendEvent({ name: 'feedback-submitted' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the allocated resources.
|
||||
|
||||
26
modules/API/external/external_api.js
vendored
@@ -21,6 +21,7 @@ const commands = {
|
||||
displayName: 'display-name',
|
||||
email: 'email',
|
||||
hangup: 'video-hangup',
|
||||
submitFeedback: 'submit-feedback',
|
||||
toggleAudio: 'toggle-audio',
|
||||
toggleChat: 'toggle-chat',
|
||||
toggleContactList: 'toggle-contact-list',
|
||||
@@ -38,6 +39,7 @@ const events = {
|
||||
'audio-availability-changed': 'audioAvailabilityChanged',
|
||||
'audio-mute-status-changed': 'audioMuteStatusChanged',
|
||||
'display-name-change': 'displayNameChange',
|
||||
'feedback-submitted': 'feedbackSubmitted',
|
||||
'incoming-message': 'incomingMessage',
|
||||
'outgoing-message': 'outgoingMessage',
|
||||
'participant-joined': 'participantJoined',
|
||||
@@ -121,7 +123,8 @@ function parseArguments(args) {
|
||||
configOverwrite,
|
||||
interfaceConfigOverwrite,
|
||||
noSSL,
|
||||
jwt
|
||||
jwt,
|
||||
onload
|
||||
] = args;
|
||||
|
||||
return {
|
||||
@@ -132,7 +135,8 @@ function parseArguments(args) {
|
||||
configOverwrite,
|
||||
interfaceConfigOverwrite,
|
||||
noSSL,
|
||||
jwt
|
||||
jwt,
|
||||
onload
|
||||
};
|
||||
case 'object': // new arguments format
|
||||
return args[0];
|
||||
@@ -194,6 +198,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* used.
|
||||
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
|
||||
* authentication.
|
||||
* @param {string} [options.onload] - The onload function that will listen
|
||||
* for iframe onload event.
|
||||
*/
|
||||
constructor(domain, ...args) {
|
||||
super();
|
||||
@@ -205,7 +211,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
configOverwrite = {},
|
||||
interfaceConfigOverwrite = {},
|
||||
noSSL = false,
|
||||
jwt = undefined
|
||||
jwt = undefined,
|
||||
onload = undefined
|
||||
} = parseArguments(args);
|
||||
|
||||
this._parentNode = parentNode;
|
||||
@@ -216,7 +223,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
noSSL,
|
||||
roomName
|
||||
});
|
||||
this._createIFrame(height, width);
|
||||
this._createIFrame(height, width, onload);
|
||||
this._transport = new Transport({
|
||||
backend: new PostMessageTransportBackend({
|
||||
postisOptions: {
|
||||
@@ -241,11 +248,13 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* parseSizeParam for format details.
|
||||
* @param {number|string} width - The with of the iframe. Check
|
||||
* parseSizeParam for format details.
|
||||
* @param {Function} onload - The function that will listen
|
||||
* for onload event.
|
||||
* @returns {void}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_createIFrame(height, width) {
|
||||
_createIFrame(height, width, onload) {
|
||||
const frameName = `jitsiConferenceFrame${id}`;
|
||||
|
||||
this._frame = document.createElement('iframe');
|
||||
@@ -256,6 +265,13 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
this._setSize(height, width);
|
||||
this._frame.setAttribute('allowFullScreen', 'true');
|
||||
this._frame.style.border = 0;
|
||||
|
||||
if (onload) {
|
||||
// waits for iframe resources to load
|
||||
// and fires event when it is done
|
||||
this._frame.onload = onload;
|
||||
}
|
||||
|
||||
this._frame = this._parentNode.appendChild(this._frame);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,6 +142,32 @@ UI.toggleFullScreen = function() {
|
||||
UIUtil.isFullScreen() ? UIUtil.exitFullScreen() : UIUtil.enterFullScreen();
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if we're currently in full screen mode.
|
||||
*
|
||||
* @return {boolean} {true} to indicate that we're currently in full screen
|
||||
* mode, {false} otherwise
|
||||
*/
|
||||
UI.isFullScreen = function() {
|
||||
return UIUtil.isFullScreen();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the etherpad window is currently visible.
|
||||
* @returns {Boolean} - true if the etherpad window is currently visible.
|
||||
*/
|
||||
UI.isEtherpadVisible = function() {
|
||||
return Boolean(etherpadManager && etherpadManager.isVisible());
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if there is a shared video which is being shown (?).
|
||||
* @returns {boolean} - true if there is a shared video which is being shown.
|
||||
*/
|
||||
UI.isSharedVideoShown = function() {
|
||||
return Boolean(sharedVideoManager && sharedVideoManager.isSharedVideoShown);
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify user that server has shut down.
|
||||
*/
|
||||
@@ -616,11 +642,16 @@ UI.toggleFilmstrip = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if the filmstrip is currently visible or not.
|
||||
* @returns {true} if the filmstrip is currently visible, otherwise
|
||||
* Checks if the filmstrip is currently visible or not.
|
||||
* @returns {true} if the filmstrip is currently visible, and false otherwise.
|
||||
*/
|
||||
UI.isFilmstripVisible = () => Filmstrip.isFilmstripVisible();
|
||||
|
||||
/**
|
||||
* @returns {true} if the chat panel is currently visible, and false otherwise.
|
||||
*/
|
||||
UI.isChatVisible = () => Chat.isVisible();
|
||||
|
||||
/**
|
||||
* Toggles chat panel.
|
||||
*/
|
||||
@@ -851,8 +882,7 @@ UI.notifyInitiallyMuted = function() {
|
||||
'notify.mutedTitle',
|
||||
'connected',
|
||||
'notify.muted',
|
||||
null,
|
||||
120000);
|
||||
null);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,11 +24,9 @@ import {
|
||||
JitsiRecordingStatus
|
||||
} from '../../../react/features/base/lib-jitsi-meet';
|
||||
import {
|
||||
RECORDING_CANCELED,
|
||||
RECORDING_CLICKED,
|
||||
RECORDING_STARTED,
|
||||
RECORDING_STOPPED,
|
||||
sendAnalyticsEvent
|
||||
createToolbarEvent,
|
||||
createRecordingDialogEvent,
|
||||
sendAnalytics
|
||||
} from '../../../react/features/analytics';
|
||||
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
||||
import { setNotificationsEnabled } from '../../../react/features/notifications';
|
||||
@@ -452,12 +450,13 @@ const Recording = {
|
||||
},
|
||||
|
||||
// checks whether recording is enabled and whether we have params
|
||||
// to start automatically recording
|
||||
// to start automatically recording (XXX: No, it doesn't do that).
|
||||
checkAutoRecord() {
|
||||
if (_isRecordingButtonEnabled && config.autoRecord) {
|
||||
this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
|
||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
|
||||
this.predefinedToken);
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token: this.predefinedToken });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -467,11 +466,16 @@ const Recording = {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToolbarButtonClick() {
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'recording.button',
|
||||
{
|
||||
'dialog_present': Boolean(dialog)
|
||||
}));
|
||||
|
||||
if (dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendAnalyticsEvent(RECORDING_CLICKED);
|
||||
switch (this.currentState) {
|
||||
case JitsiRecordingStatus.ON:
|
||||
case JitsiRecordingStatus.RETRYING:
|
||||
@@ -479,7 +483,13 @@ const Recording = {
|
||||
_showStopRecordingPrompt(this.recordingType).then(
|
||||
() => {
|
||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
||||
sendAnalyticsEvent(RECORDING_STOPPED);
|
||||
|
||||
// The confirm button on the stop recording dialog was
|
||||
// clicked
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent(
|
||||
'stop',
|
||||
'confirm.button'));
|
||||
},
|
||||
() => {}); // eslint-disable-line no-empty-function
|
||||
break;
|
||||
@@ -492,21 +502,32 @@ const Recording = {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ streamId });
|
||||
sendAnalyticsEvent(RECORDING_STARTED);
|
||||
|
||||
// The confirm button on the start recording dialog was
|
||||
// clicked
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent(
|
||||
'start',
|
||||
'confirm.button'));
|
||||
})
|
||||
.catch(reason => {
|
||||
if (reason === APP.UI.messageHandler.CANCEL) {
|
||||
sendAnalyticsEvent(RECORDING_CANCELED);
|
||||
// The cancel button on the start recording dialog was
|
||||
// clicked
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent(
|
||||
'start',
|
||||
'cancel.button'));
|
||||
} else {
|
||||
logger.error(reason);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Note that we only fire analytics events for Jibri.
|
||||
if (this.predefinedToken) {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token: this.predefinedToken });
|
||||
sendAnalyticsEvent(RECORDING_STARTED);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -515,12 +536,9 @@ const Recording = {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token });
|
||||
sendAnalyticsEvent(RECORDING_STARTED);
|
||||
})
|
||||
.catch(reason => {
|
||||
if (reason === APP.UI.messageHandler.CANCEL) {
|
||||
sendAnalyticsEvent(RECORDING_CANCELED);
|
||||
} else {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL) {
|
||||
logger.error(reason);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,15 +11,8 @@ import LargeContainer from '../videolayout/LargeContainer';
|
||||
import Filmstrip from '../videolayout/Filmstrip';
|
||||
|
||||
import {
|
||||
SHARED_VIDEO_ALREADY_SHARED,
|
||||
SHARED_VIDEO_AUDIO_MUTED,
|
||||
SHARED_VIDEO_AUDIO_UNMUTED,
|
||||
SHARED_VIDEO_CANCELED,
|
||||
SHARED_VIDEO_PAUSED,
|
||||
SHARED_VIDEO_STARTED,
|
||||
SHARED_VIDEO_STOPPED,
|
||||
SHARED_VIDEO_VOLUME_CHANGED,
|
||||
sendAnalyticsEvent
|
||||
createSharedVideoEvent as createEvent,
|
||||
sendAnalytics
|
||||
} from '../../../react/features/analytics';
|
||||
import {
|
||||
participantJoined,
|
||||
@@ -95,11 +88,11 @@ export default class SharedVideoManager {
|
||||
url => {
|
||||
this.emitter.emit(
|
||||
UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
|
||||
sendAnalyticsEvent(SHARED_VIDEO_STARTED);
|
||||
sendAnalytics(createEvent('started'));
|
||||
},
|
||||
err => {
|
||||
logger.log('SHARED VIDEO CANCELED', err);
|
||||
sendAnalyticsEvent(SHARED_VIDEO_CANCELED);
|
||||
sendAnalytics(createEvent('canceled'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -119,7 +112,7 @@ export default class SharedVideoManager {
|
||||
}
|
||||
this.emitter.emit(
|
||||
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
|
||||
sendAnalyticsEvent(SHARED_VIDEO_STOPPED);
|
||||
sendAnalytics(createEvent('stopped'));
|
||||
},
|
||||
() => {}); // eslint-disable-line no-empty-function
|
||||
} else {
|
||||
@@ -127,7 +120,7 @@ export default class SharedVideoManager {
|
||||
descriptionKey: 'dialog.alreadySharedVideoMsg',
|
||||
titleKey: 'dialog.alreadySharedVideoTitle'
|
||||
});
|
||||
sendAnalyticsEvent(SHARED_VIDEO_ALREADY_SHARED);
|
||||
sendAnalytics(createEvent('already.shared'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +229,7 @@ export default class SharedVideoManager {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
} else if (event.data == YT.PlayerState.PAUSED) {
|
||||
self.smartAudioUnmute();
|
||||
sendAnalyticsEvent(SHARED_VIDEO_PAUSED);
|
||||
sendAnalytics(createEvent('paused'));
|
||||
}
|
||||
// eslint-disable-next-line eqeqeq
|
||||
self.fireSharedVideoEvent(event.data == YT.PlayerState.PAUSED);
|
||||
@@ -268,7 +261,12 @@ export default class SharedVideoManager {
|
||||
} else if (event.data.volume <= 0 || event.data.muted) {
|
||||
self.smartAudioUnmute();
|
||||
}
|
||||
sendAnalyticsEvent(SHARED_VIDEO_VOLUME_CHANGED);
|
||||
sendAnalytics(createEvent(
|
||||
'volume.changed',
|
||||
{
|
||||
volume: event.data.volume,
|
||||
muted: event.data.muted
|
||||
}));
|
||||
};
|
||||
|
||||
window.onPlayerReady = function(event) {
|
||||
@@ -434,8 +432,8 @@ export default class SharedVideoManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates video, if its not playing and needs starting or
|
||||
* if its playing and needs to be paysed
|
||||
* Updates video, if it's not playing and needs starting or if it's playing
|
||||
* and needs to be paused.
|
||||
* @param id the id of the sender of the command
|
||||
* @param url the video url
|
||||
* @param attributes
|
||||
@@ -574,7 +572,7 @@ export default class SharedVideoManager {
|
||||
if (APP.conference.isLocalAudioMuted()
|
||||
&& !this.mutedWithUserInteraction
|
||||
&& !this.isSharedVideoVolumeOn()) {
|
||||
sendAnalyticsEvent(SHARED_VIDEO_AUDIO_UNMUTED);
|
||||
sendAnalytics(createEvent('audio.unmuted'));
|
||||
logger.log('Shared video: audio unmuted');
|
||||
this.emitter.emit(UIEvents.AUDIO_MUTED, false, false);
|
||||
this.showMicMutedPopup(false);
|
||||
@@ -588,7 +586,7 @@ export default class SharedVideoManager {
|
||||
smartAudioMute() {
|
||||
if (!APP.conference.isLocalAudioMuted()
|
||||
&& this.isSharedVideoVolumeOn()) {
|
||||
sendAnalyticsEvent(SHARED_VIDEO_AUDIO_MUTED);
|
||||
sendAnalytics(createEvent('audio.muted'));
|
||||
logger.log('Shared video: audio muted');
|
||||
this.emitter.emit(UIEvents.AUDIO_MUTED, true, false);
|
||||
this.showMicMutedPopup(true);
|
||||
|
||||
@@ -4,9 +4,8 @@ import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import Settings from '../../../settings/Settings';
|
||||
|
||||
import {
|
||||
AUTHENTICATE_LOGIN_CLICKED,
|
||||
AUTHENTICATE_LOGOUT_CLICKED,
|
||||
sendAnalyticsEvent
|
||||
createProfilePanelButtonEvent,
|
||||
sendAnalytics
|
||||
} from '../../../../react/features/analytics';
|
||||
|
||||
const sidePanelsContainerId = 'sideToolbarContainer';
|
||||
@@ -95,7 +94,7 @@ export default {
|
||||
*
|
||||
*/
|
||||
function loginClicked() {
|
||||
sendAnalyticsEvent(AUTHENTICATE_LOGIN_CLICKED);
|
||||
sendAnalytics(createProfilePanelButtonEvent('login.button'));
|
||||
emitter.emit(UIEvents.AUTH_CLICKED);
|
||||
}
|
||||
|
||||
@@ -108,7 +107,7 @@ export default {
|
||||
const titleKey = 'dialog.logoutTitle';
|
||||
const msgKey = 'dialog.logoutQuestion';
|
||||
|
||||
sendAnalyticsEvent(AUTHENTICATE_LOGOUT_CLICKED);
|
||||
sendAnalytics(createProfilePanelButtonEvent('logout.button'));
|
||||
|
||||
// Ask for confirmation
|
||||
APP.UI.messageHandler.openTwoButtonDialog({
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
import { setFilmstripVisibility } from '../../../react/features/filmstrip';
|
||||
import { setFilmstripVisible } from '../../../react/features/filmstrip';
|
||||
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
import {
|
||||
TOOLBAR_FILMSTRIP_TOGGLED,
|
||||
sendAnalyticsEvent
|
||||
createShortcutEvent,
|
||||
createToolbarEvent,
|
||||
sendAnalytics
|
||||
} from '../../../react/features/analytics';
|
||||
|
||||
const Filmstrip = {
|
||||
@@ -75,8 +76,18 @@ const Filmstrip = {
|
||||
// Firing the event instead of executing toggleFilmstrip method because
|
||||
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
||||
// to correctly resize the video area.
|
||||
$('#toggleFilmstripButton').on('click',
|
||||
() => this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP));
|
||||
$('#toggleFilmstripButton').on(
|
||||
'click',
|
||||
() => {
|
||||
// The 'enable' parameter is set to true if the action results
|
||||
// in the filmstrip being hidden.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'toggle.filmstrip.button',
|
||||
{
|
||||
enable: this.isFilmstripVisible()
|
||||
}));
|
||||
this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||
});
|
||||
|
||||
this._registerToggleFilmstripShortcut();
|
||||
},
|
||||
@@ -94,7 +105,14 @@ const Filmstrip = {
|
||||
// Firing the event instead of executing toggleFilmstrip method because
|
||||
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
||||
// to correctly resize the video area.
|
||||
const handler = () => this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||
const handler = () => {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.filmstrip',
|
||||
{
|
||||
enable: this.isFilmstripVisible()
|
||||
}));
|
||||
this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||
};
|
||||
|
||||
APP.keyboardshortcut.registerShortcut(
|
||||
shortcut,
|
||||
@@ -129,50 +147,43 @@ const Filmstrip = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the filmstrip.
|
||||
* Toggles the visibility of the filmstrip, or sets it to a specific value
|
||||
* if the 'visible' parameter is specified.
|
||||
*
|
||||
* @param visible optional {Boolean} which specifies the desired visibility
|
||||
* of the filmstrip. If not specified, the visibility will be flipped
|
||||
* (i.e. toggled); otherwise, the visibility will be set to the specified
|
||||
* value.
|
||||
* @param {Boolean} sendAnalytics - True to send an analytics event. The
|
||||
* default value is true.
|
||||
*
|
||||
* Note:
|
||||
* This method shouldn't be executed directly to hide the filmstrip.
|
||||
* It's important to hide the filmstrip with UI.toggleFilmstrip in order
|
||||
* to correctly resize the video area.
|
||||
*/
|
||||
toggleFilmstrip(visible, sendAnalytics = true) {
|
||||
const isVisibleDefined = typeof visible === 'boolean';
|
||||
toggleFilmstrip(visible) {
|
||||
const wasFilmstripVisible = this.isFilmstripVisible();
|
||||
|
||||
if (!isVisibleDefined) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
visible = this.isFilmstripVisible();
|
||||
} else if (this.isFilmstripVisible() === visible) {
|
||||
// If 'visible' is defined and matches the current state, we have
|
||||
// nothing to do. Otherwise (regardless of whether 'visible' is defined)
|
||||
// we need to toggle the state.
|
||||
if (visible === wasFilmstripVisible) {
|
||||
return;
|
||||
}
|
||||
if (sendAnalytics) {
|
||||
sendAnalyticsEvent(TOOLBAR_FILMSTRIP_TOGGLED);
|
||||
}
|
||||
|
||||
this.filmstrip.toggleClass('hidden');
|
||||
|
||||
if (visible) {
|
||||
if (wasFilmstripVisible) {
|
||||
this.showMenuUpIcon();
|
||||
} else {
|
||||
this.showMenuDownIcon();
|
||||
}
|
||||
|
||||
// Emit/fire UIEvents.TOGGLED_FILMSTRIP.
|
||||
const eventEmitter = this.eventEmitter;
|
||||
const isFilmstripVisible = this.isFilmstripVisible();
|
||||
|
||||
if (eventEmitter) {
|
||||
eventEmitter.emit(
|
||||
if (this.eventEmitter) {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.TOGGLED_FILMSTRIP,
|
||||
this.isFilmstripVisible());
|
||||
!wasFilmstripVisible);
|
||||
}
|
||||
APP.store.dispatch(setFilmstripVisibility(isFilmstripVisible));
|
||||
APP.store.dispatch(setFilmstripVisible(!wasFilmstripVisible));
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
import { toggleDialog } from '../../react/features/base/dialog';
|
||||
import {
|
||||
SHORTCUT_HELP,
|
||||
SHORTCUT_SPEAKER_STATS_CLICKED,
|
||||
SHORTCUT_TALK_CLICKED,
|
||||
SHORTCUT_TALK_RELEASED,
|
||||
sendAnalyticsEvent
|
||||
ACTION_SHORTCUT_PRESSED as PRESSED,
|
||||
ACTION_SHORTCUT_RELEASED as RELEASED,
|
||||
createShortcutEvent,
|
||||
sendAnalytics
|
||||
} from '../../react/features/analytics';
|
||||
import { KeyboardShortcutsDialog }
|
||||
from '../../react/features/keyboard-shortcuts';
|
||||
@@ -72,8 +71,10 @@ const KeyboardShortcut = {
|
||||
|| $(':focus').is('textarea'))) {
|
||||
if (this._getKeyboardKey(e).toUpperCase() === ' ') {
|
||||
if (APP.conference.isLocalAudioMuted()) {
|
||||
sendAnalyticsEvent(SHORTCUT_TALK_RELEASED);
|
||||
logger.log('Talk shortcut released');
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'push.to.talk',
|
||||
PRESSED));
|
||||
logger.log('Talk shortcut pressed');
|
||||
APP.conference.muteAudio(false);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +94,7 @@ const KeyboardShortcut = {
|
||||
* Registers a new shortcut.
|
||||
*
|
||||
* @param shortcutChar the shortcut character triggering the action
|
||||
* @param shortcutAttr the "shortcut" html element attribute mappring an
|
||||
* @param shortcutAttr the "shortcut" html element attribute mapping an
|
||||
* element to this shortcut and used to show the shortcut character on the
|
||||
* element tooltip
|
||||
* @param exec the function to be executed when the shortcut is pressed
|
||||
@@ -175,7 +176,7 @@ const KeyboardShortcut = {
|
||||
*/
|
||||
_initGlobalShortcuts() {
|
||||
this.registerShortcut('?', null, () => {
|
||||
sendAnalyticsEvent(SHORTCUT_HELP);
|
||||
sendAnalytics(createShortcutEvent('help'));
|
||||
APP.store.dispatch(toggleDialog(KeyboardShortcutsDialog, {
|
||||
shortcutDescriptions: _shortcutsHelp
|
||||
}));
|
||||
@@ -184,15 +185,15 @@ const KeyboardShortcut = {
|
||||
// register SPACE shortcut in two steps to insure visibility of help
|
||||
// message
|
||||
this.registerShortcut(' ', null, () => {
|
||||
sendAnalyticsEvent(SHORTCUT_TALK_CLICKED);
|
||||
logger.log('Talk shortcut pressed');
|
||||
sendAnalytics(createShortcutEvent('push.to.talk', RELEASED));
|
||||
logger.log('Talk shortcut released');
|
||||
APP.conference.muteAudio(true);
|
||||
});
|
||||
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
this.registerShortcut('T', null, () => {
|
||||
sendAnalyticsEvent(SHORTCUT_SPEAKER_STATS_CLICKED);
|
||||
sendAnalytics(createShortcutEvent('speaker.stats'));
|
||||
APP.store.dispatch(toggleDialog(SpeakerStats, {
|
||||
conference: APP.conference
|
||||
}));
|
||||
|
||||
@@ -37,20 +37,20 @@ export default class JitsiMeetLogStorage {
|
||||
return;
|
||||
}
|
||||
|
||||
let logJSON = `{"log${this.counter}":"\n`;
|
||||
let logMessage = `{"log${this.counter}":"\n`;
|
||||
|
||||
for (let i = 0, len = logEntries.length; i < len; i++) {
|
||||
const logEntry = logEntries[i];
|
||||
|
||||
if (typeof logEntry === 'object') {
|
||||
// Aggregated message
|
||||
logJSON += `(${logEntry.count}) ${logEntry.text}\n`;
|
||||
logMessage += `(${logEntry.count}) ${logEntry.text}\n`;
|
||||
} else {
|
||||
// Regular message
|
||||
logJSON += `${logEntry}\n`;
|
||||
logMessage += `${logEntry}\n`;
|
||||
}
|
||||
}
|
||||
logJSON += '"}';
|
||||
logMessage += '"}';
|
||||
|
||||
this.counter += 1;
|
||||
|
||||
@@ -58,11 +58,11 @@ export default class JitsiMeetLogStorage {
|
||||
// on the way that could be uninitialized if the storeLogs
|
||||
// attempt would be made very early (which is unlikely)
|
||||
try {
|
||||
APP.conference.logJSON(logJSON);
|
||||
APP.conference._room.sendApplicationLog(logMessage);
|
||||
} catch (error) {
|
||||
// NOTE console is intentional here
|
||||
console.error(
|
||||
'Failed to store the logs: ', logJSON, error);
|
||||
'Failed to store the logs: ', logMessage, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5531
package-lock.json
generated
@@ -44,16 +44,17 @@
|
||||
"jquery-i18next": "1.2.0",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#f71ff66fb26f9dd0b49baeeee239345ba268d46d",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#5f8c0a662af086e7bcc19c010f1129afc9b6d650",
|
||||
"lodash": "4.17.4",
|
||||
"moment": "2.19.4",
|
||||
"nuclear-js": "1.4.0",
|
||||
"postis": "2.2.0",
|
||||
"prop-types": "15.6.0",
|
||||
"react": "16.2.0",
|
||||
"react-animations": "^1.0.0",
|
||||
"react-dom": "16.2.0",
|
||||
"react-i18next": "4.8.0",
|
||||
"react-native": "0.50.4",
|
||||
"react-native": "0.51.0",
|
||||
"react-native-background-timer": "2.0.0",
|
||||
"react-native-callstats": "3.27.0",
|
||||
"react-native-fetch-blob": "0.10.8",
|
||||
@@ -63,7 +64,7 @@
|
||||
"react-native-locale-detector": "github:jitsi/react-native-locale-detector#cc76092fc4335488a28a9529c8b50afae2c3ecdc",
|
||||
"react-native-prompt": "1.0.0",
|
||||
"react-native-vector-icons": "4.4.2",
|
||||
"react-native-webrtc": "github:jitsi/react-native-webrtc#7c6b167120d2224f62222037ff71deb8ff93bd38",
|
||||
"react-native-webrtc": "github:jitsi/react-native-webrtc#806435b41fa152a8239ebeb7d002d1c6e979be86",
|
||||
"react-redux": "5.0.6",
|
||||
"redux": "3.7.2",
|
||||
"redux-thunk": "2.2.0",
|
||||
@@ -91,7 +92,7 @@
|
||||
"eslint-plugin-react-native": "3.2.0",
|
||||
"expose-loader": "0.7.4",
|
||||
"file-loader": "1.1.5",
|
||||
"flow-bin": "0.56.0",
|
||||
"flow-bin": "0.57.3",
|
||||
"imports-loader": "0.7.1",
|
||||
"node-sass": "3.13.1",
|
||||
"precommit-hook": "3.0.0",
|
||||
|
||||
@@ -9,12 +9,18 @@ import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Sends an analytics event.
|
||||
* Sends an event through the lib-jitsi-meet AnalyticsAdapter interface.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @param {Object} event - The event to send. It should be formatted as
|
||||
* described in AnalyticsAdapter.js in lib-jitsi-meet.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function sendAnalyticsEvent(...args: Array<any>) {
|
||||
analytics.sendEvent(...args);
|
||||
export function sendAnalytics(event: Object) {
|
||||
try {
|
||||
analytics.sendEvent(event);
|
||||
} catch (e) {
|
||||
logger.warn(`Error sending analytics event: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,24 +43,21 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||
|
||||
const state = getState();
|
||||
const config = state['features/base/config'];
|
||||
const { analyticsScriptUrls } = config;
|
||||
const machineId = JitsiMeetJS.getMachineId();
|
||||
const { user } = state['features/base/jwt'];
|
||||
const { analyticsScriptUrls, deploymentInfo } = config;
|
||||
const { group, server, user } = state['features/base/jwt'];
|
||||
const handlerConstructorOptions = {
|
||||
product: 'lib-jitsi-meet',
|
||||
version: JitsiMeetJS.version,
|
||||
session: machineId,
|
||||
user: user ? user.id : `uid-${machineId}`,
|
||||
server: state['features/base/connection'].locationURL.host
|
||||
envType: (deploymentInfo && deploymentInfo.envType) || 'dev',
|
||||
group,
|
||||
product: deploymentInfo && deploymentInfo.product,
|
||||
subproduct: deploymentInfo && deploymentInfo.environment,
|
||||
user: user && user.id,
|
||||
version: JitsiMeetJS.version
|
||||
};
|
||||
|
||||
_loadHandlers(analyticsScriptUrls, handlerConstructorOptions)
|
||||
.then(handlers => {
|
||||
const permanentProperties: Object = {
|
||||
roomName: state['features/base/conference'].room,
|
||||
userAgent: navigator.userAgent
|
||||
};
|
||||
const { group, server } = state['features/base/jwt'];
|
||||
const roomName = state['features/base/conference'].room;
|
||||
const permanentProperties = {};
|
||||
|
||||
if (server) {
|
||||
permanentProperties.server = server;
|
||||
@@ -65,8 +68,6 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||
|
||||
// Optionally, include local deployment information based on the
|
||||
// contents of window.config.deploymentInfo.
|
||||
const { deploymentInfo } = config;
|
||||
|
||||
if (deploymentInfo) {
|
||||
for (const key in deploymentInfo) {
|
||||
if (deploymentInfo.hasOwnProperty(key)) {
|
||||
@@ -76,6 +77,9 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||
}
|
||||
|
||||
analytics.addPermanentProperties(permanentProperties);
|
||||
analytics.setConferenceName(roomName);
|
||||
|
||||
// Set the handlers last, since this triggers emptying of the cache
|
||||
analytics.setAnalyticsHandlers(handlers);
|
||||
},
|
||||
error => analytics.dispose() && logger.error(error));
|
||||
|
||||
19
react/features/app-settings/actionTypes.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* The type of (redux) action which signals the request
|
||||
* to hide the app settings modal.
|
||||
*
|
||||
* {
|
||||
* type: HIDE_APP_SETTINGS
|
||||
* }
|
||||
*/
|
||||
export const HIDE_APP_SETTINGS = Symbol('HIDE_APP_SETTINGS');
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals the request
|
||||
* to show the app settings modal where available.
|
||||
*
|
||||
* {
|
||||
* type: SHOW_APP_SETTINGS
|
||||
* }
|
||||
*/
|
||||
export const SHOW_APP_SETTINGS = Symbol('SHOW_APP_SETTINGS');
|
||||
29
react/features/app-settings/actions.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// @flow
|
||||
|
||||
import { HIDE_APP_SETTINGS, SHOW_APP_SETTINGS } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Redux-signals the request to hide the app settings modal.
|
||||
*
|
||||
* @returns {{
|
||||
* type: HIDE_APP_SETTINGS
|
||||
* }}
|
||||
*/
|
||||
export function hideAppSettings() {
|
||||
return {
|
||||
type: HIDE_APP_SETTINGS
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Redux-signals the request to open the app settings modal.
|
||||
*
|
||||
* @returns {{
|
||||
* type: SHOW_APP_SETTINGS
|
||||
* }}
|
||||
*/
|
||||
export function showAppSettings() {
|
||||
return {
|
||||
type: SHOW_APP_SETTINGS
|
||||
};
|
||||
}
|
||||
181
react/features/app-settings/components/AbstractAppSettings.js
Normal file
@@ -0,0 +1,181 @@
|
||||
// @flow
|
||||
|
||||
import { Component } from 'react';
|
||||
|
||||
import { getProfile, updateProfile } from '../../base/profile';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AbstractAppSettings}
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current aspect ratio of the screen.
|
||||
*/
|
||||
_aspectRatio: Symbol,
|
||||
|
||||
/**
|
||||
* The current profile object.
|
||||
*/
|
||||
_profile: Object,
|
||||
|
||||
/**
|
||||
* The default URL for when there is no custom URL set in the profile.
|
||||
*/
|
||||
_serverURL: string,
|
||||
|
||||
/**
|
||||
* The visibility prop of the settings modal.
|
||||
*/
|
||||
_visible: boolean,
|
||||
|
||||
/**
|
||||
* Redux store dispatch function.
|
||||
*/
|
||||
dispatch: Dispatch<*>,
|
||||
|
||||
/**
|
||||
* The i18n translate function.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Base (abstract) class for container component rendering the app settings
|
||||
* page.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
export class AbstractAppSettings extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code AbstractAppSettings} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* the component.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._onChangeDisplayName = this._onChangeDisplayName.bind(this);
|
||||
this._onChangeEmail = this._onChangeEmail.bind(this);
|
||||
this._onChangeServerURL = this._onChangeServerURL.bind(this);
|
||||
this._onStartAudioMutedChange
|
||||
= this._onStartAudioMutedChange.bind(this);
|
||||
this._onStartVideoMutedChange
|
||||
= this._onStartVideoMutedChange.bind(this);
|
||||
}
|
||||
|
||||
_onChangeDisplayName: (string) => void;
|
||||
|
||||
/**
|
||||
* Handles the display name field value change.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} text - The value typed in the name field.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeDisplayName(text) {
|
||||
this._updateProfile({
|
||||
displayName: text
|
||||
});
|
||||
}
|
||||
|
||||
_onChangeEmail: (string) => void;
|
||||
|
||||
/**
|
||||
* Handles the email field value change.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} text - The value typed in the email field.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeEmail(text) {
|
||||
this._updateProfile({
|
||||
email: text
|
||||
});
|
||||
}
|
||||
|
||||
_onChangeServerURL: (string) => void;
|
||||
|
||||
/**
|
||||
* Handles the server name field value change.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} text - The server URL typed in the server field.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeServerURL(text) {
|
||||
this._updateProfile({
|
||||
serverURL: text
|
||||
});
|
||||
}
|
||||
|
||||
_onStartAudioMutedChange: (boolean) => void;
|
||||
|
||||
/**
|
||||
* Handles the start audio muted change event.
|
||||
*
|
||||
* @protected
|
||||
* @param {boolean} newValue - The new value for the
|
||||
* start audio muted option.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStartAudioMutedChange(newValue) {
|
||||
this._updateProfile({
|
||||
startWithAudioMuted: newValue
|
||||
});
|
||||
}
|
||||
|
||||
_onStartVideoMutedChange: (boolean) => void;
|
||||
|
||||
/**
|
||||
* Handles the start video muted change event.
|
||||
*
|
||||
* @protected
|
||||
* @param {boolean} newValue - The new value for the
|
||||
* start video muted option.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStartVideoMutedChange(newValue) {
|
||||
this._updateProfile({
|
||||
startWithVideoMuted: newValue
|
||||
});
|
||||
}
|
||||
|
||||
_updateProfile: (Object) => void;
|
||||
|
||||
/**
|
||||
* Updates the persisted profile on any change.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} updateObject - The partial update object for the profile.
|
||||
* @returns {void}
|
||||
*/
|
||||
_updateProfile(updateObject: Object) {
|
||||
this.props.dispatch(updateProfile({
|
||||
...this.props._profile,
|
||||
...updateObject
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props of
|
||||
* {@code AbstractAppSettings}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
const _serverURL = state['features/app'].app._getDefaultURL();
|
||||
const _profile = getProfile(state);
|
||||
|
||||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
|
||||
_profile,
|
||||
_serverURL,
|
||||
_visible: state['features/app-settings'].visible
|
||||
};
|
||||
}
|
||||
258
react/features/app-settings/components/AppSettings.native.js
Normal file
@@ -0,0 +1,258 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Alert,
|
||||
Modal,
|
||||
ScrollView,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getSafetyOffset, isIPad } from '../../base/react';
|
||||
import { ASPECT_RATIO_NARROW } from '../../base/responsive-ui';
|
||||
|
||||
import { _mapStateToProps, AbstractAppSettings } from './AbstractAppSettings';
|
||||
import { hideAppSettings } from '../actions';
|
||||
import BackButton from './BackButton.native';
|
||||
import FormRow from './FormRow.native';
|
||||
import FormSectionHeader from './FormSectionHeader.native';
|
||||
import { normalizeUserInputURL } from '../functions';
|
||||
import styles, { HEADER_PADDING } from './styles';
|
||||
|
||||
/**
|
||||
* The native container rendering the app settings page.
|
||||
*
|
||||
* @extends AbstractAppSettings
|
||||
*/
|
||||
class AppSettings extends AbstractAppSettings {
|
||||
_urlField: Object;
|
||||
|
||||
/**
|
||||
* Instantiates a new {@code AppSettings} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._getSafetyPadding = this._getSafetyPadding.bind(this);
|
||||
this._onBlurServerURL = this._onBlurServerURL.bind(this);
|
||||
this._onRequestClose = this._onRequestClose.bind(this);
|
||||
this._setURLFieldReference = this._setURLFieldReference.bind(this);
|
||||
this._showURLAlert = this._showURLAlert.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the settings page.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _profile, t } = this.props;
|
||||
|
||||
// FIXME: presentationStyle is added to workaround orientation issue on
|
||||
// iOS
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType = 'slide'
|
||||
onRequestClose = { this._onRequestClose }
|
||||
presentationStyle = 'overFullScreen'
|
||||
supportedOrientations = { [
|
||||
'landscape',
|
||||
'portrait'
|
||||
] }
|
||||
visible = { this.props._visible }>
|
||||
<View
|
||||
style = { [
|
||||
styles.headerContainer,
|
||||
this._getSafetyPadding()
|
||||
] } >
|
||||
<BackButton
|
||||
onPress = { this._onRequestClose }
|
||||
style = { styles.settingsBackButton } />
|
||||
<Text style = { [ styles.text, styles.headerTitle ] } >
|
||||
{ t('profileModal.header') }
|
||||
</Text>
|
||||
</View>
|
||||
<ScrollView style = { styles.settingsContainer } >
|
||||
<FormSectionHeader
|
||||
i18nLabel = 'profileModal.profileSection' />
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'profileModal.displayName' >
|
||||
<TextInput
|
||||
onChangeText = { this._onChangeDisplayName }
|
||||
placeholder = 'John Doe'
|
||||
value = { _profile.displayName } />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
i18nLabel = 'profileModal.email' >
|
||||
<TextInput
|
||||
keyboardType = { 'email-address' }
|
||||
onChangeText = { this._onChangeEmail }
|
||||
placeholder = 'email@example.com'
|
||||
value = { _profile.email } />
|
||||
</FormRow>
|
||||
<FormSectionHeader
|
||||
i18nLabel = 'profileModal.conferenceSection' />
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'profileModal.serverURL' >
|
||||
<TextInput
|
||||
autoCapitalize = 'none'
|
||||
onBlur = { this._onBlurServerURL }
|
||||
onChangeText = { this._onChangeServerURL }
|
||||
placeholder = { this.props._serverURL }
|
||||
ref = { this._setURLFieldReference }
|
||||
value = { _profile.serverURL } />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'profileModal.startWithAudioMuted' >
|
||||
<Switch
|
||||
onValueChange = {
|
||||
this._onStartAudioMutedChange
|
||||
}
|
||||
value = {
|
||||
_profile.startWithAudioMuted
|
||||
} />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
i18nLabel = 'profileModal.startWithVideoMuted' >
|
||||
<Switch
|
||||
onValueChange = {
|
||||
this._onStartVideoMutedChange
|
||||
}
|
||||
value = {
|
||||
_profile.startWithVideoMuted
|
||||
} />
|
||||
</FormRow>
|
||||
</ScrollView>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
_getSafetyPadding: () => Object;
|
||||
|
||||
/**
|
||||
* Calculates header safety padding for mobile devices. See comment in
|
||||
* functions.js.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getSafetyPadding() {
|
||||
if (isIPad() || this.props._aspectRatio === ASPECT_RATIO_NARROW) {
|
||||
const safeOffset = Math.max(getSafetyOffset(), HEADER_PADDING);
|
||||
|
||||
return {
|
||||
paddingTop: safeOffset
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
_onBlurServerURL: () => void;
|
||||
|
||||
/**
|
||||
* Handler the server URL lose focus event. Here we validate the server URL
|
||||
* and update it to the normalized version, or show an error if incorrect.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onBlurServerURL() {
|
||||
this._processServerURL(false /* hideOnSuccess */);
|
||||
}
|
||||
|
||||
_onChangeDisplayName: (string) => void;
|
||||
|
||||
_onChangeEmail: (string) => void;
|
||||
|
||||
_onChangeServerURL: (string) => void;
|
||||
|
||||
_onStartAudioMutedChange: (boolean) => void;
|
||||
|
||||
_onStartVideoMutedChange: (boolean) => void;
|
||||
|
||||
_onRequestClose: () => void;
|
||||
|
||||
/**
|
||||
* Processes the server URL. It normalizes it and an error alert is
|
||||
* displayed in case it's incorrect.
|
||||
*
|
||||
* @param {boolean} hideOnSuccess - True if the dialog should be hidden if
|
||||
* normalization / validation succeeds, false otherwise.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_processServerURL(hideOnSuccess: boolean) {
|
||||
const { serverURL } = this.props._profile;
|
||||
const normalizedURL = normalizeUserInputURL(serverURL);
|
||||
|
||||
if (normalizedURL === null) {
|
||||
this._showURLAlert();
|
||||
} else {
|
||||
this._onChangeServerURL(normalizedURL);
|
||||
if (hideOnSuccess) {
|
||||
this.props.dispatch(hideAppSettings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the back button.
|
||||
* Also invokes normalizeUserInputURL to validate the URL entered
|
||||
* by the user.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRequestClose() {
|
||||
this._processServerURL(true /* hideOnSuccess */);
|
||||
}
|
||||
|
||||
_setURLFieldReference: (React$ElementRef<*> | null) => void;
|
||||
|
||||
/**
|
||||
* Stores a reference to the URL field for later use.
|
||||
*
|
||||
* @protected
|
||||
* @param {Object} component - The field component.
|
||||
* @returns {void}
|
||||
*/
|
||||
_setURLFieldReference(component) {
|
||||
this._urlField = component;
|
||||
}
|
||||
|
||||
_showURLAlert: () => void;
|
||||
|
||||
/**
|
||||
* Shows an alert telling the user that the URL he/she entered was invalid.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_showURLAlert() {
|
||||
const { t } = this.props;
|
||||
|
||||
Alert.alert(
|
||||
t('profileModal.alertTitle'),
|
||||
t('profileModal.alertURLText'),
|
||||
[
|
||||
{
|
||||
onPress: () => this._urlField.focus(),
|
||||
text: t('profileModal.alertOk')
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(AppSettings));
|
||||
50
react/features/app-settings/components/BackButton.native.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
import { Icon } from '../../base/font-icons';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link BackButton}
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The action to be performed when the button is pressed.
|
||||
*/
|
||||
onPress: Function,
|
||||
|
||||
/**
|
||||
* An external style object passed to the component.
|
||||
*/
|
||||
style: Object
|
||||
};
|
||||
|
||||
/**
|
||||
* A component rendering a back button.
|
||||
*/
|
||||
export default class BackButton extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the button.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityLabel = { 'Back' }
|
||||
onPress = { this.props.onPress }>
|
||||
<Icon
|
||||
name = { 'arrow_back' }
|
||||
style = { [
|
||||
styles.backIcon,
|
||||
this.props.style
|
||||
] } />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
169
react/features/app-settings/components/FormRow.native.js
Normal file
@@ -0,0 +1,169 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getSafetyOffset } from '../../base/react';
|
||||
import { ASPECT_RATIO_WIDE } from '../../base/responsive-ui';
|
||||
|
||||
import styles, { ANDROID_UNDERLINE_COLOR, CONTAINER_PADDING } from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link FormRow}
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current aspect ratio of the screen.
|
||||
*/
|
||||
_aspectRatio: Symbol,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
children: Object,
|
||||
|
||||
/**
|
||||
* Prop to decide if a row separator is to be rendered.
|
||||
*/
|
||||
fieldSeparator: boolean,
|
||||
|
||||
/**
|
||||
* The i18n key of the text label of the form field.
|
||||
*/
|
||||
i18nLabel: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} which renders a standardized row on a
|
||||
* form. The component should have exactly one child component.
|
||||
*/
|
||||
class FormRow extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new {@code FormRow} instance.
|
||||
*
|
||||
* @param {Object} props - Component properties.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
React.Children.only(this.props.children);
|
||||
this._getDefaultFieldProps = this._getDefaultFieldProps.bind(this);
|
||||
this._getRowStyle = this._getRowStyle.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @override
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
// Some field types need additional props to look good and standardized
|
||||
// on a form.
|
||||
const newChild = React.cloneElement(
|
||||
this.props.children,
|
||||
this._getDefaultFieldProps(this.props.children)
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { this._getRowStyle() } >
|
||||
<View style = { styles.fieldLabelContainer } >
|
||||
<Text style = { styles.text } >
|
||||
{ t(this.props.i18nLabel) }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.fieldValueContainer } >
|
||||
{ newChild }
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_getDefaultFieldProps: (field: Component<*, *>) => Object;
|
||||
|
||||
/**
|
||||
* Assembles the default props to the field child component of this form
|
||||
* row.
|
||||
*
|
||||
* Currently tested/supported field types:
|
||||
* - TextInput
|
||||
* - Switch (needs no addition props ATM).
|
||||
*
|
||||
* @private
|
||||
* @param {Object} field - The field (child) component.
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getDefaultFieldProps(field: Object) {
|
||||
if (field && field.type) {
|
||||
switch (field.type.displayName) {
|
||||
case 'TextInput':
|
||||
return {
|
||||
style: styles.textInputField,
|
||||
underlineColorAndroid: ANDROID_UNDERLINE_COLOR
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
_getRowStyle: () => Array<Object>;
|
||||
|
||||
/**
|
||||
* Assembles the row style array based on the row's props. For padding, see
|
||||
* comment in functions.js.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
_getRowStyle() {
|
||||
const rowStyle = [
|
||||
styles.fieldContainer
|
||||
];
|
||||
|
||||
if (this.props.fieldSeparator) {
|
||||
rowStyle.push(styles.fieldSeparator);
|
||||
}
|
||||
|
||||
if (this.props._aspectRatio === ASPECT_RATIO_WIDE) {
|
||||
const safeOffset = Math.max(
|
||||
getSafetyOffset() - CONTAINER_PADDING, 0
|
||||
);
|
||||
|
||||
rowStyle.push({
|
||||
marginLeft: safeOffset,
|
||||
marginRight: safeOffset
|
||||
});
|
||||
}
|
||||
|
||||
return rowStyle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props of
|
||||
* {@code FormRow}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(FormRow));
|
||||
@@ -0,0 +1,117 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getSafetyOffset } from '../../base/react';
|
||||
import { ASPECT_RATIO_WIDE } from '../../base/responsive-ui';
|
||||
|
||||
import styles, { CONTAINER_PADDING } from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link FormSectionHeader}
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current aspect ratio of the screen.
|
||||
*/
|
||||
_aspectRatio: Symbol,
|
||||
|
||||
/**
|
||||
* The i18n key of the text label of the section.
|
||||
*/
|
||||
i18nLabel: string,
|
||||
|
||||
/**
|
||||
* An external style object passed to the component.
|
||||
*/
|
||||
style: Object,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} which renders a section header on a
|
||||
* form. This calculates the available safe view as well.
|
||||
*/
|
||||
class FormSectionHeader extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new {@code FormSectionHeader} instance.
|
||||
*
|
||||
* @param {Object} props - Component properties.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._getSafetyMargin = this._getSafetyMargin.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @override
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
styles.formSectionTitle,
|
||||
this.props.style,
|
||||
this._getSafetyMargin()
|
||||
] } >
|
||||
<Text>
|
||||
{ t(this.props.i18nLabel) }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_getSafetyMargin: () => Object;
|
||||
|
||||
/**
|
||||
* Calculates the safety margin for this header. See comment in
|
||||
* functions.js.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getSafetyMargin() {
|
||||
if (this.props._aspectRatio === ASPECT_RATIO_WIDE) {
|
||||
const safeOffset
|
||||
= Math.max(getSafetyOffset() - CONTAINER_PADDING, 0);
|
||||
|
||||
return {
|
||||
marginLeft: safeOffset,
|
||||
marginRight: safeOffset
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props of
|
||||
* {@code FormSectionHeader}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(FormSectionHeader));
|
||||
1
react/features/app-settings/components/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as AppSettings } from './AppSettings';
|
||||
133
react/features/app-settings/components/styles.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
BoxModel,
|
||||
ColorPalette,
|
||||
createStyleSheet
|
||||
} from '../../base/styles';
|
||||
|
||||
export const ANDROID_UNDERLINE_COLOR = 'transparent';
|
||||
export const CONTAINER_PADDING = 2 * BoxModel.padding;
|
||||
export const HEADER_COLOR = ColorPalette.blue;
|
||||
export const HEADER_PADDING = BoxModel.padding;
|
||||
const TEXT_SIZE = 17;
|
||||
|
||||
/**
|
||||
* The styles of the React {@code Components} of the feature
|
||||
* {@code app-settings}.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
|
||||
/**
|
||||
* The back button style.
|
||||
*/
|
||||
backIcon: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 26,
|
||||
padding: 8,
|
||||
paddingRight: 22
|
||||
},
|
||||
|
||||
/**
|
||||
* Standardized style for a field container {@code View}.
|
||||
*/
|
||||
fieldContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
minHeight: 65
|
||||
},
|
||||
|
||||
/**
|
||||
* Standard container for a {@code View} containing a field label.
|
||||
*/
|
||||
fieldLabelContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
marginRight: 5
|
||||
},
|
||||
|
||||
/**
|
||||
* Field container style for all but last row {@code View}.
|
||||
*/
|
||||
fieldSeparator: {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for the {@code View} containing each
|
||||
* field values (the actual field).
|
||||
*/
|
||||
fieldValueContainer: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
|
||||
formSectionTitle: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
marginTop: 5,
|
||||
padding: 5
|
||||
},
|
||||
|
||||
/**
|
||||
* Page header {@code View}.
|
||||
*/
|
||||
headerContainer: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: HEADER_COLOR,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
padding: HEADER_PADDING
|
||||
},
|
||||
|
||||
/**
|
||||
* The title {@code Text} of the header.
|
||||
*/
|
||||
headerTitle: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 22
|
||||
},
|
||||
|
||||
/**
|
||||
* Style of the ScrollView to be able to scroll the content.
|
||||
*/
|
||||
scrollView: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* The back button style on the settings screen.
|
||||
*/
|
||||
settingsBackButton: {
|
||||
color: ColorPalette.white
|
||||
},
|
||||
|
||||
/**
|
||||
* The top level container {@code View}.
|
||||
*/
|
||||
settingsContainer: {
|
||||
backgroundColor: ColorPalette.white,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
margin: 0,
|
||||
padding: CONTAINER_PADDING,
|
||||
paddingTop: 0
|
||||
},
|
||||
|
||||
/**
|
||||
* Global {@code Text} color for the page.
|
||||
*/
|
||||
text: {
|
||||
color: ColorPalette.black,
|
||||
fontSize: TEXT_SIZE
|
||||
},
|
||||
|
||||
/**
|
||||
* Standard text input field style.
|
||||
*/
|
||||
textInputField: {
|
||||
flex: 1,
|
||||
fontSize: TEXT_SIZE,
|
||||
textAlign: 'right'
|
||||
}
|
||||
});
|
||||
37
react/features/app-settings/functions.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// @flow
|
||||
|
||||
import { parseStandardURIString } from '../base/util';
|
||||
|
||||
/**
|
||||
* Normalizes a URL entered by the user.
|
||||
* FIXME: Consider adding this to base/util/uri.
|
||||
*
|
||||
* @param {string} url - The URL to validate.
|
||||
* @returns {string|null} - The normalized URL, or null if the URL is invalid.
|
||||
*/
|
||||
export function normalizeUserInputURL(url: string) {
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
if (url) {
|
||||
url = url.replace(/\s/g, '').toLowerCase();
|
||||
const urlRegExp = new RegExp('^(\\w+://)?(.+)$');
|
||||
const urlComponents = urlRegExp.exec(url);
|
||||
|
||||
if (!urlComponents[1] || !urlComponents[1].startsWith('http')) {
|
||||
url = `https://${urlComponents[2]}`;
|
||||
}
|
||||
|
||||
const parsedURI
|
||||
= parseStandardURIString(url);
|
||||
|
||||
if (!parsedURI.host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsedURI.toString();
|
||||
}
|
||||
|
||||
return url;
|
||||
|
||||
/* eslint-enable no-param-reassign */
|
||||
}
|
||||
4
react/features/app-settings/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './actions';
|
||||
export * from './components';
|
||||
|
||||
import './reducer';
|
||||
37
react/features/app-settings/middleware.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// @flow
|
||||
|
||||
import { SET_ROOM } from '../base/conference';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { hideAppSettings } from './actions';
|
||||
|
||||
/**
|
||||
* The Redux middleware to trigger settings screen show or hide
|
||||
* when necessary.
|
||||
*
|
||||
* @param {Store} store - The Redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case SET_ROOM:
|
||||
return _closeAppSettings(store, next, action);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Hides the settings screen.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @param {Dispatch} next - The redux dispatch function.
|
||||
* @param {Action} action - The redux action.
|
||||
* @private
|
||||
* @returns {Object} The new state.
|
||||
*/
|
||||
function _closeAppSettings({ dispatch }, next, action) {
|
||||
dispatch(hideAppSettings());
|
||||
|
||||
return next(action);
|
||||
}
|
||||
31
react/features/app-settings/reducer.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
HIDE_APP_SETTINGS,
|
||||
SHOW_APP_SETTINGS
|
||||
} from './actionTypes';
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
visible: false
|
||||
};
|
||||
|
||||
ReducerRegistry.register(
|
||||
'features/app-settings', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case HIDE_APP_SETTINGS:
|
||||
return {
|
||||
...state,
|
||||
visible: false
|
||||
};
|
||||
|
||||
case SHOW_APP_SETTINGS:
|
||||
return {
|
||||
...state,
|
||||
visible: true
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import { setRoom } from '../base/conference';
|
||||
import { configWillLoad, loadConfigError, setConfig } from '../base/config';
|
||||
import { setLocationURL } from '../base/connection';
|
||||
import { loadConfig } from '../base/lib-jitsi-meet';
|
||||
import { getProfile } from '../base/profile';
|
||||
import { parseURIString } from '../base/util';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
|
||||
@@ -82,7 +83,11 @@ function _appNavigateToMandatoryLocation(
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(() => dispatch(setConfig(config)));
|
||||
const profile = getProfile(getState());
|
||||
|
||||
return promise.then(() => dispatch(setConfig(
|
||||
_mergeConfigWithProfile(config, profile)
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,3 +250,23 @@ function _loadConfig({ contextRoot, host, protocol, room }) {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the downloaded config with the current profile values. The profile
|
||||
* values are named the same way as the config values in the config.js so
|
||||
* a clean merge is possible.
|
||||
*
|
||||
* @param {Object|undefined} config - The downloaded config.
|
||||
* @param {Object} profile - The persisted profile.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _mergeConfigWithProfile(config, profile) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
...profile
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import {
|
||||
localParticipantJoined,
|
||||
localParticipantLeft
|
||||
} from '../../base/participants';
|
||||
import { getProfile } from '../../base/profile';
|
||||
import { Fragment, RouteRegistry } from '../../base/react';
|
||||
import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
|
||||
import { PersistenceRegistry } from '../../base/storage';
|
||||
import { toURLString } from '../../base/util';
|
||||
import { OverlayContainer } from '../../overlay';
|
||||
import { BlankPage } from '../../welcome';
|
||||
@@ -72,6 +74,7 @@ export class AbstractApp extends Component {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
||||
/**
|
||||
* The Route rendered by this {@code AbstractApp}.
|
||||
*
|
||||
@@ -79,13 +82,37 @@ export class AbstractApp extends Component {
|
||||
*/
|
||||
route: undefined,
|
||||
|
||||
/**
|
||||
* The state of the »possible« async initialization of
|
||||
* the {@code AbstractApp}.
|
||||
*/
|
||||
appAsyncInitialized: false,
|
||||
|
||||
/**
|
||||
* The redux store used by this {@code AbstractApp}.
|
||||
*
|
||||
* @type {Store}
|
||||
*/
|
||||
store: this._maybeCreateStore(props)
|
||||
store: undefined
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the mobile {@code AbstractApp} wait until the
|
||||
* {@code AsyncStorage} implementation of {@code Storage} initializes
|
||||
* fully.
|
||||
*
|
||||
* @private
|
||||
* @see {@link #_initStorage}
|
||||
* @type {Promise}
|
||||
*/
|
||||
this._init
|
||||
= this._initStorage()
|
||||
.catch(() => { /* AbstractApp should always initialize! */ })
|
||||
.then(() =>
|
||||
this.setState({
|
||||
route: undefined,
|
||||
store: this._maybeCreateStore(props)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,29 +122,50 @@ export class AbstractApp extends Component {
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentWillMount() {
|
||||
const { dispatch } = this._getStore();
|
||||
this._init.then(() => {
|
||||
const { dispatch } = this._getStore();
|
||||
|
||||
dispatch(appWillMount(this));
|
||||
dispatch(appWillMount(this));
|
||||
|
||||
// FIXME I believe it makes more sense for a middleware to dispatch
|
||||
// localParticipantJoined on APP_WILL_MOUNT because the order of actions
|
||||
// is important, not the call site. Moreover, we've got localParticipant
|
||||
// business logic in the React Component (i.e. UI) AbstractApp now.
|
||||
let localParticipant;
|
||||
// FIXME I believe it makes more sense for a middleware to dispatch
|
||||
// localParticipantJoined on APP_WILL_MOUNT because the order of
|
||||
// actions is important, not the call site. Moreover, we've got
|
||||
// localParticipant business logic in the React Component
|
||||
// (i.e. UI) AbstractApp now.
|
||||
let localParticipant = {};
|
||||
|
||||
if (typeof APP === 'object') {
|
||||
localParticipant = {
|
||||
avatarID: APP.settings.getAvatarId(),
|
||||
avatarURL: APP.settings.getAvatarUrl(),
|
||||
email: APP.settings.getEmail(),
|
||||
name: APP.settings.getDisplayName()
|
||||
};
|
||||
}
|
||||
dispatch(localParticipantJoined(localParticipant));
|
||||
if (typeof APP === 'object') {
|
||||
localParticipant = {
|
||||
avatarID: APP.settings.getAvatarId(),
|
||||
avatarURL: APP.settings.getAvatarUrl(),
|
||||
email: APP.settings.getEmail(),
|
||||
name: APP.settings.getDisplayName()
|
||||
};
|
||||
}
|
||||
|
||||
// If a URL was explicitly specified to this React Component, then open
|
||||
// it; otherwise, use a default.
|
||||
this._openURL(toURLString(this.props.url) || this._getDefaultURL());
|
||||
// Profile is the new React compatible settings.
|
||||
const profile = getProfile(this._getStore().getState());
|
||||
|
||||
if (profile) {
|
||||
localParticipant.email
|
||||
= profile.email || localParticipant.email;
|
||||
localParticipant.name
|
||||
= profile.displayName || localParticipant.name;
|
||||
}
|
||||
|
||||
// We set the initialized state here and not in the contructor to
|
||||
// make sure that {@code componentWillMount} gets invoked before
|
||||
// the app tries to render the actual app content.
|
||||
this.setState({
|
||||
appAsyncInitialized: true
|
||||
});
|
||||
|
||||
dispatch(localParticipantJoined(localParticipant));
|
||||
|
||||
// If a URL was explicitly specified to this React Component,
|
||||
// then open it; otherwise, use a default.
|
||||
this._openURL(toURLString(this.props.url) || this._getDefaultURL());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,32 +178,36 @@ export class AbstractApp extends Component {
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// The consumer of this AbstractApp did not provide a redux store.
|
||||
if (typeof nextProps.store === 'undefined'
|
||||
const { props } = this;
|
||||
|
||||
// The consumer of this AbstractApp did provide a redux store
|
||||
// before. Which means that the consumer changed their mind. In
|
||||
// such a case this instance should create its own internal
|
||||
// redux store. If the consumer did not provide a redux store
|
||||
// before, then this instance is using its own internal redux
|
||||
// store already.
|
||||
&& typeof this.props.store !== 'undefined') {
|
||||
this.setState({
|
||||
store: this._maybeCreateStore(nextProps)
|
||||
});
|
||||
}
|
||||
this._init.then(() => {
|
||||
// The consumer of this AbstractApp did not provide a redux store.
|
||||
if (typeof nextProps.store === 'undefined'
|
||||
|
||||
// Deal with URL changes.
|
||||
let { url } = nextProps;
|
||||
// The consumer of this AbstractApp did provide a redux
|
||||
// store before. Which means that the consumer changed
|
||||
// their mind. In such a case this instance should create
|
||||
// its own internal redux store. If the consumer did not
|
||||
// provide a redux store before, then this instance is
|
||||
// using its own internal redux store already.
|
||||
&& typeof props.store !== 'undefined') {
|
||||
this.setState({
|
||||
store: this._maybeCreateStore(nextProps)
|
||||
});
|
||||
}
|
||||
|
||||
url = toURLString(url);
|
||||
if (toURLString(this.props.url) !== url
|
||||
// Deal with URL changes.
|
||||
let { url } = nextProps;
|
||||
|
||||
// XXX Refer to the implementation of loadURLObject: in
|
||||
// ios/sdk/src/JitsiMeetView.m for further information.
|
||||
|| this.props.timestamp !== nextProps.timestamp) {
|
||||
this._openURL(url || this._getDefaultURL());
|
||||
}
|
||||
url = toURLString(url);
|
||||
if (toURLString(props.url) !== url
|
||||
|
||||
// XXX Refer to the implementation of loadURLObject: in
|
||||
// ios/sdk/src/JitsiMeetView.m for further information.
|
||||
|| props.timestamp !== nextProps.timestamp) {
|
||||
this._openURL(url || this._getDefaultURL());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,6 +240,24 @@ export class AbstractApp extends Component {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delays this {@code AbstractApp}'s startup until the {@code Storage}
|
||||
* implementation of {@code localStorage} initializes. While the
|
||||
* initialization is instantaneous on Web (with Web Storage API), it is
|
||||
* asynchronous on mobile/react-native.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_initStorage() {
|
||||
const localStorageInitializing = window.localStorage._initializing;
|
||||
|
||||
return (
|
||||
typeof localStorageInitializing === 'undefined'
|
||||
? Promise.resolve()
|
||||
: localStorageInitializing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -195,10 +265,10 @@ export class AbstractApp extends Component {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { route } = this.state;
|
||||
const { appAsyncInitialized, route } = this.state;
|
||||
const component = (route && route.component) || BlankPage;
|
||||
|
||||
if (component) {
|
||||
if (appAsyncInitialized && component) {
|
||||
return (
|
||||
<I18nextProvider i18n = { i18next }>
|
||||
<Provider store = { this._getStore() }>
|
||||
@@ -281,7 +351,11 @@ export class AbstractApp extends Component {
|
||||
middleware = compose(middleware, devToolsExtension());
|
||||
}
|
||||
|
||||
return createStore(reducer, middleware);
|
||||
return (
|
||||
createStore(
|
||||
reducer,
|
||||
PersistenceRegistry.getPersistedState(),
|
||||
middleware));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,7 +379,10 @@ export class AbstractApp extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
return this.props.defaultURL || DEFAULT_URL;
|
||||
return (
|
||||
this.props.defaultURL
|
||||
|| getProfile(this._getStore().getState()).serverURL
|
||||
|| DEFAULT_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,8 +6,11 @@ import { Linking } from 'react-native';
|
||||
|
||||
import '../../analytics';
|
||||
import '../../authentication';
|
||||
import { AspectRatioDetector } from '../../base/aspect-ratio';
|
||||
import { Platform } from '../../base/react';
|
||||
import {
|
||||
AspectRatioDetector,
|
||||
ReducedUIDetector
|
||||
} from '../../base/responsive-ui';
|
||||
import '../../mobile/audio-mode';
|
||||
import '../../mobile/background';
|
||||
import '../../mobile/callkit';
|
||||
@@ -97,7 +100,9 @@ export class App extends AbstractApp {
|
||||
_createElement(component, props) {
|
||||
return (
|
||||
<AspectRatioDetector>
|
||||
{ super._createElement(component, props) }
|
||||
<ReducedUIDetector>
|
||||
{ super._createElement(component, props) }
|
||||
</ReducedUIDetector>
|
||||
</AspectRatioDetector>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AtlasKitThemeProvider } from '@atlaskit/theme';
|
||||
import React from 'react';
|
||||
|
||||
import '../../base/responsive-ui';
|
||||
import { getLocationContextRoot } from '../../base/util';
|
||||
import '../../room-lock';
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* The type of (redux) action which sets the aspect ratio of the app's user
|
||||
* interface.
|
||||
*
|
||||
* {
|
||||
* type: SET_ASPECT_RATIO,
|
||||
* aspectRatio: Symbol
|
||||
* }
|
||||
*/
|
||||
export const SET_ASPECT_RATIO = Symbol('SET_ASPECT_RATIO');
|
||||
@@ -1,22 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { SET_ASPECT_RATIO } from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW, ASPECT_RATIO_WIDE } from './constants';
|
||||
|
||||
/**
|
||||
* Sets the aspect ratio of the app's user interface based on specific width and
|
||||
* height.
|
||||
*
|
||||
* @param {number} width - The width of the app's user interface.
|
||||
* @param {number} height - The height of the app's user interface.
|
||||
* @returns {{
|
||||
* type: SET_ASPECT_RATIO,
|
||||
* aspectRatio: Symbol
|
||||
* }}
|
||||
*/
|
||||
export function setAspectRatio(width: number, height: number): Object {
|
||||
return {
|
||||
type: SET_ASPECT_RATIO,
|
||||
aspectRatio: width < height ? ASPECT_RATIO_NARROW : ASPECT_RATIO_WIDE
|
||||
};
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './AspectRatioAware';
|
||||
export { default as AspectRatioDetector } from './AspectRatioDetector';
|
||||
@@ -3,9 +3,8 @@
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import {
|
||||
START_MUTED_SERVER_AUDIO_,
|
||||
START_MUTED_SERVER_VIDEO_,
|
||||
sendAnalyticsEvent
|
||||
createStartMutedConfigurationEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import { getName } from '../../app';
|
||||
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
|
||||
@@ -90,12 +89,8 @@ function _addConferenceListeners(conference, dispatch) {
|
||||
const audioMuted = Boolean(conference.startAudioMuted);
|
||||
const videoMuted = Boolean(conference.startVideoMuted);
|
||||
|
||||
sendAnalyticsEvent(
|
||||
`${START_MUTED_SERVER_AUDIO_}.${
|
||||
audioMuted ? 'muted' : 'unmuted'}`);
|
||||
sendAnalyticsEvent(
|
||||
`${START_MUTED_SERVER_VIDEO_}.${
|
||||
videoMuted ? 'muted' : 'unmuted'}`);
|
||||
sendAnalytics(createStartMutedConfigurationEvent(
|
||||
'remote', audioMuted, videoMuted));
|
||||
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
|
||||
videoMuted ? 'video' : ''}`);
|
||||
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import {
|
||||
_LOCAL,
|
||||
_REMOTE,
|
||||
AUDIO_ONLY_DISABLED,
|
||||
PINNED_,
|
||||
UNPINNED_,
|
||||
sendAnalyticsEvent
|
||||
ACTION_PINNED,
|
||||
ACTION_UNPINNED,
|
||||
createAudioOnlyDisableEvent,
|
||||
createPinnedEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import { CONNECTION_ESTABLISHED } from '../connection';
|
||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../media';
|
||||
@@ -131,7 +130,7 @@ function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
if (getState()['features/base/conference'].audioOnly) {
|
||||
sendAnalyticsEvent(AUDIO_ONLY_DISABLED);
|
||||
sendAnalytics(createAudioOnlyDisableEvent());
|
||||
logger.log('Audio only disabled');
|
||||
dispatch(setAudioOnly(false));
|
||||
}
|
||||
@@ -186,26 +185,29 @@ function _conferenceJoined(store, next, action) {
|
||||
function _pinParticipant(store, next, action) {
|
||||
const state = store.getState();
|
||||
const { conference } = state['features/base/conference'];
|
||||
|
||||
if (!conference) {
|
||||
return next(action);
|
||||
}
|
||||
|
||||
const participants = state['features/base/participants'];
|
||||
const id = action.participant.id;
|
||||
const participantById = getParticipantById(participants, id);
|
||||
let pin;
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
const pinnedParticipant = getPinnedParticipant(participants);
|
||||
const actionName = action.participant.id ? PINNED_ : UNPINNED_;
|
||||
let videoType;
|
||||
const actionName = id ? ACTION_PINNED : ACTION_UNPINNED;
|
||||
const local
|
||||
= (participantById && participantById.local)
|
||||
|| (!id && pinnedParticipant && pinnedParticipant.local);
|
||||
|
||||
if ((participantById && participantById.local)
|
||||
|| (!id && pinnedParticipant && pinnedParticipant.local)) {
|
||||
videoType = _LOCAL;
|
||||
} else {
|
||||
videoType = _REMOTE;
|
||||
}
|
||||
|
||||
sendAnalyticsEvent(
|
||||
`${actionName}.${videoType}`,
|
||||
{ value: conference.getParticipantCount() });
|
||||
sendAnalytics(createPinnedEvent(
|
||||
actionName,
|
||||
local ? 'local' : id,
|
||||
{
|
||||
local,
|
||||
'participant_count': conference.getParticipantCount()
|
||||
}));
|
||||
}
|
||||
|
||||
// The following condition prevents signaling to pin local participant and
|
||||
@@ -215,6 +217,8 @@ function _pinParticipant(store, next, action) {
|
||||
// - If we don't have an ID (i.e. no participant identified by an ID), we
|
||||
// check for local participant. If she's currently pinned, then this
|
||||
// action will unpin her and that's why we won't signal here too.
|
||||
let pin;
|
||||
|
||||
if (participantById) {
|
||||
pin = !participantById.local && !participantById.isBot;
|
||||
} else {
|
||||
@@ -223,7 +227,6 @@ function _pinParticipant(store, next, action) {
|
||||
pin = !localParticipant || !localParticipant.pinned;
|
||||
}
|
||||
if (pin) {
|
||||
|
||||
try {
|
||||
conference.pinParticipant(id);
|
||||
} catch (err) {
|
||||
@@ -317,9 +320,11 @@ function _setLastN(store, next, action) {
|
||||
function _setReceiveVideoQuality({ dispatch, getState }, next, action) {
|
||||
const { audioOnly, conference } = getState()['features/base/conference'];
|
||||
|
||||
conference.setReceiverVideoConstraint(action.receiveVideoQuality);
|
||||
if (audioOnly) {
|
||||
dispatch(toggleAudioOnly());
|
||||
if (conference) {
|
||||
conference.setReceiverVideoConstraint(action.receiveVideoQuality);
|
||||
if (audioOnly) {
|
||||
dispatch(toggleAudioOnly());
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -177,6 +177,7 @@ export function connectionEstablished(connection: Object) {
|
||||
* @param {string} [message] - Error message.
|
||||
* @param {Object} [credentials] - The invalid credentials that failed
|
||||
* the authentication.
|
||||
* @param {Object} [details] - The details about the connection failed event.
|
||||
* @public
|
||||
* @returns {{
|
||||
* type: CONNECTION_FAILED,
|
||||
@@ -188,7 +189,8 @@ export function connectionFailed(
|
||||
connection: Object,
|
||||
error: string,
|
||||
message: ?string,
|
||||
credentials: ?Object) {
|
||||
credentials: ?Object,
|
||||
details: ?Object) {
|
||||
return {
|
||||
type: CONNECTION_FAILED,
|
||||
connection,
|
||||
@@ -201,7 +203,8 @@ export function connectionFailed(
|
||||
? credentials
|
||||
: undefined,
|
||||
message,
|
||||
name: error
|
||||
name: error,
|
||||
details
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,60 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M854 470v84h-520l238 240-60 60-342-342 342-342 60 60-238 240h520z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"arrow_back"
|
||||
],
|
||||
"defaultCode": 58820,
|
||||
"grid": 24
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"ligatures": "arrow_back",
|
||||
"id": 45,
|
||||
"order": 924,
|
||||
"prevSize": 24,
|
||||
"code": 58820,
|
||||
"name": "arrow_back"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 45
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M658 316l-196 196 196 196-60 60-256-256 256-256z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"navigate_before"
|
||||
],
|
||||
"defaultCode": 58376,
|
||||
"grid": 24
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"ligatures": "chevron_left, navigate_before",
|
||||
"id": 152,
|
||||
"order": 923,
|
||||
"prevSize": 24,
|
||||
"code": 58376,
|
||||
"name": "navigate_before"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 152
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -24,9 +78,9 @@
|
||||
"code": 59403,
|
||||
"name": "public"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 605
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -53,7 +107,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
"iconIdx": 1
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -80,7 +134,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 1
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -107,7 +161,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 2
|
||||
"iconIdx": 3
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -134,7 +188,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 3
|
||||
"iconIdx": 4
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -161,7 +215,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
"iconIdx": 5
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -188,7 +242,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 5
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -217,7 +271,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 6
|
||||
"iconIdx": 7
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -244,7 +298,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 7
|
||||
"iconIdx": 8
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -271,7 +325,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 8
|
||||
"iconIdx": 9
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -300,7 +354,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 9
|
||||
"iconIdx": 10
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -329,7 +383,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 10
|
||||
"iconIdx": 11
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -358,7 +412,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 11
|
||||
"iconIdx": 12
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -387,7 +441,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
"iconIdx": 13
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -416,7 +470,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
"iconIdx": 14
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -442,7 +496,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 14
|
||||
"iconIdx": 15
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -468,7 +522,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 15
|
||||
"iconIdx": 16
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -494,7 +548,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 16
|
||||
"iconIdx": 17
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -520,7 +574,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 17
|
||||
"iconIdx": 18
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -546,7 +600,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 18
|
||||
"iconIdx": 19
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -572,7 +626,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 19
|
||||
"iconIdx": 20
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -598,7 +652,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 20
|
||||
"iconIdx": 21
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -616,7 +670,7 @@
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 10,
|
||||
"order": 900,
|
||||
"order": 922,
|
||||
"ligatures": "expand_less",
|
||||
"prevSize": 32,
|
||||
"code": 59679,
|
||||
@@ -624,7 +678,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 21
|
||||
"iconIdx": 22
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -650,7 +704,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
"iconIdx": 23
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -676,7 +730,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 23
|
||||
"iconIdx": 24
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -702,7 +756,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 24
|
||||
"iconIdx": 25
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -728,7 +782,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 25
|
||||
"iconIdx": 26
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -754,7 +808,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 26
|
||||
"iconIdx": 27
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -780,7 +834,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 27
|
||||
"iconIdx": 28
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -806,7 +860,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 28
|
||||
"iconIdx": 29
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -832,7 +886,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 29
|
||||
"iconIdx": 30
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -858,7 +912,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 30
|
||||
"iconIdx": 31
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -884,7 +938,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 31
|
||||
"iconIdx": 32
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -910,7 +964,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 32
|
||||
"iconIdx": 33
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -936,7 +990,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 33
|
||||
"iconIdx": 34
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -962,7 +1016,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 34
|
||||
"iconIdx": 35
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -988,7 +1042,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 35
|
||||
"iconIdx": 36
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1014,7 +1068,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 36
|
||||
"iconIdx": 37
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1040,7 +1094,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 37
|
||||
"iconIdx": 38
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1066,7 +1120,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 38
|
||||
"iconIdx": 39
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1092,7 +1146,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 39
|
||||
"iconIdx": 40
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1118,7 +1172,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 40
|
||||
"iconIdx": 41
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1144,7 +1198,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 41
|
||||
"iconIdx": 42
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1170,7 +1224,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 42
|
||||
"iconIdx": 43
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1199,7 +1253,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 43
|
||||
"iconIdx": 44
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1229,7 +1283,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 44
|
||||
"iconIdx": 45
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1259,7 +1313,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 45
|
||||
"iconIdx": 46
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1285,7 +1339,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 46
|
||||
"iconIdx": 47
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1311,7 +1365,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 47
|
||||
"iconIdx": 48
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
@@ -1337,7 +1391,7 @@
|
||||
},
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 48
|
||||
"iconIdx": 49
|
||||
}
|
||||
],
|
||||
"height": 1024,
|
||||
|
||||
129
react/features/base/i18n/BuiltinLanguages.native.js
Normal file
@@ -0,0 +1,129 @@
|
||||
// @flow
|
||||
|
||||
import i18next from 'i18next';
|
||||
|
||||
/**
|
||||
* The builtin languages.
|
||||
*/
|
||||
const _LANGUAGES = {
|
||||
|
||||
// Bulgarian
|
||||
'bg': {
|
||||
languages: require('../../../../lang/languages-bg'),
|
||||
main: require('../../../../lang/main-bg')
|
||||
},
|
||||
|
||||
// German
|
||||
'de': {
|
||||
languages: require('../../../../lang/languages-de'),
|
||||
main: require('../../../../lang/main-de')
|
||||
},
|
||||
|
||||
// Esperanto
|
||||
'eo': {
|
||||
languages: require('../../../../lang/languages-eo'),
|
||||
main: require('../../../../lang/main-eo')
|
||||
},
|
||||
|
||||
// Spanish
|
||||
'es': {
|
||||
languages: require('../../../../lang/languages-es'),
|
||||
main: require('../../../../lang/main-es')
|
||||
},
|
||||
|
||||
// French
|
||||
'fr': {
|
||||
languages: require('../../../../lang/languages-fr'),
|
||||
main: require('../../../../lang/main-fr')
|
||||
},
|
||||
|
||||
// Armenian
|
||||
'hy': {
|
||||
languages: require('../../../../lang/languages-hy'),
|
||||
main: require('../../../../lang/main-hy')
|
||||
},
|
||||
|
||||
// Italian
|
||||
'it': {
|
||||
languages: require('../../../../lang/languages-it'),
|
||||
main: require('../../../../lang/main-it')
|
||||
},
|
||||
|
||||
// Norwegian Bokmal
|
||||
'nb': {
|
||||
languages: require('../../../../lang/languages-nb'),
|
||||
main: require('../../../../lang/main-nb')
|
||||
},
|
||||
|
||||
// Occitan
|
||||
'oc': {
|
||||
languages: require('../../../../lang/languages-oc'),
|
||||
main: require('../../../../lang/main-oc')
|
||||
},
|
||||
|
||||
// Polish
|
||||
'pl': {
|
||||
languages: require('../../../../lang/languages-pl'),
|
||||
main: require('../../../../lang/main-pl')
|
||||
},
|
||||
|
||||
// Portuguese (Brazil)
|
||||
'ptBR': {
|
||||
languages: require('../../../../lang/languages-ptBR'),
|
||||
main: require('../../../../lang/main-ptBR')
|
||||
},
|
||||
|
||||
// Russian
|
||||
'ru': {
|
||||
languages: require('../../../../lang/languages-ru'),
|
||||
main: require('../../../../lang/main-ru')
|
||||
},
|
||||
|
||||
// Slovak
|
||||
'sk': {
|
||||
languages: require('../../../../lang/languages-sk'),
|
||||
main: require('../../../../lang/main-sk')
|
||||
},
|
||||
|
||||
// Slovenian
|
||||
'sl': {
|
||||
languages: require('../../../../lang/languages-sl'),
|
||||
main: require('../../../../lang/main-sl')
|
||||
},
|
||||
|
||||
// Swedish
|
||||
'sv': {
|
||||
languages: require('../../../../lang/languages-sv'),
|
||||
main: require('../../../../lang/main-sv')
|
||||
},
|
||||
|
||||
// Turkish
|
||||
'tr': {
|
||||
languages: require('../../../../lang/languages-tr'),
|
||||
main: require('../../../../lang/main-tr')
|
||||
},
|
||||
|
||||
// Chinese (China)
|
||||
'zhCN': {
|
||||
languages: require('../../../../lang/languages-zhCN'),
|
||||
main: require('../../../../lang/main-zhCN')
|
||||
}
|
||||
};
|
||||
|
||||
// Register all builtin languages with the i18n library.
|
||||
for (const name in _LANGUAGES) { // eslint-disable-line guard-for-in
|
||||
const { languages, main } = _LANGUAGES[name];
|
||||
|
||||
i18next.addResourceBundle(
|
||||
name,
|
||||
'languages',
|
||||
languages,
|
||||
/* deep */ true,
|
||||
/* overwrite */ true);
|
||||
i18next.addResourceBundle(
|
||||
name,
|
||||
'main',
|
||||
main,
|
||||
/* deep */ true,
|
||||
/* overwrite */ true);
|
||||
}
|
||||
0
react/features/base/i18n/BuiltinLanguages.web.js
Normal file
@@ -1,4 +1,4 @@
|
||||
/* @flow */
|
||||
// @flow
|
||||
|
||||
import i18next from 'i18next';
|
||||
import I18nextXHRBackend from 'i18next-xhr-backend';
|
||||
@@ -58,7 +58,7 @@ const options = {
|
||||
};
|
||||
|
||||
i18next
|
||||
.use(I18nextXHRBackend)
|
||||
.use(navigator.product === 'ReactNative' ? {} : I18nextXHRBackend)
|
||||
.use(languageDetector)
|
||||
.use({
|
||||
name: 'resolveAppName',
|
||||
@@ -68,17 +68,23 @@ i18next
|
||||
.init(options);
|
||||
|
||||
// Add default language which is preloaded from the source code.
|
||||
i18next.addResourceBundle(
|
||||
DEFAULT_LANGUAGE,
|
||||
'main',
|
||||
MAIN_RESOURCES,
|
||||
/* deep */ true,
|
||||
/* overwrite */ true);
|
||||
i18next.addResourceBundle(
|
||||
DEFAULT_LANGUAGE,
|
||||
'languages',
|
||||
LANGUAGES_RESOURCES,
|
||||
/* deep */ true,
|
||||
/* overwrite */ true);
|
||||
i18next.addResourceBundle(
|
||||
DEFAULT_LANGUAGE,
|
||||
'main',
|
||||
MAIN_RESOURCES,
|
||||
/* deep */ true,
|
||||
/* overwrite */ true);
|
||||
|
||||
// Add builtin languages.
|
||||
// XXX: Note we are using require here, because we want the side-effects of the
|
||||
// import, but imports can only be placed at the top, and it would be too early,
|
||||
// since i18next is not yet initialized at that point.
|
||||
require('./BuiltinLanguages');
|
||||
|
||||
export default i18next;
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
import JitsiMeetJS from './_';
|
||||
export { JitsiMeetJS as default };
|
||||
|
||||
// XXX Re-export the types exported by JitsiMeetJS in order to prevent undefined
|
||||
// imported JitsiMeetJS. It may be caused by import cycles but I have not
|
||||
// confirmed the theory.
|
||||
// XXX Re-export the properties exported by JitsiMeetJS in order to prevent
|
||||
// undefined imported JitsiMeetJS. It may be caused by import cycles but I have
|
||||
// not confirmed the theory.
|
||||
export const analytics = JitsiMeetJS.analytics;
|
||||
export const browser = JitsiMeetJS.util.browser;
|
||||
export const JitsiConferenceErrors = JitsiMeetJS.errors.conference;
|
||||
export const JitsiConferenceEvents = JitsiMeetJS.events.conference;
|
||||
export const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
@@ -20,8 +22,6 @@ export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
|
||||
export const JitsiTrackErrors = JitsiMeetJS.errors.track;
|
||||
export const JitsiTrackEvents = JitsiMeetJS.events.track;
|
||||
|
||||
export const analytics = JitsiMeetJS.analytics;
|
||||
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './constants';
|
||||
|
||||