Compare commits
157 Commits
2997
...
node_10_bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e48ddc28eb | ||
|
|
71edea8aac | ||
|
|
2b1cb75e40 | ||
|
|
216782d606 | ||
|
|
c707b82419 | ||
|
|
3fdf944763 | ||
|
|
56100d0d5c | ||
|
|
486e8e35d9 | ||
|
|
554974a36d | ||
|
|
cd943319d6 | ||
|
|
837f496e8f | ||
|
|
c43f7c8979 | ||
|
|
c9c9f7eac0 | ||
|
|
5ccc397e47 | ||
|
|
00cd82d976 | ||
|
|
61deb74444 | ||
|
|
b30008e3a5 | ||
|
|
62b6737a3f | ||
|
|
cd77a9176c | ||
|
|
2a61968566 | ||
|
|
be4813e10d | ||
|
|
ae890dc093 | ||
|
|
9407f562f6 | ||
|
|
011a46ce2d | ||
|
|
8e0bd36ece | ||
|
|
6f8743af3a | ||
|
|
58d220d645 | ||
|
|
d3c5756f7a | ||
|
|
5ff1ce5a60 | ||
|
|
843f08f38e | ||
|
|
418575136f | ||
|
|
8c97ce2ee9 | ||
|
|
b2245729cc | ||
|
|
cc2b5a261b | ||
|
|
13c4ec884b | ||
|
|
7162080d00 | ||
|
|
b71adbdf70 | ||
|
|
2ae2f04f0a | ||
|
|
4424c456a9 | ||
|
|
cfa1e2f90d | ||
|
|
d290d28248 | ||
|
|
0474031a78 | ||
|
|
a57a5ca49d | ||
|
|
d8c1f107da | ||
|
|
9d27c36d80 | ||
|
|
057b300074 | ||
|
|
e164a23cf0 | ||
|
|
61456b0d99 | ||
|
|
df55448a2c | ||
|
|
6953569629 | ||
|
|
4d2614660c | ||
|
|
60f7ba7301 | ||
|
|
e5cc732b72 | ||
|
|
d604cdfe27 | ||
|
|
dc90800e50 | ||
|
|
6f17988d17 | ||
|
|
c54db8337d | ||
|
|
9a1e9fff98 | ||
|
|
a214be0dfe | ||
|
|
39a22effb1 | ||
|
|
ca600928f5 | ||
|
|
60decf7692 | ||
|
|
467452d110 | ||
|
|
af37141e3d | ||
|
|
ae7a882188 | ||
|
|
a49e590e7c | ||
|
|
1928efda11 | ||
|
|
57bf165ebd | ||
|
|
38517127c3 | ||
|
|
b7b43e8d9c | ||
|
|
8adc8a090a | ||
|
|
8c23d43a3a | ||
|
|
5773bc48ed | ||
|
|
9613755055 | ||
|
|
1fbc68d0cc | ||
|
|
2cbe7922f6 | ||
|
|
a712e26ee2 | ||
|
|
b673c4a11a | ||
|
|
8282873de5 | ||
|
|
2101f70a09 | ||
|
|
717fade79c | ||
|
|
959e687ed4 | ||
|
|
5f5adc3fa8 | ||
|
|
f317f993fd | ||
|
|
b2baab573e | ||
|
|
12ed711cce | ||
|
|
dfbd8d71ad | ||
|
|
d051d3450d | ||
|
|
7c88de20fe | ||
|
|
7b71482b03 | ||
|
|
2339f232a5 | ||
|
|
3bb3b4500d | ||
|
|
0fca0f392d | ||
|
|
c25d6eb9a8 | ||
|
|
37ff77cd5b | ||
|
|
fd30481ac2 | ||
|
|
2d87757aaa | ||
|
|
126e2d6e14 | ||
|
|
32fbcb17b9 | ||
|
|
d3bf0b7862 | ||
|
|
17f4b24a3f | ||
|
|
e63cd8c81b | ||
|
|
282e66b2dc | ||
|
|
72922130a2 | ||
|
|
514175b1af | ||
|
|
ceb8d7b03d | ||
|
|
22803f36e9 | ||
|
|
7674e90d4d | ||
|
|
1d128e027a | ||
|
|
ee9f304345 | ||
|
|
f148b50100 | ||
|
|
95785a9585 | ||
|
|
26d906fa46 | ||
|
|
eac069c930 | ||
|
|
5119f41af6 | ||
|
|
e2771b53bb | ||
|
|
008fb868a6 | ||
|
|
6dea107bcd | ||
|
|
d10d61fb7a | ||
|
|
9fe2b834eb | ||
|
|
a327a5d804 | ||
|
|
288bb59f71 | ||
|
|
f3d623e0ca | ||
|
|
388c906312 | ||
|
|
2043845d52 | ||
|
|
024671165a | ||
|
|
aba0912abf | ||
|
|
e446acb045 | ||
|
|
3927f29ba8 | ||
|
|
dafcde5060 | ||
|
|
3b754fa219 | ||
|
|
4283d8b342 | ||
|
|
31cc63b757 | ||
|
|
79bd5cce00 | ||
|
|
4fd8172126 | ||
|
|
fe7652ec90 | ||
|
|
72776e3a23 | ||
|
|
8addf0f436 | ||
|
|
73146e77cc | ||
|
|
28115b963d | ||
|
|
15819f7974 | ||
|
|
deb58798ba | ||
|
|
07ccb0a386 | ||
|
|
8d6e1b1872 | ||
|
|
80dadd0218 | ||
|
|
955e0a3382 | ||
|
|
1354731fc5 | ||
|
|
3ca704d81d | ||
|
|
3ad27961e5 | ||
|
|
86caf52d08 | ||
|
|
f2cb15ba44 | ||
|
|
d62974b433 | ||
|
|
8ff33684f7 | ||
|
|
b8179102c5 | ||
|
|
c23c798f7a | ||
|
|
3c27d2ee54 | ||
|
|
7267f386dc |
18
.flowconfig
@@ -38,7 +38,23 @@ node_modules/react-native/flow-github/
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip .ios suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
@@ -67,4 +83,4 @@ module.file_ext=.jsx
|
||||
module.file_ext=.json
|
||||
|
||||
[version]
|
||||
^0.67.0
|
||||
^0.78.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
osx_image: xcode9.4
|
||||
osx_image: xcode10
|
||||
language: objective-c
|
||||
script:
|
||||
- "./ios/travis-ci/build-ipa.sh"
|
||||
|
||||
@@ -130,7 +130,7 @@ 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).
|
||||
at [8x8](https://8x8.com).
|
||||
|
||||
## Mobile app
|
||||
Jitsi Meet is also available as a React Native app for Android and iOS.
|
||||
|
||||
@@ -126,6 +126,15 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const ignoredEvents
|
||||
= [ 'e2e_rtt', 'rtp.stats', 'rtt.by.region', 'available.device',
|
||||
'stream.switch.delay', 'ice.state.changed', 'ice.duration' ];
|
||||
|
||||
// Temporary removing some of the events that are too noisy.
|
||||
if (ignoredEvents.indexOf(event.action) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gaEvent = {
|
||||
'eventCategory': 'jitsi-meet',
|
||||
'eventAction': this._extractAction(event),
|
||||
|
||||
@@ -491,3 +491,29 @@ Picture-in-Picture style scenario, in a rectangle too small to accommodate its
|
||||
Jitsi Meet SDK automatically enables (unless explicitly disabled by a
|
||||
`setPictureInPictureEnabled(false)` call) Android's native Picture-in-Picture
|
||||
mode iff the platform is supported i.e. Android >= Oreo.
|
||||
|
||||
## Dropbox integration
|
||||
|
||||
To setup the Dropbox integration, follow these steps:
|
||||
|
||||
1. Add the following to the app's AndroidManifest.xml and change `<APP_KEY>` to
|
||||
your Dropbox app key:
|
||||
```
|
||||
<activity
|
||||
android:configChanges="keyboard|orientation"
|
||||
android:launchMode="singleTask"
|
||||
android:name="com.dropbox.core.android.AuthActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="db-<APP_KEY>" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
```
|
||||
|
||||
2. Add the following to the app's strings.xml and change `<APP_KEY>` to your
|
||||
Dropbox app key:
|
||||
```
|
||||
<string name="dropbox_app_key"><APP_KEY></string>
|
||||
```
|
||||
|
||||
@@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'org.jitsi.meet'
|
||||
@@ -27,9 +28,13 @@ android {
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-debug.pro'
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-release.pro'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +45,69 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
|
||||
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||
implementation 'com.google.android.gms:play-services-auth:15.0.0'
|
||||
|
||||
implementation project(':sdk')
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
|
||||
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
// Dropbox integration
|
||||
//
|
||||
|
||||
def plistParser = new XmlSlurper(
|
||||
/* validating */ false,
|
||||
/* namespaceAware */ false,
|
||||
/* allowDocTypeDeclaration */ true)
|
||||
plistParser.setFeature(
|
||||
'http://apache.org/xml/features/nonvalidating/load-external-dtd',
|
||||
false)
|
||||
def plist = plistParser.parse('../ios/app/src/Info.plist')
|
||||
def dropboxScheme = plist.dict.array.dict.array.string.find { string ->
|
||||
string.text().startsWith('db-')
|
||||
}
|
||||
def dropboxAppKey = dropboxScheme?.text() - 'db-'
|
||||
|
||||
if (dropboxAppKey) {
|
||||
android.defaultConfig.resValue('string', 'dropbox_app_key', "${dropboxAppKey}")
|
||||
|
||||
def dropboxActivity = """
|
||||
<activity
|
||||
android:configChanges="keyboard|orientation"
|
||||
android:launchMode="singleTask"
|
||||
android:name="com.dropbox.core.android.AuthActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="db-${dropboxAppKey}" />
|
||||
</intent-filter>
|
||||
</activity>""";
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
output.processManifest.doLast {
|
||||
def f = new File(manifestOutputDirectory, 'AndroidManifest.xml')
|
||||
if (!f.isFile()) {
|
||||
f = new File(new File(manifestOutputDirectory, output.dirName), 'AndroidManifest.xml')
|
||||
}
|
||||
if (f.exists()) {
|
||||
def charset = 'UTF-8'
|
||||
def s = f.getText(charset)
|
||||
s = s.replace('</application>', "${dropboxActivity}</application>")
|
||||
f.write(s, charset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.file('google-services.json').exists()) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
|
||||
5
android/app/proguard-rules-debug.pro
Normal file
@@ -0,0 +1,5 @@
|
||||
-include proguard-rules.pro
|
||||
|
||||
# Disabling obfuscation is useful if you collect stack traces from production crashes
|
||||
# (unless you are using a system that supports de-obfuscate the stack traces).
|
||||
-dontobfuscate
|
||||
6
android/app/proguard-rules-release.pro
Normal file
@@ -0,0 +1,6 @@
|
||||
-include proguard-rules.pro
|
||||
|
||||
# Crashlytics
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keep public class * extends java.lang.Exception
|
||||
23
android/app/proguard-rules.pro
vendored
@@ -16,10 +16,6 @@
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Disabling obfuscation is useful if you collect stack traces from production crashes
|
||||
# (unless you are using a system that supports de-obfuscate the stack traces).
|
||||
-dontobfuscate
|
||||
|
||||
# React Native
|
||||
|
||||
# Keep our interfaces so they can be used by other ProGuard rules.
|
||||
@@ -71,6 +67,23 @@
|
||||
|
||||
# FastImage
|
||||
|
||||
-keep public class com.dylanvann.fastimage.* {*;}
|
||||
-keep public class com.dylanvann.fastimage.** {*;}
|
||||
|
||||
# We added the following when we switched minifyEnabled on. Probably because we
|
||||
# ran the app and hit problems...
|
||||
|
||||
-keep class com.facebook.react.bridge.CatalystInstanceImpl { *; }
|
||||
-keep class com.facebook.react.bridge.ExecutorToken { *; }
|
||||
-keep class com.facebook.react.bridge.JavaScriptExecutor { *; }
|
||||
-keep class com.facebook.react.bridge.ModuleRegistryHolder { *; }
|
||||
-keep class com.facebook.react.bridge.ReadableType { *; }
|
||||
-keep class com.facebook.react.bridge.queue.NativeRunnable { *; }
|
||||
-keep class com.facebook.react.devsupport.** { *; }
|
||||
-keep class org.webrtc.** { *; }
|
||||
|
||||
-dontwarn com.facebook.react.devsupport.**
|
||||
-dontwarn com.google.appengine.**
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
-dontwarn javax.servlet.**
|
||||
|
||||
# ^^^ We added the above when we switched minifyEnabled on.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:name=".MainApplication"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||
|
||||
@@ -27,11 +27,9 @@ import org.jitsi.meet.sdk.invite.AddPeopleControllerListener;
|
||||
import org.jitsi.meet.sdk.invite.InviteController;
|
||||
import org.jitsi.meet.sdk.invite.InviteControllerListener;
|
||||
|
||||
import com.calendarevents.CalendarEventsPackage;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -229,20 +227,4 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
addPeopleController.endAddPeople();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode,
|
||||
String[] permissions,
|
||||
int[] grantResults) {
|
||||
CalendarEventsPackage.onRequestPermissionsResult(
|
||||
requestCode,
|
||||
permissions,
|
||||
grantResults);
|
||||
|
||||
super.onRequestPermissionsResult(
|
||||
requestCode,
|
||||
permissions,
|
||||
grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
/**
|
||||
* Simple {@link Application} for hooking up LeakCanary:
|
||||
* https://github.com/square/leakcanary
|
||||
*/
|
||||
public class MainApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
if (!LeakCanary.isInAnalyzerProcess(this)) {
|
||||
LeakCanary.install(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
classpath 'com.google.gms:google-services:3.2.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files.
|
||||
@@ -16,6 +17,7 @@ buildscript {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url "https://maven.google.com" }
|
||||
google()
|
||||
jcenter()
|
||||
maven { url "$rootDir/../node_modules/jsc-android/dist" }
|
||||
@@ -151,10 +153,11 @@ allprojects {
|
||||
}
|
||||
|
||||
ext {
|
||||
buildToolsVersion = "26.0.2"
|
||||
compileSdkVersion = 26
|
||||
buildToolsVersion = "27.0.3"
|
||||
compileSdkVersion = 27
|
||||
minSdkVersion = 21
|
||||
targetSdkVersion = 26
|
||||
supportLibVersion = "27.1.1"
|
||||
|
||||
// The Maven artifact groupdId of the third-party react-native modules which
|
||||
// Jitsi Meet SDK for Android depends on and which are not available in
|
||||
@@ -177,3 +180,8 @@ subprojects { subproject ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.4'
|
||||
distributionUrl = distributionUrl.replace("bin", "all")
|
||||
}
|
||||
|
||||
@@ -17,5 +17,4 @@
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useDeprecatedNdk=true
|
||||
version=1
|
||||
version=1
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
|
||||
110
android/gradlew
vendored
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,47 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
@@ -90,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -114,6 +113,7 @@ fi
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
@@ -154,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
14
android/gradlew.bat
vendored
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
@@ -19,132 +19,144 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
compile 'com.android.support:appcompat-v7:27.0.2'
|
||||
compile 'com.facebook.react:react-native:+'
|
||||
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
|
||||
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||
|
||||
compile project(':react-native-background-timer')
|
||||
compile project(':react-native-fast-image')
|
||||
compile project(':react-native-immersive')
|
||||
compile project(':react-native-keep-awake')
|
||||
compile project(':react-native-linear-gradient')
|
||||
compile project(':react-native-locale-detector')
|
||||
compile project(':react-native-sound')
|
||||
compile project(':react-native-vector-icons')
|
||||
compile project(':react-native-webrtc')
|
||||
compile project(':react-native-calendar-events')
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
|
||||
api 'com.facebook.react:react-native:+'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
// Build process helpers
|
||||
//
|
||||
|
||||
void runBefore(String dependentTaskName, Task task) {
|
||||
Task dependentTask = tasks.findByPath(dependentTaskName);
|
||||
if (dependentTask != null) {
|
||||
dependentTask.dependsOn task
|
||||
implementation project(':react-native-background-timer')
|
||||
implementation project(':react-native-calendar-events')
|
||||
implementation(project(':react-native-fast-image')) {
|
||||
exclude group: 'com.android.support'
|
||||
}
|
||||
implementation(project(":react-native-google-signin")) {
|
||||
exclude group: 'com.google.android.gms'
|
||||
exclude group: 'com.android.support'
|
||||
}
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
implementation project(':react-native-locale-detector')
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-webrtc')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
android.buildTypes.all { buildType ->
|
||||
def buildNameCapitalized = "${buildType.name.capitalize()}"
|
||||
def bundlePath = "${buildDir}/intermediates/bundles/${buildType.name}"
|
||||
|
||||
// Bundle fonts in react-native-vector-icons.
|
||||
// Here we bundle all assets, resources and React files. We cannot use the
|
||||
// react.gradle file provided by react-native because it's designed to be used
|
||||
// in an application (it taps into applicationVariants, but the SDK is a library
|
||||
// so we need libraryVariants instead).
|
||||
android.libraryVariants.all { def variant ->
|
||||
// Create variant and target names
|
||||
def targetName = variant.name.capitalize()
|
||||
def targetPath = variant.dirName
|
||||
|
||||
// React js bundle directories
|
||||
def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}")
|
||||
def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")
|
||||
|
||||
def jsBundleFile = file("$jsBundleDir/index.android.bundle")
|
||||
|
||||
def currentBundleTask = tasks.create(
|
||||
name: "bundle${targetName}JsAndAssets",
|
||||
type: Exec) {
|
||||
group = "react"
|
||||
description = "bundle JS and assets for ${targetName}."
|
||||
|
||||
// Create dirs if they are not there (e.g. the "clean" task just ran)
|
||||
doFirst {
|
||||
jsBundleDir.deleteDir()
|
||||
jsBundleDir.mkdirs()
|
||||
resourcesDir.deleteDir()
|
||||
resourcesDir.mkdirs()
|
||||
}
|
||||
|
||||
// Set up inputs and outputs so gradle can cache the result
|
||||
def reactRoot = file("${projectDir}/../../")
|
||||
inputs.files fileTree(dir: reactRoot, excludes: ["android/**", "ios/**"])
|
||||
outputs.dir jsBundleDir
|
||||
outputs.dir resourcesDir
|
||||
|
||||
// Set up the call to the react-native cli
|
||||
workingDir reactRoot
|
||||
|
||||
// Set up dev mode
|
||||
def devEnabled = !targetName.toLowerCase().contains("release")
|
||||
|
||||
// Run the bundler
|
||||
commandLine(
|
||||
"node",
|
||||
"node_modules/react-native/local-cli/cli.js",
|
||||
"bundle",
|
||||
"--platform", "android",
|
||||
"--dev", "${devEnabled}",
|
||||
"--reset-cache",
|
||||
"--entry-file", "index.android.js",
|
||||
"--bundle-output", jsBundleFile,
|
||||
"--assets-dest", resourcesDir)
|
||||
|
||||
// Disable bundling on dev builds
|
||||
enabled !devEnabled
|
||||
}
|
||||
|
||||
currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
|
||||
currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)
|
||||
|
||||
variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)
|
||||
variant.mergeResources.dependsOn(currentBundleTask)
|
||||
|
||||
def assetsDir = variant.mergeAssets.outputDir
|
||||
|
||||
variant.mergeAssets.doLast {
|
||||
// Bundle fonts
|
||||
//
|
||||
|
||||
def currentFontTask = tasks.create(
|
||||
name: "copy${buildNameCapitalized}Fonts",
|
||||
type: Copy) {
|
||||
copy {
|
||||
from("${projectDir}/../../fonts/jitsi.ttf")
|
||||
from("${projectDir}/../../node_modules/react-native-vector-icons/Fonts/")
|
||||
into("${bundlePath}/assets/fonts")
|
||||
into("${assetsDir}/fonts")
|
||||
}
|
||||
|
||||
currentFontTask.dependsOn("merge${buildNameCapitalized}Resources")
|
||||
currentFontTask.dependsOn("merge${buildNameCapitalized}Assets")
|
||||
|
||||
runBefore("processArmeabi-v7a${buildNameCapitalized}Resources", currentFontTask)
|
||||
runBefore("processX86${buildNameCapitalized}Resources", currentFontTask)
|
||||
runBefore("processUniversal${buildNameCapitalized}Resources", currentFontTask)
|
||||
runBefore("process${buildNameCapitalized}Resources", currentFontTask)
|
||||
|
||||
def currentSoundsTask = tasks.create(
|
||||
name: "copy${buildNameCapitalized}Sounds",
|
||||
type: Copy) {
|
||||
from("${projectDir}/../../sounds/joined.wav")
|
||||
from("${projectDir}/../../sounds/left.wav")
|
||||
from("${projectDir}/../../sounds/outgoingRinging.wav")
|
||||
from("${projectDir}/../../sounds/outgoingStart.wav")
|
||||
from("${projectDir}/../../sounds/recordingOn.mp3")
|
||||
from("${projectDir}/../../sounds/recordingOff.mp3")
|
||||
from("${projectDir}/../../sounds/rejected.wav")
|
||||
into("${bundlePath}/assets/sounds")
|
||||
}
|
||||
|
||||
currentSoundsTask.dependsOn("merge${buildNameCapitalized}Resources")
|
||||
currentSoundsTask.dependsOn("merge${buildNameCapitalized}Assets")
|
||||
|
||||
runBefore("processArmeabi-v7a${buildNameCapitalized}Resources", currentSoundsTask)
|
||||
runBefore("processX86${buildNameCapitalized}Resources", currentSoundsTask)
|
||||
runBefore("processUniversal${buildNameCapitalized}Resources", currentSoundsTask)
|
||||
runBefore("process${buildNameCapitalized}Resources", currentSoundsTask)
|
||||
|
||||
// Bundle JavaScript and React resources.
|
||||
// (adapted from react-native/react.gradle)
|
||||
// Bundle sounds
|
||||
//
|
||||
|
||||
// React JS bundle directories
|
||||
def jsBundleDir = file("${bundlePath}/assets")
|
||||
def resourcesDir = file("${bundlePath}/res/merged")
|
||||
def jsBundleFile = file("${jsBundleDir}/index.android.bundle")
|
||||
|
||||
// Bundle task name for variant.
|
||||
def bundleJsAndAssetsTaskName = "bundle${buildNameCapitalized}JsAndAssets"
|
||||
|
||||
def currentBundleTask = tasks.create(
|
||||
name: bundleJsAndAssetsTaskName,
|
||||
type: Exec) {
|
||||
// Set up inputs and outputs so gradle can cache the result.
|
||||
def reactRoot = file("${projectDir}/../../")
|
||||
inputs.files fileTree(dir: reactRoot, excludes: ['android/**', 'ios/**'])
|
||||
outputs.dir jsBundleDir
|
||||
outputs.dir resourcesDir
|
||||
|
||||
// Set up the call to the react-native cli.
|
||||
workingDir reactRoot
|
||||
|
||||
// Create JS bundle
|
||||
def devEnabled = !buildNameCapitalized.toLowerCase().contains('release')
|
||||
commandLine(
|
||||
'node',
|
||||
'node_modules/react-native/local-cli/cli.js',
|
||||
'bundle',
|
||||
'--assets-dest', resourcesDir,
|
||||
'--bundle-output', jsBundleFile,
|
||||
'--dev', "${devEnabled}",
|
||||
'--entry-file', 'index.android.js',
|
||||
'--platform', 'android',
|
||||
'--reset-cache')
|
||||
|
||||
// Disable bundling on dev builds
|
||||
enabled !devEnabled
|
||||
copy {
|
||||
from("${projectDir}/../../sounds/joined.wav")
|
||||
from("${projectDir}/../../sounds/left.wav")
|
||||
from("${projectDir}/../../sounds/outgoingRinging.wav")
|
||||
from("${projectDir}/../../sounds/outgoingStart.wav")
|
||||
from("${projectDir}/../../sounds/recordingOn.mp3")
|
||||
from("${projectDir}/../../sounds/recordingOff.mp3")
|
||||
from("${projectDir}/../../sounds/rejected.wav")
|
||||
into("${assetsDir}/sounds")
|
||||
}
|
||||
|
||||
// Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process
|
||||
currentBundleTask.dependsOn("merge${buildNameCapitalized}Resources")
|
||||
currentBundleTask.dependsOn("merge${buildNameCapitalized}Assets")
|
||||
// Copy React assets
|
||||
//
|
||||
if (currentBundleTask.enabled) {
|
||||
copy {
|
||||
from(jsBundleDir)
|
||||
into(assetsDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runBefore("processArmeabi-v7a${buildNameCapitalized}Resources", currentBundleTask)
|
||||
runBefore("processX86${buildNameCapitalized}Resources", currentBundleTask)
|
||||
runBefore("processUniversal${buildNameCapitalized}Resources", currentBundleTask)
|
||||
runBefore("process${buildNameCapitalized}Resources", currentBundleTask)
|
||||
variant.mergeResources.doLast {
|
||||
// Copy React resources
|
||||
//
|
||||
if (currentBundleTask.enabled) {
|
||||
copy {
|
||||
from(resourcesDir)
|
||||
into(variant.mergeResources.outputDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
aarArchive(MavenPublication) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.view.KeyEvent;
|
||||
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
@@ -42,7 +43,7 @@ import java.net.URL;
|
||||
* {@code JitsiMeetView} static methods.
|
||||
*/
|
||||
public class JitsiMeetActivity
|
||||
extends AppCompatActivity {
|
||||
extends AppCompatActivity implements JitsiMeetActivityInterface {
|
||||
|
||||
/**
|
||||
* The request code identifying requests for the permission to draw on top
|
||||
@@ -174,7 +175,12 @@ public class JitsiMeetActivity
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
initializeContentView();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ReactActivityLifecycleCallbacks.onActivityResult(
|
||||
this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -260,6 +266,15 @@ public class JitsiMeetActivity
|
||||
ReactActivityLifecycleCallbacks.onNewIntent(intent);
|
||||
}
|
||||
|
||||
// https://developer.android.com/reference/android/support/v4/app/ActivityCompat.OnRequestPermissionsResultCallback
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
final int requestCode,
|
||||
final String[] permissions,
|
||||
final int[] grantResults) {
|
||||
ReactActivityLifecycleCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
@@ -283,6 +298,14 @@ public class JitsiMeetActivity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the {@code PermissionAwareActivity} interface.
|
||||
*/
|
||||
@Override
|
||||
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
|
||||
ReactActivityLifecycleCallbacks.requestPermissions(this, permissions, requestCode, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see JitsiMeetView#setDefaultURL(URL)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
|
||||
import com.facebook.react.modules.core.PermissionAwareActivity;
|
||||
|
||||
/**
|
||||
* This interface serves as the umbrella interface that applications not using
|
||||
* {@code JitsiMeetActivity} must implement in order to ensure full
|
||||
* functionality.
|
||||
*/
|
||||
public interface JitsiMeetActivityInterface
|
||||
extends ActivityCompat.OnRequestPermissionsResultCallback,
|
||||
PermissionAwareActivity {
|
||||
}
|
||||
@@ -16,11 +16,16 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import com.calendarevents.CalendarEventsPackage;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
/**
|
||||
* Helper class to encapsulate the work which needs to be done on
|
||||
@@ -28,6 +33,37 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
* it.
|
||||
*/
|
||||
public class ReactActivityLifecycleCallbacks {
|
||||
|
||||
/**
|
||||
* {@link Activity} lifecycle method which should be called from
|
||||
* {@code Activity#onActivityResult} so we are notified about results of external intents
|
||||
* started/finished.
|
||||
*
|
||||
* @param activity {@code Activity} activity from where the result comes from.
|
||||
* @param requestCode {@code int} code of the request.
|
||||
* @param resultCode {@code int} code of the result.
|
||||
* @param data {@code Intent} the intent of the activity.
|
||||
*/
|
||||
public static void onActivityResult(
|
||||
Activity activity,
|
||||
int requestCode,
|
||||
int resultCode,
|
||||
Intent data) {
|
||||
ReactInstanceManager reactInstanceManager
|
||||
= ReactInstanceManagerHolder.getReactInstanceManager();
|
||||
|
||||
if (reactInstanceManager != null) {
|
||||
reactInstanceManager.onActivityResult(activity, requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed for making sure this class working with the "PermissionsAndroid"
|
||||
* React Native module.
|
||||
*/
|
||||
private static PermissionListener permissionListener;
|
||||
private static Callback permissionsCallback;
|
||||
|
||||
/**
|
||||
* {@link Activity} lifecycle method which should be called from
|
||||
* {@link Activity#onBackPressed} so we can do the required internal
|
||||
@@ -107,6 +143,11 @@ public class ReactActivityLifecycleCallbacks {
|
||||
if (reactInstanceManager != null) {
|
||||
reactInstanceManager.onHostResume(activity, defaultBackButtonImpl);
|
||||
}
|
||||
|
||||
if (permissionsCallback != null) {
|
||||
permissionsCallback.invoke();
|
||||
permissionsCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,4 +167,29 @@ public class ReactActivityLifecycleCallbacks {
|
||||
reactInstanceManager.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onRequestPermissionsResult(
|
||||
final int requestCode,
|
||||
final String[] permissions,
|
||||
final int[] grantResults) {
|
||||
CalendarEventsPackage.onRequestPermissionsResult(
|
||||
requestCode,
|
||||
permissions,
|
||||
grantResults);
|
||||
permissionsCallback = new Callback() {
|
||||
@Override
|
||||
public void invoke(Object... args) {
|
||||
if (permissionListener != null
|
||||
&& permissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
|
||||
permissionListener = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public static void requestPermissions(Activity activity, String[] permissions, int requestCode, PermissionListener listener) {
|
||||
permissionListener = listener;
|
||||
activity.requestPermissions(permissions, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class ReactInstanceManagerHolder {
|
||||
new PictureInPictureModule(reactContext),
|
||||
new ProximityModule(reactContext),
|
||||
new WiFiStatsModule(reactContext),
|
||||
new org.jitsi.meet.sdk.dropbox.Dropbox(reactContext),
|
||||
new org.jitsi.meet.sdk.invite.InviteModule(reactContext),
|
||||
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)
|
||||
);
|
||||
@@ -119,6 +120,7 @@ class ReactInstanceManagerHolder {
|
||||
.setApplication(application)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModulePath("index.android")
|
||||
.addPackage(new co.apptailor.googlesignin.RNGoogleSigninPackage())
|
||||
.addPackage(new com.BV.LinearGradient.LinearGradientPackage())
|
||||
.addPackage(new com.calendarevents.CalendarEventsPackage())
|
||||
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package org.jitsi.meet.sdk.dropbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.dropbox.core.DbxException;
|
||||
import com.dropbox.core.DbxRequestConfig;
|
||||
import com.dropbox.core.v2.DbxClientV2;
|
||||
import com.dropbox.core.v2.users.FullAccount;
|
||||
import com.dropbox.core.v2.users.SpaceAllocation;
|
||||
import com.dropbox.core.v2.users.SpaceUsage;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.dropbox.core.android.Auth;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implements the react-native module for the dropbox integration.
|
||||
*/
|
||||
public class Dropbox
|
||||
extends ReactContextBaseJavaModule
|
||||
implements LifecycleEventListener {
|
||||
private String appKey;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private final boolean isEnabled;
|
||||
|
||||
private Promise promise;
|
||||
|
||||
public Dropbox(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
appKey
|
||||
= reactContext.getString(
|
||||
org.jitsi.meet.sdk.R.string.dropbox_app_key);
|
||||
isEnabled = !TextUtils.isEmpty(appKey);
|
||||
|
||||
clientId = generateClientId();
|
||||
|
||||
reactContext.addLifecycleEventListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the dropbox auth flow.
|
||||
*
|
||||
* @param promise The promise used to return the result of the auth flow.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void authorize(final Promise promise) {
|
||||
if (isEnabled) {
|
||||
Auth.startOAuth2Authentication(this.getCurrentActivity(), appKey);
|
||||
this.promise = promise;
|
||||
} else {
|
||||
promise.reject(
|
||||
new Exception("Dropbox integration isn't configured."));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a client identifier for the dropbox sdk.
|
||||
*
|
||||
* @returns a client identifier for the dropbox sdk.
|
||||
* @see {https://dropbox.github.io/dropbox-sdk-java/api-docs/v3.0.x/com/dropbox/core/DbxRequestConfig.html#getClientIdentifier--}
|
||||
*/
|
||||
private String generateClientId() {
|
||||
Context context = getReactApplicationContext();
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
ApplicationInfo applicationInfo = null;
|
||||
PackageInfo packageInfo = null;
|
||||
|
||||
try {
|
||||
String packageName = context.getPackageName();
|
||||
|
||||
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
|
||||
packageInfo = packageManager.getPackageInfo(packageName, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
|
||||
String applicationLabel
|
||||
= applicationInfo == null
|
||||
? "JitsiMeet"
|
||||
: packageManager.getApplicationLabel(applicationInfo).toString()
|
||||
.replaceAll("\\s", "");
|
||||
String version = packageInfo == null ? "dev" : packageInfo.versionName;
|
||||
|
||||
return applicationLabel + "/" + version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
Map<String, Object> constants = new HashMap<>();
|
||||
|
||||
constants.put("ENABLED", isEnabled);
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the current user dropbox display name.
|
||||
*
|
||||
* @param token A dropbox access token.
|
||||
* @param promise The promise used to return the result of the auth flow.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void getDisplayName(final String token, final Promise promise) {
|
||||
DbxRequestConfig config = DbxRequestConfig.newBuilder(clientId).build();
|
||||
DbxClientV2 client = new DbxClientV2(config, token);
|
||||
|
||||
// Get current account info
|
||||
try {
|
||||
FullAccount account = client.users().getCurrentAccount();
|
||||
|
||||
promise.resolve(account.getName().getDisplayName());
|
||||
} catch (DbxException e) {
|
||||
promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Dropbox";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the current user space usage.
|
||||
*
|
||||
* @param token A dropbox access token.
|
||||
* @param promise The promise used to return the result of the auth flow.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void getSpaceUsage(final String token, final Promise promise) {
|
||||
DbxRequestConfig config = DbxRequestConfig.newBuilder(clientId).build();
|
||||
DbxClientV2 client = new DbxClientV2(config, token);
|
||||
|
||||
try {
|
||||
SpaceUsage spaceUsage = client.users().getSpaceUsage();
|
||||
WritableMap map = Arguments.createMap();
|
||||
|
||||
map.putString("used", String.valueOf(spaceUsage.getUsed()));
|
||||
|
||||
SpaceAllocation allocation = spaceUsage.getAllocation();
|
||||
long allocated = 0;
|
||||
|
||||
if (allocation.isIndividual()) {
|
||||
allocated += allocation.getIndividualValue().getAllocated();
|
||||
}
|
||||
if (allocation.isTeam()) {
|
||||
allocated += allocation.getTeamValue().getAllocated();
|
||||
}
|
||||
map.putString("allocated", String.valueOf(allocated));
|
||||
|
||||
promise.resolve(map);
|
||||
} catch (DbxException e) {
|
||||
promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {}
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
String token = Auth.getOAuth2Token();
|
||||
|
||||
if (token != null && this.promise != null) {
|
||||
this.promise.resolve(token);
|
||||
this.promise = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">Jitsi Meet SDK</string>
|
||||
<string name="dropbox_app_key"></string>
|
||||
</resources>
|
||||
|
||||
@@ -5,6 +5,8 @@ include ':react-native-background-timer'
|
||||
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
|
||||
include ':react-native-fast-image'
|
||||
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-signin/android')
|
||||
include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
|
||||
171
conference.js
@@ -11,6 +11,7 @@ import * as RemoteControlEvents
|
||||
from './service/remotecontrol/RemoteControlEvents';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import { createTaskQueue } from './modules/util/helpers';
|
||||
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import {
|
||||
@@ -30,14 +31,16 @@ import EventEmitter from 'events';
|
||||
import {
|
||||
AVATAR_ID_COMMAND,
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
authStatusChanged,
|
||||
commonUserJoinedHandling,
|
||||
commonUserLeftHandling,
|
||||
conferenceFailed,
|
||||
conferenceJoined,
|
||||
conferenceLeft,
|
||||
conferenceWillJoin,
|
||||
conferenceWillLeave,
|
||||
dataChannelOpened,
|
||||
EMAIL_COMMAND,
|
||||
lockStateChanged,
|
||||
onStartMutedPolicyChanged,
|
||||
p2pStatusChanged,
|
||||
@@ -74,14 +77,10 @@ import {
|
||||
getAvatarURLByParticipantId,
|
||||
getLocalParticipant,
|
||||
getParticipantById,
|
||||
hiddenParticipantJoined,
|
||||
hiddenParticipantLeft,
|
||||
localParticipantConnectionStatusChanged,
|
||||
localParticipantRoleChanged,
|
||||
MAX_DISPLAY_NAME_LENGTH,
|
||||
participantConnectionStatusChanged,
|
||||
participantJoined,
|
||||
participantLeft,
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantUpdated
|
||||
@@ -98,6 +97,7 @@ import {
|
||||
getLocationContextRoot,
|
||||
getJitsiMeetGlobalNS
|
||||
} from './react/features/base/util';
|
||||
import { addMessage } from './react/features/chat';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
import {
|
||||
@@ -274,6 +274,27 @@ function redirectToStaticPage(pathname) {
|
||||
windowLocation.pathname = newPathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* A queue for the async replaceLocalTrack action so that multiple audio
|
||||
* replacements cannot happen simultaneously. This solves the issue where
|
||||
* replaceLocalTrack is called multiple times with an oldTrack of null, causing
|
||||
* multiple local tracks of the same type to be used.
|
||||
*
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
const _replaceLocalAudioTrackQueue = createTaskQueue();
|
||||
|
||||
/**
|
||||
* A task queue for replacement local video tracks. This separate queue exists
|
||||
* so video replacement is not blocked by audio replacement tasks in the queue
|
||||
* {@link _replaceLocalAudioTrackQueue}.
|
||||
*
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
const _replaceLocalVideoTrackQueue = createTaskQueue();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -404,10 +425,16 @@ class ConferenceConnector {
|
||||
switch (err) {
|
||||
case JitsiConferenceErrors.CHAT_ERROR:
|
||||
logger.error('Chat error.', err);
|
||||
if (isButtonEnabled('chat')) {
|
||||
if (isButtonEnabled('chat') && !interfaceConfig.filmStripOnly) {
|
||||
const [ code, msg ] = params;
|
||||
|
||||
APP.UI.showChatError(code, msg);
|
||||
APP.store.dispatch(addMessage({
|
||||
hasRead: true,
|
||||
error: code,
|
||||
message: msg,
|
||||
timestamp: Date.now(),
|
||||
type: 'error'
|
||||
}));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -856,9 +883,6 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME it is possible to queue this task twice, but it's not causing
|
||||
// any issues. Specifically this can happen when the previous
|
||||
// get user media call is blocked on "ask user for permissions" dialog.
|
||||
if (!this.localVideo && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.UI.showCameraErrorNotification(error);
|
||||
@@ -1261,16 +1285,23 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream(newStream) {
|
||||
return APP.store.dispatch(
|
||||
replaceLocalTrack(this.localVideo, newStream, room))
|
||||
.then(() => {
|
||||
this.localVideo = newStream;
|
||||
this._setSharingScreen(newStream);
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setVideoMuteStatus(this.isLocalVideoMuted());
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localVideo, newStream, room))
|
||||
.then(() => {
|
||||
this.localVideo = newStream;
|
||||
this._setSharingScreen(newStream);
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setVideoMuteStatus(this.isLocalVideoMuted());
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.then(onFinish);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1300,15 +1331,22 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useAudioStream(newStream) {
|
||||
return APP.store.dispatch(
|
||||
replaceLocalTrack(this.localAudio, newStream, room))
|
||||
.then(() => {
|
||||
this.localAudio = newStream;
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setAudioMuteStatus(this.isLocalAudioMuted());
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalAudioTrackQueue.enqueue(onFinish => {
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localAudio, newStream, room))
|
||||
.then(() => {
|
||||
this.localAudio = newStream;
|
||||
if (newStream) {
|
||||
APP.UI.addLocalStream(newStream);
|
||||
}
|
||||
this.setAudioMuteStatus(this.isLocalAudioMuted());
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.then(onFinish);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1661,22 +1699,14 @@ export default {
|
||||
room.on(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
|
||||
user => APP.UI.onUserFeaturesChanged(user));
|
||||
room.on(JitsiConferenceEvents.USER_JOINED, (id, user) => {
|
||||
const displayName = user.getDisplayName();
|
||||
// The logic shared between RN and web.
|
||||
commonUserJoinedHandling(APP.store, room, user);
|
||||
|
||||
if (user.isHidden()) {
|
||||
APP.store.dispatch(hiddenParticipantJoined(id, displayName));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantJoined({
|
||||
botType: user.getBotType(),
|
||||
conference: room,
|
||||
id,
|
||||
name: displayName,
|
||||
presence: user.getStatus(),
|
||||
role: user.getRole()
|
||||
}));
|
||||
const displayName = user.getDisplayName();
|
||||
|
||||
logger.log(`USER ${id} connnected:`, user);
|
||||
APP.API.notifyUserJoined(id, {
|
||||
@@ -1691,13 +1721,13 @@ export default {
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.USER_LEFT, (id, user) => {
|
||||
if (user.isHidden()) {
|
||||
APP.store.dispatch(hiddenParticipantLeft(id));
|
||||
// The logic shared between RN and web.
|
||||
commonUserLeftHandling(APP.store, room, user);
|
||||
|
||||
if (user.isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantLeft(id, room));
|
||||
logger.log(`USER ${id} LEFT:`, user);
|
||||
APP.API.notifyUserLeft(id);
|
||||
APP.UI.messageHandler.participantNotification(
|
||||
@@ -1796,35 +1826,6 @@ export default {
|
||||
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
||||
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
if (isButtonEnabled('chat')) {
|
||||
room.on(
|
||||
JitsiConferenceEvents.MESSAGE_RECEIVED,
|
||||
(id, body, ts) => {
|
||||
let nick = getDisplayName(id);
|
||||
|
||||
if (!nick) {
|
||||
nick = `${
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME} (${
|
||||
id})`;
|
||||
}
|
||||
|
||||
APP.API.notifyReceivedChatMessage({
|
||||
id,
|
||||
nick,
|
||||
body,
|
||||
ts
|
||||
});
|
||||
APP.UI.addMessage(id, nick, body, ts);
|
||||
}
|
||||
);
|
||||
APP.UI.addListener(UIEvents.MESSAGE_CREATED, message => {
|
||||
APP.API.notifySendingChatMessage(message);
|
||||
room.sendTextMessage(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
room.on(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => {
|
||||
APP.store.dispatch(localParticipantConnectionStatusChanged(
|
||||
JitsiParticipantConnectionStatus.INTERRUPTED));
|
||||
@@ -2010,13 +2011,6 @@ export default {
|
||||
&& APP.UI.notifyInitiallyMuted();
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.AVAILABLE_DEVICES_CHANGED,
|
||||
(id, devices) => {
|
||||
APP.UI.updateDevicesAvailability(id, devices);
|
||||
}
|
||||
);
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.DATA_CHANNEL_OPENED, () => {
|
||||
APP.store.dispatch(dataChannelOpened());
|
||||
@@ -2375,11 +2369,24 @@ export default {
|
||||
createLocalTracksF,
|
||||
newDevices.videoinput,
|
||||
newDevices.audioinput)
|
||||
.then(tracks =>
|
||||
Promise.all(this._setLocalAudioVideoStreams(tracks)))
|
||||
.then(tracks => {
|
||||
// If audio or video muted before, or we unplugged current
|
||||
// device and selected new one, then mute new track.
|
||||
const muteSyncPromises = tracks.map(track => {
|
||||
if ((track.isVideoTrack() && videoWasMuted)
|
||||
|| (track.isAudioTrack() && audioWasMuted)) {
|
||||
return track.mute();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
return Promise.all(muteSyncPromises)
|
||||
.then(() => Promise.all(
|
||||
this._setLocalAudioVideoStreams(tracks)));
|
||||
})
|
||||
.then(() => {
|
||||
// If audio was muted before, or we unplugged current device
|
||||
// and selected new one, then mute new audio track.
|
||||
// Log and sync known mute state.
|
||||
if (audioWasMuted) {
|
||||
sendAnalytics(createTrackMutedEvent(
|
||||
'audio',
|
||||
@@ -2388,8 +2395,6 @@ export default {
|
||||
muteLocalAudio(true);
|
||||
}
|
||||
|
||||
// If video was muted before, or we unplugged current device
|
||||
// and selected new one, then mute new video track.
|
||||
if (!this.isSharingScreen && videoWasMuted) {
|
||||
sendAnalytics(createTrackMutedEvent(
|
||||
'video',
|
||||
|
||||
15
config.js
@@ -18,9 +18,6 @@ var config = {
|
||||
// XMPP domain.
|
||||
domain: 'jitsi-meet.example.com',
|
||||
|
||||
// XMPP MUC domain. FIXME: use XEP-0030 to discover it.
|
||||
muc: 'conference.jitsi-meet.example.com'
|
||||
|
||||
// When using authentication, domain for guest users.
|
||||
// anonymousdomain: 'guest.example.com',
|
||||
|
||||
@@ -35,6 +32,9 @@ var config = {
|
||||
|
||||
// Focus component domain. Defaults to focus.<domain>.
|
||||
// focus: 'focus.jitsi-meet.example.com',
|
||||
|
||||
// XMPP MUC domain. FIXME: use XEP-0030 to discover it.
|
||||
muc: 'conference.jitsi-meet.example.com'
|
||||
},
|
||||
|
||||
// BOSH URL. FIXME: use XEP-0156 to discover it.
|
||||
@@ -171,6 +171,10 @@ var config = {
|
||||
|
||||
// Whether to enable file recording or not.
|
||||
// fileRecordingsEnabled: false,
|
||||
// Enable the dropbox integration.
|
||||
// dropbox: {
|
||||
// appKey: '<APP_KEY>' // Specify your app key here.
|
||||
// },
|
||||
|
||||
// Whether to enable live streaming or not.
|
||||
// liveStreamingEnabled: false,
|
||||
@@ -237,10 +241,6 @@ var config = {
|
||||
// Disable hiding of remote thumbnails when in a 1-on-1 conference call.
|
||||
// disable1On1Mode: false,
|
||||
|
||||
// The minimum value a video's height (or width, whichever is smaller) needs
|
||||
// to be in order to be considered high-definition.
|
||||
minHDHeight: 540,
|
||||
|
||||
// Default language for the user interface.
|
||||
// defaultLanguage: 'en',
|
||||
|
||||
@@ -400,6 +400,7 @@ var config = {
|
||||
externalConnectUrl
|
||||
firefox_fake_device
|
||||
googleApiApplicationClientID
|
||||
googleApiIOSClientID
|
||||
iAmRecorder
|
||||
iAmSipGateway
|
||||
microsoftApiApplicationClientID
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Project animations
|
||||
**/
|
||||
|
||||
/**
|
||||
* Slide in animation for extended toolbar (inner) panel.
|
||||
*/
|
||||
|
||||
// FIX: Can't use percentage because of breaking animation when width is changed
|
||||
// (100% of 0 is also zero) Extracted this to config variable.
|
||||
@include keyframes(slideInExt) {
|
||||
from { left: -$sidebarWidth; }
|
||||
to { left: 0; }
|
||||
}
|
||||
|
||||
@include keyframes(slideOutExt) {
|
||||
from { left: 0; }
|
||||
to { left: -$sidebarWidth; }
|
||||
}
|
||||
@@ -10,3 +10,48 @@
|
||||
-ms-transform: translateX(0) translateY(100%) translateY(16px) !important;
|
||||
-webkit-transform: translateX(0) translateY(100%) translateY(16px) !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Welcome page tab color adjustments.
|
||||
*/
|
||||
.welcome {
|
||||
/**
|
||||
* The text color of the selected tab and hovered tabs.
|
||||
*/
|
||||
.bVobOt,
|
||||
.bVobOt:hover,
|
||||
.ebveIl:hover {
|
||||
color: #172B4D;
|
||||
}
|
||||
|
||||
/**
|
||||
* The color of the inactive tab text.
|
||||
*/
|
||||
.ebveIl {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* The color of the underline of a selected tab.
|
||||
*/
|
||||
.kByArU {
|
||||
background-color: #172B4D;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form {
|
||||
/**
|
||||
* Update the dropdown trigger wrapper to make sure it looks click-able.
|
||||
*/
|
||||
.gwEjuO {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override Atlaskit dropdown styling when in a modal because the dropdown
|
||||
* backgrounds clash with the modal backgrounds.
|
||||
*/
|
||||
.gBLqhw[data-role=droplistContent] {
|
||||
border: 1px solid #455166;
|
||||
}
|
||||
}
|
||||
|
||||
206
css/_chat.scss
@@ -1,21 +1,58 @@
|
||||
#sideToolbarContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
/**
|
||||
* Make the sidebar flush with the top of the toolbar. Take the size of
|
||||
* the toolbar and subtract from 100%.
|
||||
*/
|
||||
height: calc(100% - #{$newToolbarSizeWithPadding});
|
||||
left: -$sidebarWidth;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transition: left 0.5s;
|
||||
width: $sidebarWidth;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
/**
|
||||
* The sidebar (chat) is off-screen when hidden. Move it flush to the left
|
||||
* side of the window when it should be visible.
|
||||
*/
|
||||
&.slideInExt {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.sideToolbarContainer__inner {
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: $sidebarWidth;
|
||||
}
|
||||
}
|
||||
|
||||
#chat_container * {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
top: 15px;
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
font-size: 10pt;
|
||||
line-height: 20px;
|
||||
margin-top: 15px;
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
line-height: 20px;
|
||||
font-size: 10pt;
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
overflow: auto;
|
||||
width: $sidebarWidth;
|
||||
word-wrap: break-word;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: rgb(184, 184, 184);
|
||||
}
|
||||
@@ -55,41 +92,52 @@
|
||||
}
|
||||
}
|
||||
|
||||
#chat_container.is-conversation-mode #chatconversation {
|
||||
visibility: visible;
|
||||
.chat-close {
|
||||
background: gray;
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 100%;
|
||||
color: white;
|
||||
cursor:pointer;
|
||||
height: 10px;
|
||||
line-height: 10px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.localuser {
|
||||
color: #4C9AFF
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
color: red;
|
||||
#chat-input {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.remoteuser {
|
||||
color: #B8C7E0;
|
||||
}
|
||||
|
||||
.usrmsg-form {
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
visibility:hidden;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
width: 83%;
|
||||
height: 30px;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
color: white;
|
||||
font-size: 10pt;
|
||||
line-height: 30px;
|
||||
padding: 5px 5px 5px 0px;
|
||||
max-height:150px;
|
||||
min-height:35px;
|
||||
border: 0px none;
|
||||
color: white;
|
||||
box-shadow: none;
|
||||
border-radius:0;
|
||||
font-size: 10pt;
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
#usermsg:hover {
|
||||
@@ -97,10 +145,6 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#chat_container.is-conversation-mode #usermsg {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#nickname {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
@@ -112,20 +156,7 @@
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
#chat_container.is-conversation-mode #nickname {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#nickinput {
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
background: #3a3a3a;
|
||||
box-shadow: inset 0 0 3px 2px #a7a7a7;
|
||||
border: 1px solid #a7a7a7;
|
||||
color: #a7a7a7;
|
||||
}
|
||||
|
||||
#chat_container .username {
|
||||
#chat_container .display-name {
|
||||
float: left;
|
||||
padding-left: 5px;
|
||||
font-weight: bold;
|
||||
@@ -141,29 +172,45 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#chat_container .usermessage {
|
||||
.usermessage {
|
||||
padding-top: 20px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.chatArrow {
|
||||
position: absolute;
|
||||
height: 15px;
|
||||
left: 5px;
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $newToolbarBackgroundColor;;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
width: 93%;
|
||||
margin-left: 9px;
|
||||
margin-right: auto;
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0px;
|
||||
margin-top: 3px;
|
||||
left: 5px;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
padding-bottom: 3px;
|
||||
position: relative;
|
||||
|
||||
&.localuser .display-name {
|
||||
color: #4C9AFF
|
||||
}
|
||||
|
||||
&.error {
|
||||
.chatArrow,
|
||||
.timestamp,
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.usermessage {
|
||||
color: red;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.smiley {
|
||||
@@ -171,11 +218,9 @@
|
||||
}
|
||||
|
||||
#smileys {
|
||||
position: absolute;
|
||||
bottom: 7px;
|
||||
right: 5px;
|
||||
background: white;
|
||||
border-radius: 50px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 26px;
|
||||
margin: auto;
|
||||
cursor: pointer;
|
||||
@@ -187,33 +232,40 @@
|
||||
}
|
||||
|
||||
#smileysarea {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 17%;
|
||||
min-width: 31px;
|
||||
height: 40px;
|
||||
padding: 0px;
|
||||
max-height:150px;
|
||||
min-height:35px;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border: 0px none;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
height: 70px;
|
||||
max-height: 150px;
|
||||
min-height: 35px;
|
||||
min-width: 31px;
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
width: 17%;
|
||||
}
|
||||
|
||||
#chat_container.is-conversation-mode #smileysarea {
|
||||
visibility: visible;
|
||||
.smiley-input {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#smileysContainer {
|
||||
display: none;
|
||||
.smileys-panel {
|
||||
bottom: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border-bottom: 1px solid;
|
||||
border-top: 1px solid;
|
||||
width: 100%;
|
||||
bottom: 10%;
|
||||
transition: height 0.3s;
|
||||
width: $sidebarWidth;
|
||||
|
||||
&.show-smileys {
|
||||
height: 202px;
|
||||
}
|
||||
|
||||
#smileysContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border-bottom: 1px solid;
|
||||
border-top: 1px solid;
|
||||
}
|
||||
}
|
||||
|
||||
#smileysContainer .smiley {
|
||||
|
||||
117
css/_meetings_list.scss
Normal file
@@ -0,0 +1,117 @@
|
||||
.meetings-list {
|
||||
font-size: 14px;
|
||||
color: #253858;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.meetings-list-empty {
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
|
||||
.description {
|
||||
font-size: 16px;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #0074E0;
|
||||
border-radius: 4px;
|
||||
color: #FFFFFF;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: rgba(255,255,255,0.50);
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
margin-top: 5px;
|
||||
min-height: 92px;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
text-align: left;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 140px;
|
||||
flex-grow: 0;
|
||||
padding-left: 30px;
|
||||
padding-top: 25px;
|
||||
|
||||
.date {
|
||||
font-weight: bold;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding-left: 30px;
|
||||
padding-top: 25px;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 0;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
&.with-click-handler {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.with-click-handler:hover {
|
||||
background-color: #75A7E7;
|
||||
}
|
||||
|
||||
.add-button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.join-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .join-button {
|
||||
display: block
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,31 +13,68 @@
|
||||
float: left;
|
||||
}
|
||||
.navigate-section-list-tile {
|
||||
height: 90px;
|
||||
width: 260px;
|
||||
border-radius: 4px;
|
||||
background-color: #1754A9;
|
||||
margin-right: 8px;
|
||||
padding: 16px;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
margin-bottom: 8px;
|
||||
margin-right: 8px;
|
||||
min-height: 100px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
|
||||
&.with-click-handler {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.with-click-handler:hover {
|
||||
background-color: #1a5dbb;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.element-after {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.join-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .join-button {
|
||||
display: block
|
||||
}
|
||||
}
|
||||
.navigate-section-tile-body {
|
||||
@extend %navigate-section-list-tile-text;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
}
|
||||
.navigate-section-list-tile-info {
|
||||
flex: 1;
|
||||
word-break: break-word;
|
||||
}
|
||||
.navigate-section-tile-title {
|
||||
@extend %navigate-section-list-tile-text;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
}
|
||||
.navigate-section-section-header {
|
||||
@extend %navigate-section-list-text;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16px;
|
||||
display: block;
|
||||
}
|
||||
.navigate-section-list {
|
||||
position: relative;
|
||||
margin-top: 36px;
|
||||
margin-bottom: 36px;
|
||||
width: 100%;
|
||||
}
|
||||
.navigate-section-list-empty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,23 @@
|
||||
}
|
||||
|
||||
.recording-dialog {
|
||||
flex: 0;
|
||||
flex-direction: column;
|
||||
|
||||
.recording-header {
|
||||
display: flex;
|
||||
flex: 0;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.recording-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.authorization-panel {
|
||||
border-bottom: 2px solid rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
@@ -32,7 +47,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.logged-in-pannel {
|
||||
.logged-in-panel {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* Toolbar side panel main container element.
|
||||
*/
|
||||
#sideToolbarContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
/**
|
||||
* Make the sidebar flush with the top of the toolbar. Take the size of
|
||||
* the toolbar and subtract from 100%.
|
||||
*/
|
||||
height: calc(100% - #{$newToolbarSizeWithPadding});
|
||||
left: 0;
|
||||
max-width: $sidebarWidth;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
/**
|
||||
* Labels inside the side panel.
|
||||
*/
|
||||
label {
|
||||
color: $baseLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form elements and blocks.
|
||||
*/
|
||||
input,
|
||||
a,
|
||||
.sideToolbarBlock,
|
||||
.form-control {
|
||||
display: block;
|
||||
margin-top: 15px;
|
||||
margin-left: 10%;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify styling of elements inside a block.
|
||||
*/
|
||||
.sideToolbarBlock {
|
||||
input, a {
|
||||
margin-left: 0;
|
||||
margin-top: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner container, for example settings or profile.
|
||||
*/
|
||||
.sideToolbarContainer__inner {
|
||||
display: none;
|
||||
height: 100%;
|
||||
width: $sidebarWidth;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
|
||||
.input-control {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Titles and subtitles of inner containers.
|
||||
*/
|
||||
div.title {
|
||||
margin: 24px 0 11px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main title size.
|
||||
*/
|
||||
div.title {
|
||||
color: $toolbarTitleColor;
|
||||
text-align: center;
|
||||
font-size: $toolbarTitleFontSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* First element after a title.
|
||||
*/
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.side-toolbar-close {
|
||||
background: gray;
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 100%;
|
||||
color: white;
|
||||
cursor:pointer;
|
||||
height: 10px;
|
||||
line-height: 10px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
@@ -303,27 +303,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* START of slide in animation for extended toolbar panel.
|
||||
*/
|
||||
@include keyframes(slideInExt) {
|
||||
from { width: 0px; }
|
||||
to { width: $sidebarWidth; } // TO FIX: Make this value a percentage.
|
||||
}
|
||||
|
||||
.slideInExt {
|
||||
@include animation("slideInExt .5s forwards");
|
||||
}
|
||||
|
||||
@include keyframes(slideOutExt) {
|
||||
from { width: $sidebarWidth; } // TO FIX: Make this value a percentage.
|
||||
to { width: 0px; }
|
||||
}
|
||||
|
||||
.slideOutExt {
|
||||
@include animation("slideOutExt .5s forwards");
|
||||
}
|
||||
|
||||
/**
|
||||
* START of fade in animation for main toolbar
|
||||
*/
|
||||
|
||||
@@ -144,7 +144,7 @@ $watermarkHeight: 74px;
|
||||
/**
|
||||
* Welcome page variables.
|
||||
*/
|
||||
$welcomePageDescriptionColor: #E6EDFA;
|
||||
$welcomePageDescriptionColor: #fff;
|
||||
$welcomePageFontFamily: inherit;
|
||||
$welcomePageHeaderBackground: #1D69D4;
|
||||
$welcomePageHeaderBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
|
||||
$welcomePageTitleColor: #fff;
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
* wrapper needed before we're able to move all top toolbar indicators
|
||||
* creation to react.
|
||||
*/
|
||||
.ckAJgx {
|
||||
.sc-ifAKCX {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
@@ -557,30 +557,6 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.noMic {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
z-index: $zindex1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../images/noMic.png");
|
||||
background-color: #000;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.noVideo {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
z-index: $zindex1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../images/noVideo.png");
|
||||
background-color: #000;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.videoMessageFilter {
|
||||
-webkit-filter: grayscale(.5) opacity(0.8);
|
||||
filter: grayscale(.5) opacity(0.8);
|
||||
|
||||
@@ -4,7 +4,7 @@ body.welcome-page {
|
||||
}
|
||||
|
||||
.welcome {
|
||||
background-color: $welcomePageHeaderBackground;
|
||||
background-image: $welcomePageHeaderBackground;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: $welcomePageFontFamily;
|
||||
@@ -24,8 +24,8 @@ body.welcome-page {
|
||||
.header-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: $watermarkHeight + 80;
|
||||
margin-bottom: 36px;
|
||||
margin-top: $watermarkHeight + 35;
|
||||
margin-bottom: 35px;
|
||||
max-width: calc(100% - 40px);
|
||||
width: 650px;
|
||||
z-index: $zindex2;
|
||||
@@ -35,7 +35,6 @@ body.welcome-page {
|
||||
color: $welcomePageTitleColor;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
line-height: 1.18;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@@ -45,27 +44,123 @@ body.welcome-page {
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#enter_room {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: calc(100% - 40px);
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
width: 650px;
|
||||
width: 680px;
|
||||
z-index: $zindex2;
|
||||
background-color: #fff;
|
||||
padding: 25px 30px;
|
||||
|
||||
.enter-room-input {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
.enter-room-input-container {
|
||||
width: 100%;
|
||||
padding-right: 8px;
|
||||
padding-bottom: 5px;
|
||||
text-align: left;
|
||||
color: #253858;
|
||||
height: fit-content;
|
||||
border-width: 0px 0px 2px 0px;
|
||||
border-style: solid;
|
||||
border-image: linear-gradient(to right, #dee1e6, #fff) 1;
|
||||
|
||||
.enter-room-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.enter-room-input {
|
||||
border: none;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #253858;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
min-height: 354px;
|
||||
width: 710px;
|
||||
background: #75A7E7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tab-content{
|
||||
margin: 5px 0px;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
|
||||
> * {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-buttons {
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
flex-direction: row;
|
||||
min-height: 54px;
|
||||
width: 100%;
|
||||
|
||||
.tab {
|
||||
text-align: center;
|
||||
background: rgba(9,30,66,0.37);
|
||||
height: 55px;
|
||||
line-height: 54px;
|
||||
flex-grow: 1;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected, &:hover {
|
||||
background: rgba(9,30,66,0.71);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-page-button {
|
||||
font-size: 16px;
|
||||
width: 51px;
|
||||
height: 35px;
|
||||
font-size: 14px;
|
||||
background: #0074E0;
|
||||
border-radius: 4px;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 35px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.welcome-page-settings {
|
||||
color: $welcomePageDescriptionColor;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
right: 32px;
|
||||
z-index: $zindex2;
|
||||
|
||||
* {
|
||||
cursor: pointer;
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-watermark {
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
* see.
|
||||
*/
|
||||
.active-speaker {
|
||||
border: $thumbnailVideoBorder solid $videoThumbnailSelected;
|
||||
box-shadow: inset 0 0 3px $videoThumbnailSelected,
|
||||
0 0 3px $videoThumbnailSelected;
|
||||
box-shadow: 0 0 5px 3px $videoThumbnailSelected
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
@@ -62,9 +60,10 @@
|
||||
box-sizing: border-box;
|
||||
|
||||
/**
|
||||
* Allow scrolling of the thumbnails.
|
||||
* Allow vertical scrolling of the thumbnails.
|
||||
*/
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,6 +82,7 @@
|
||||
padding: 100px 0;
|
||||
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 5px;
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
/* Mixins END */
|
||||
|
||||
/* Animations BEGIN */
|
||||
|
||||
@import "animations";
|
||||
|
||||
/* Animations END */
|
||||
|
||||
/* Fonts BEGIN */
|
||||
@@ -56,7 +52,6 @@
|
||||
@import 'welcome_page';
|
||||
@import 'welcome_page_content';
|
||||
@import 'toolbars';
|
||||
@import 'side_toolbar_container';
|
||||
@import 'jquery.contextMenu';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'redirect_page';
|
||||
@@ -81,6 +76,7 @@
|
||||
@import 'modals/invite/add-people';
|
||||
@import 'deep-linking/main';
|
||||
@import 'transcription-subtitles';
|
||||
@import '_meetings_list.scss';
|
||||
@import 'navigate_section_list';
|
||||
@import 'third-party-branding/google';
|
||||
@import 'third-party-branding/microsoft';
|
||||
|
||||
@@ -154,14 +154,6 @@
|
||||
&-error {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override Atlaskit dropdown styling when in a modal because the dropdown
|
||||
* backgrounds clash with the modal backgrounds.
|
||||
*/
|
||||
.htclLc[data-role=droplistContent] {
|
||||
border: 1px solid #455166;
|
||||
}
|
||||
}
|
||||
.modal-dialog-footer {
|
||||
font-size: $modalButtonFontSize;
|
||||
|
||||
@@ -135,7 +135,8 @@ server {
|
||||
location / {
|
||||
ssi on;
|
||||
}
|
||||
# BOSH
|
||||
# BOSH, Bidirectional-streams Over Synchronous HTTP
|
||||
# https://en.wikipedia.org/wiki/BOSH_(protocol)
|
||||
location /http-bind {
|
||||
proxy_pass http://localhost:5280/http-bind;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
|
||||
28
doc/mobile-dropbox.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Setting up Dropbox integration
|
||||
1. Create a Dropbox app.
|
||||
2. Add the following to ```ios/app/src/Info.plist``` by replacing `<APP_KEY>`
|
||||
with your own Dropbox app key (which can be found in the
|
||||
[App Console](https://www.dropbox.com/developers/apps)):
|
||||
```
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>db-<APP_KEY></string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>dbapi-2</string>
|
||||
<string>dbapi-8-emm</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
**NOTE:** Both Android and iOS builds of the apps will parse the Dropbox app key
|
||||
from ```ios/app/src/Info.plist```.
|
||||
|
||||
**NOTE:** See [Dropbox developer guide](https://www.dropbox.com/developers/reference/developer-guide) for more information
|
||||
22
doc/mobile-google-auth.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Setting up Google Authentication
|
||||
|
||||
- Create a Firebase project here: https://firebase.google.com/. You'll need a
|
||||
signed Android build for that, that can be a debug self-signed build too, just
|
||||
retrieve the signing hash. The key hash of an already signed ap can be obtained
|
||||
as follows (on macOS): ```keytool -list -printcert -jarfile the-app.apk```
|
||||
- Place the generated ```google-services.json``` file in ```android/app```
|
||||
for Android and the ```GoogleService-Info.plist``` into ```ios/app/src``` for
|
||||
iOS (you can stop at that step, no need for the driver and the code changes they
|
||||
suggest in the wizard).
|
||||
- You may want to exclude these files in YOUR GIT config (do not exclude them in
|
||||
the ```.gitignore``` of the application itself!).
|
||||
- Your web client ID is auto generated during the Firebase project
|
||||
creation. Find them in the Google Developer console
|
||||
(https://console.developers.google.com/)
|
||||
- Make sure your config reflects this ID by setting
|
||||
```googleApiApplicationClientID``` in config.js.
|
||||
- Add your iOS client ID (the REVERSED_CLIENT_ID in the plist file) as an
|
||||
application URL schema into ```ios/app/src/Info.plist```
|
||||
(replacing placeholder).
|
||||
- Enable YouTube API access on the developer console (see above) to enable live
|
||||
streaming.
|
||||
@@ -4,6 +4,8 @@ This document describes the required steps for a quick Jitsi Meet installation o
|
||||
|
||||
Debian Wheezy and other older systems may require additional things to be done. Specifically for Wheezy, [libc needs to be updated](http://lists.jitsi.org/pipermail/users/2015-September/010064.html).
|
||||
|
||||
Also note that a recent default Ubuntu installation has only the `main` repository enabled, and Jitsi Meet needs packages from `universe`. Check your `/etc/apt/sources.list` file, and if `universe` is not present refer to [Ubuntu's documentation](https://help.ubuntu.com/community/Repositories/Ubuntu) on how to enable it. (Usually it amounts to copying the `main` lines and changing to `universe`.)
|
||||
|
||||
N.B.:
|
||||
|
||||
a.) All commands are supposed to be run by root. If you are logged in as a regular user with sudo rights, please prepend ___sudo___ to each of the commands.
|
||||
@@ -63,6 +65,15 @@ org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
|
||||
See [the documenation of ice4j](https://github.com/jitsi/ice4j/blob/master/doc/configuration.md)
|
||||
for details.
|
||||
|
||||
Default deployments on systems using systemd will have low default values for maximum processes and open files. If the used bridge will expect higher number of participants the default values need to be adjusted (the default values are good for less than 100 participants).
|
||||
To update the values edit `/etc/systemd/system.conf` and make sure you have the following values:
|
||||
```
|
||||
DefaultLimitNOFILE=65000
|
||||
DefaultLimitNPROC=65000
|
||||
DefaultTasksMax=65000
|
||||
```
|
||||
To load the values and check them look [here](#systemd-details) for details.
|
||||
|
||||
By default, anyone who has access to your jitsi instance will be able to start a conferencee: if your server is open to the world, anyone can have a chat with anyone else. If you want to limit the ability to start a conference to registered users, set up a "secure domain". Follow the instructions at https://github.com/jitsi/jicofo#secure-domain.
|
||||
|
||||
### Open a conference
|
||||
@@ -109,3 +120,12 @@ Sometimes the following packages will fail to uninstall properly:
|
||||
When this happens, just run the uninstall command a second time and it should be ok.
|
||||
|
||||
The reason for failure is that sometimes, the uninstall script is faster than the process that stops the daemons. The second run of the uninstall command fixes this, as by then the jigasi or jvb daemons are already stopped.
|
||||
|
||||
#### Systemd details
|
||||
To reload the systemd changes on a running system execute `systemctl daemon-reload` and `service jitsi-videobridge restart`.
|
||||
To check the tasks part execute `service jitsi-videobridge status` and you should see `Tasks: XX (limit: 65000)`.
|
||||
To check the files and process part execute ```cat /proc/`cat /var/run/jitsi-videobridge.pid`/limits``` and you should see:
|
||||
```
|
||||
Max processes 65000 65000 processes
|
||||
Max open files 65000 65000 files
|
||||
```
|
||||
|
||||
BIN
images/btn_google_signin_dark_normal.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 612 B |
|
Before Width: | Height: | Size: 889 B |
BIN
images/noMic.png
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -52,7 +52,7 @@ var interfaceConfig = {
|
||||
'tileview'
|
||||
],
|
||||
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile' ],
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
|
||||
|
||||
// Determines how the video would fit the screen. 'both' would fit the whole
|
||||
// screen, 'height' would fit the original video height to the height of the
|
||||
|
||||
@@ -26,6 +26,8 @@ target 'JitsiMeet' do
|
||||
pod 'Folly',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
||||
|
||||
pod 'ObjectiveDropboxOfficial'
|
||||
|
||||
pod 'react-native-background-timer',
|
||||
:path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-fast-image',
|
||||
@@ -35,8 +37,8 @@ target 'JitsiMeet' do
|
||||
pod 'react-native-locale-detector',
|
||||
:path => '../node_modules/react-native-locale-detector'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'ReactNativePermissions',
|
||||
:path => '../node_modules/react-native-permissions'
|
||||
pod 'RNGoogleSignin',
|
||||
:path => '../node_modules/react-native-google-signin'
|
||||
pod 'RNSound', :path => '../node_modules/react-native-sound'
|
||||
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
|
||||
pod 'react-native-calendar-events',
|
||||
|
||||
115
ios/Podfile.lock
@@ -1,17 +1,38 @@
|
||||
PODS:
|
||||
- boost-for-react-native (1.63.0)
|
||||
- DoubleConversion (1.1.5)
|
||||
- DoubleConversion (1.1.6)
|
||||
- FLAnimatedImage (1.0.12)
|
||||
- Folly (2016.09.26.00):
|
||||
- Folly (2016.10.31.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- glog (0.3.4)
|
||||
- React (0.55.4):
|
||||
- React/Core (= 0.55.4)
|
||||
- glog (0.3.5)
|
||||
- GoogleSignIn (4.3.0):
|
||||
- "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)"
|
||||
- "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)"
|
||||
- GTMOAuth2 (~> 1.0)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
- GoogleToolboxForMac/DebugUtils (2.1.4):
|
||||
- GoogleToolboxForMac/Defines (= 2.1.4)
|
||||
- GoogleToolboxForMac/Defines (2.1.4)
|
||||
- "GoogleToolboxForMac/NSDictionary+URLArguments (2.1.4)":
|
||||
- GoogleToolboxForMac/DebugUtils (= 2.1.4)
|
||||
- GoogleToolboxForMac/Defines (= 2.1.4)
|
||||
- "GoogleToolboxForMac/NSString+URLArguments (= 2.1.4)"
|
||||
- "GoogleToolboxForMac/NSString+URLArguments (2.1.4)"
|
||||
- GTMOAuth2 (1.1.6):
|
||||
- GTMSessionFetcher (~> 1.1)
|
||||
- GTMSessionFetcher (1.2.0):
|
||||
- GTMSessionFetcher/Full (= 1.2.0)
|
||||
- GTMSessionFetcher/Core (1.2.0)
|
||||
- GTMSessionFetcher/Full (1.2.0):
|
||||
- GTMSessionFetcher/Core (= 1.2.0)
|
||||
- ObjectiveDropboxOfficial (3.9.2)
|
||||
- React (0.57.1):
|
||||
- React/Core (= 0.57.1)
|
||||
- react-native-background-timer (2.0.0):
|
||||
- React
|
||||
- react-native-calendar-events (1.6.1):
|
||||
- react-native-calendar-events (1.6.2):
|
||||
- React
|
||||
- react-native-fast-image (4.0.14):
|
||||
- FLAnimatedImage
|
||||
@@ -22,66 +43,68 @@ PODS:
|
||||
- React
|
||||
- react-native-locale-detector (1.0.0):
|
||||
- React
|
||||
- react-native-webrtc (1.58.2):
|
||||
- react-native-webrtc (1.67.1):
|
||||
- React
|
||||
- React/Core (0.55.4):
|
||||
- yoga (= 0.55.4.React)
|
||||
- React/CxxBridge (0.55.4):
|
||||
- Folly (= 2016.09.26.00)
|
||||
- React/Core (0.57.1):
|
||||
- yoga (= 0.57.1.React)
|
||||
- React/CxxBridge (0.57.1):
|
||||
- Folly (= 2016.10.31.00)
|
||||
- React/Core
|
||||
- React/cxxreact
|
||||
- React/cxxreact (0.55.4):
|
||||
- React/cxxreact (0.57.1):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- Folly (= 2016.09.26.00)
|
||||
- Folly (= 2016.10.31.00)
|
||||
- React/jschelpers
|
||||
- React/jsinspector
|
||||
- React/DevSupport (0.55.4):
|
||||
- React/DevSupport (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTWebSocket
|
||||
- React/fishhook (0.55.4)
|
||||
- React/jschelpers (0.55.4):
|
||||
- Folly (= 2016.09.26.00)
|
||||
- React/fishhook (0.57.1)
|
||||
- React/jschelpers (0.57.1):
|
||||
- Folly (= 2016.10.31.00)
|
||||
- React/PrivateDatabase
|
||||
- React/jsinspector (0.55.4)
|
||||
- React/PrivateDatabase (0.55.4)
|
||||
- React/RCTActionSheet (0.55.4):
|
||||
- React/jsinspector (0.57.1)
|
||||
- React/PrivateDatabase (0.57.1)
|
||||
- React/RCTActionSheet (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTAnimation (0.55.4):
|
||||
- React/RCTAnimation (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTBlob (0.55.4):
|
||||
- React/RCTBlob (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTImage (0.55.4):
|
||||
- React/RCTImage (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTNetwork
|
||||
- React/RCTLinkingIOS (0.55.4):
|
||||
- React/RCTLinkingIOS (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTNetwork (0.55.4):
|
||||
- React/RCTNetwork (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTText (0.55.4):
|
||||
- React/RCTText (0.57.1):
|
||||
- React/Core
|
||||
- React/RCTWebSocket (0.55.4):
|
||||
- React/RCTWebSocket (0.57.1):
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- ReactNativePermissions (1.1.1):
|
||||
- RNGoogleSignin (1.0.0-rc6):
|
||||
- GoogleSignIn
|
||||
- React
|
||||
- RNSound (0.10.9):
|
||||
- React/Core
|
||||
- RNSound/Core (= 0.10.9)
|
||||
- RNSound/Core (0.10.9):
|
||||
- React/Core
|
||||
- RNVectorIcons (4.4.2):
|
||||
- RNVectorIcons (6.0.2):
|
||||
- React
|
||||
- SDWebImage/Core (4.4.2)
|
||||
- SDWebImage/GIF (4.4.2):
|
||||
- FLAnimatedImage (~> 1.0)
|
||||
- SDWebImage/Core
|
||||
- yoga (0.55.4.React)
|
||||
- yoga (0.57.1.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- ObjectiveDropboxOfficial
|
||||
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
|
||||
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
|
||||
- react-native-fast-image (from `../node_modules/react-native-fast-image`)
|
||||
@@ -98,7 +121,7 @@ DEPENDENCIES:
|
||||
- React/RCTNetwork (from `../node_modules/react-native`)
|
||||
- React/RCTText (from `../node_modules/react-native`)
|
||||
- React/RCTWebSocket (from `../node_modules/react-native`)
|
||||
- ReactNativePermissions (from `../node_modules/react-native-permissions`)
|
||||
- RNGoogleSignin (from `../node_modules/react-native-google-signin`)
|
||||
- RNSound (from `../node_modules/react-native-sound`)
|
||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
@@ -107,6 +130,11 @@ SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- boost-for-react-native
|
||||
- FLAnimatedImage
|
||||
- GoogleSignIn
|
||||
- GoogleToolboxForMac
|
||||
- GTMOAuth2
|
||||
- GTMSessionFetcher
|
||||
- ObjectiveDropboxOfficial
|
||||
- SDWebImage
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -130,8 +158,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-locale-detector"
|
||||
react-native-webrtc:
|
||||
:path: "../node_modules/react-native-webrtc"
|
||||
ReactNativePermissions:
|
||||
:path: "../node_modules/react-native-permissions"
|
||||
RNGoogleSignin:
|
||||
:path: "../node_modules/react-native-google-signin"
|
||||
RNSound:
|
||||
:path: "../node_modules/react-native-sound"
|
||||
RNVectorIcons:
|
||||
@@ -141,23 +169,28 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
DoubleConversion: e22e0762848812a87afd67ffda3998d9ef29170c
|
||||
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
|
||||
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
|
||||
Folly: 211775e49d8da0ca658aebc8eab89d642935755c
|
||||
glog: 1de0bb937dccdc981596d3b5825ebfb765017ded
|
||||
React: aa2040dbb6f317b95314968021bd2888816e03d5
|
||||
Folly: c89ac2d5c6ab169cd7397ef27485c44f35f742c7
|
||||
glog: e8acf0ebbf99759d3ff18c86c292a5898282dcde
|
||||
GoogleSignIn: 11183592dc63e105475c7305a325045ff95e02b7
|
||||
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
|
||||
GTMOAuth2: c77fe325e4acd453837e72d91e3b5f13116857b2
|
||||
GTMSessionFetcher: 0c4baf0a73acd0041bf9f71ea018deedab5ea84e
|
||||
ObjectiveDropboxOfficial: aa792e0556ceb7b72955fa29a2709072f6e35fd9
|
||||
React: 1fe0eb13d90b625d94c3b117c274dcfd2e760e11
|
||||
react-native-background-timer: 63dcbf37dbcf294b5c6c071afcdc661fa06a7594
|
||||
react-native-calendar-events: fe6fbc8ed337a7423c98f2c9012b25f20444de09
|
||||
react-native-fast-image: cba3d9bf9c2cf8ddb643d887a686c53a5dd90a2c
|
||||
react-native-keep-awake: 0de4bd66de0c23178107dce0c2fcc3354b2a8e94
|
||||
react-native-locale-detector: d1b2c6fe5abb56e3a1efb6c2d6f308c05c4251f1
|
||||
react-native-webrtc: 31b6d3f1e3e2ce373aa43fd682b04367250f807d
|
||||
ReactNativePermissions: 9f2d9c45c98800795e6c2ed330e25d11a66a8169
|
||||
RNGoogleSignin: 44debd8c359a662c0e2d585952e88b985bf78008
|
||||
RNSound: b360b3862d3118ed1c74bb9825696b5957686ac4
|
||||
RNVectorIcons: c0dbfbf6068fefa240c37b0f71bd03b45dddac44
|
||||
RNVectorIcons: 8c52e1e8da1153613fdef44748e865c25556cb9c
|
||||
SDWebImage: 624d6e296c69b244bcede364c72ae0430ac14681
|
||||
yoga: a23273df0088bf7f2bb7e5d7b00044ea57a2a54a
|
||||
yoga: b1ce48b6cf950b98deae82838f5173ea7cf89e85
|
||||
|
||||
PODFILE CHECKSUM: 1d5c8382f73d9540fac68d93b32e1d3b58d069ee
|
||||
PODFILE CHECKSUM: cf8276ba4b0933b24c6082a25a5f4eabe0ba4ea6
|
||||
|
||||
COCOAPODS: 1.5.3
|
||||
|
||||
@@ -125,7 +125,23 @@ continueUserActivity:(NSUserActivity *)userActivity
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
```
|
||||
|
||||
And also one of the following:
|
||||
|
||||
```objc
|
||||
// See https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623073-application?language=objc
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [JitsiMeetView application:app
|
||||
openURL:url
|
||||
options: options];
|
||||
}
|
||||
```
|
||||
or
|
||||
```objc
|
||||
// See https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application?language=objc
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
@@ -138,6 +154,8 @@ continueUserActivity:(NSUserActivity *)userActivity
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: The latter is deprecated.
|
||||
|
||||
### JitsiMeetViewDelegate
|
||||
|
||||
This delegate is optional, and can be set on the `JitsiMeetView` instance using
|
||||
@@ -212,3 +230,39 @@ resize `JitsiMeetView`.
|
||||
If `pictureInPictureEnabled` is set to `YES` or `delegate` implements
|
||||
`enterPictureInPicture:`, the in-call toolbar will render a button to afford the
|
||||
user to request entering Picture-in-Picture.
|
||||
|
||||
## Dropbox integration
|
||||
|
||||
To setup the Dropbox integration, follow these steps:
|
||||
|
||||
1. Add the following to the app's Info.plist and change `<APP_KEY>` to your
|
||||
Dropbox app key:
|
||||
```
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>db-<APP_KEY></string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>dbapi-2</string>
|
||||
<string>dbapi-8-emm</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
2. Add the following to the app's `AppDelegate`:
|
||||
```objc
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [JitsiMeetView application:app
|
||||
openURL:url
|
||||
options:options];
|
||||
}
|
||||
```
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0920;
|
||||
LastUpgradeCheck = 1000;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
@@ -327,12 +327,14 @@
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
@@ -383,12 +385,14 @@
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0920"
|
||||
LastUpgradeVersion = "1000"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "NO"
|
||||
@@ -40,7 +40,6 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
@@ -60,7 +59,6 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -36,14 +36,13 @@
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
return [JitsiMeetView application:application
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [JitsiMeetView application:app
|
||||
openURL:url
|
||||
sourceApplication:sourceApplication
|
||||
annotation:annotation];
|
||||
options:options];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,16 @@
|
||||
<string>org.jitsi.meet</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.googleusercontent.apps</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.YOUR_ID_HERE</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
# This script is executed from Xcode to start the React packager for Debug
|
||||
# targets.
|
||||
|
||||
export RCT_METRO_PORT="${RCT_METRO_PORT:=8081}"
|
||||
echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" > "${SRCROOT}/../../node_modules/react-native/scripts/.packager.env"
|
||||
|
||||
if [[ "$CONFIGURATION" = "Debug" ]]; then
|
||||
if nc -w 5 -z localhost 8081 ; then
|
||||
if ! curl -s "http://localhost:8081/status" | grep -q "packager-status:running" ; then
|
||||
echo "Port 8081 already in use, packager is either not running or not running correctly"
|
||||
if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
|
||||
if ! curl -s "http://localhost:${RCT_METRO_PORT}/status" | grep -q "packager-status:running" ; then
|
||||
echo "Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly"
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
open -g "$SRCROOT/../../node_modules/react-native/scripts/launchPackager.command" || echo "Can't start packager automatically"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
0B49424520AD8DBD00BD2DE0 /* outgoingStart.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */; };
|
||||
0B49424620AD8DBD00BD2DE0 /* outgoingRinging.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */; };
|
||||
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 */; };
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */; };
|
||||
@@ -34,6 +33,7 @@
|
||||
75635B0A20751D6D00F29C9F /* joined.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0820751D6D00F29C9F /* joined.wav */; };
|
||||
75635B0B20751D6D00F29C9F /* left.wav in Resources */ = {isa = PBXBuildFile; fileRef = 75635B0920751D6D00F29C9F /* left.wav */; };
|
||||
A4414AE020B37F1A003546E6 /* rejected.wav in Resources */ = {isa = PBXBuildFile; fileRef = A4414ADF20B37F1A003546E6 /* rejected.wav */; };
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
|
||||
B386B85720981A75000DEF7A /* InviteController.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85020981A74000DEF7A /* InviteController.m */; };
|
||||
B386B85820981A75000DEF7A /* AddPeopleController.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85120981A74000DEF7A /* AddPeopleController.m */; };
|
||||
B386B85920981A75000DEF7A /* AddPeopleControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B386B85220981A74000DEF7A /* AddPeopleControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -87,6 +87,8 @@
|
||||
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
A4414ADF20B37F1A003546E6 /* rejected.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = rejected.wav; path = ../../sounds/rejected.wav; sourceTree = "<group>"; };
|
||||
A4A934E8212F3ADB001E9388 /* Dropbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Dropbox.m; sourceTree = "<group>"; };
|
||||
A4A934EB21349A06001E9388 /* Dropbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dropbox.h; sourceTree = "<group>"; };
|
||||
B386B85020981A74000DEF7A /* InviteController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InviteController.m; sourceTree = "<group>"; };
|
||||
B386B85120981A74000DEF7A /* AddPeopleController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddPeopleController.m; sourceTree = "<group>"; };
|
||||
B386B85220981A74000DEF7A /* AddPeopleControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddPeopleControllerDelegate.h; sourceTree = "<group>"; };
|
||||
@@ -111,7 +113,6 @@
|
||||
files = (
|
||||
0BB9AD791F5EC6D7001C08DB /* Intents.framework in Frameworks */,
|
||||
0BB9AD771F5EC6CE001C08DB /* CallKit.framework in Frameworks */,
|
||||
0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */,
|
||||
0F65EECE1D95DA94561BB47E /* libPods-JitsiMeet.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -122,9 +123,6 @@
|
||||
0BCA49681EC4BBE500B793EE /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6C31EDC720C06D490089C899 /* recordingOn.mp3 */,
|
||||
6C31EDC920C06D530089C899 /* recordingOff.mp3 */,
|
||||
A4414ADF20B37F1A003546E6 /* rejected.wav */,
|
||||
0BC4B8681F8C01E100CE8B21 /* CallKitIcon.png */,
|
||||
C6245F5B2053091D0040BE68 /* image-resize@2x.png */,
|
||||
C6245F5C2053091D0040BE68 /* image-resize@3x.png */,
|
||||
@@ -133,6 +131,9 @@
|
||||
75635B0920751D6D00F29C9F /* left.wav */,
|
||||
0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */,
|
||||
0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */,
|
||||
6C31EDC920C06D530089C899 /* recordingOff.mp3 */,
|
||||
6C31EDC720C06D490089C899 /* recordingOn.mp3 */,
|
||||
A4414ADF20B37F1A003546E6 /* rejected.wav */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
@@ -162,6 +163,7 @@
|
||||
0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
|
||||
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
|
||||
C69EFA02209A0EFD0027712B /* callkit */,
|
||||
A4A934E7212F3AB8001E9388 /* dropbox */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
B386B84F20981A11000DEF7A /* invite */,
|
||||
@@ -193,6 +195,15 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A4A934E7212F3AB8001E9388 /* dropbox */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A4A934EB21349A06001E9388 /* Dropbox.h */,
|
||||
A4A934E8212F3ADB001E9388 /* Dropbox.m */,
|
||||
);
|
||||
path = dropbox;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B386B84F20981A11000DEF7A /* invite */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -272,6 +283,7 @@
|
||||
0BD906E31EC0C00300C8C18E /* Resources */,
|
||||
0BCA49651EC4B77500B793EE /* Package React bundle */,
|
||||
C7BC10B338C94EEB98048E64 /* [CP] Copy Pods Resources */,
|
||||
0B64F7BE2175DFEA005009CD /* Remove unneeded fonts */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -288,7 +300,7 @@
|
||||
0BD906DC1EC0C00300C8C18E /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0920;
|
||||
LastUpgradeCheck = 1000;
|
||||
ORGANIZATIONNAME = Jitsi;
|
||||
TargetAttributes = {
|
||||
0BD906E41EC0C00300C8C18E = {
|
||||
@@ -337,6 +349,24 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0B64F7BE2175DFEA005009CD /* Remove unneeded fonts */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Remove unneeded fonts";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# We need to manually do this because react-native-vecotr-icons lists fonts as resources in the Pod spec file\n# so they are automatically added.\n\nshopt -s extglob\n\nrm -f ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/!(jitsi).ttf\n";
|
||||
};
|
||||
0BCA49651EC4B77500B793EE /* Package React bundle */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -376,10 +406,16 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh",
|
||||
"${PODS_ROOT}/GTMOAuth2/Source/Touch/GTMOAuth2ViewTouch.xib",
|
||||
"${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
||||
@@ -390,10 +426,16 @@
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMOAuth2ViewTouch.nib",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
||||
"${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}/FontAwesome5_Brands.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.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",
|
||||
@@ -429,6 +471,7 @@
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
|
||||
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
|
||||
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */,
|
||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
|
||||
@@ -452,6 +495,7 @@
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
@@ -459,6 +503,7 @@
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
@@ -511,6 +556,7 @@
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
@@ -518,6 +564,7 @@
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
@@ -543,6 +590,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0920"
|
||||
LastUpgradeVersion = "1000"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,7 +26,6 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
@@ -37,7 +36,6 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
<string>1.9.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>dbapi-2</string>
|
||||
<string>dbapi-8-emm</string>
|
||||
</array>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
<key>JitsiMeetFonts</key>
|
||||
<array>
|
||||
<string>FontAwesome.ttf</string>
|
||||
<string>jitsi.ttf</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -39,10 +39,14 @@
|
||||
continueUserActivity:(NSUserActivity * _Nonnull)userActivity
|
||||
restorationHandler:(void (^ _Nullable)(NSArray * _Nullable))restorationHandler;
|
||||
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
|
||||
+ (BOOL)application:(UIApplication * _Nonnull)application
|
||||
openURL:(NSURL * _Nonnull)URL
|
||||
sourceApplication:(NSString * _Nullable)sourceApplication
|
||||
annotation:(id _Nullable)annotation;
|
||||
annotation:(id _Nullable)annotation __deprecated;
|
||||
|
||||
- (void)loadURL:(NSURL * _Nullable)url;
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
#import <Intents/Intents.h>
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
@@ -23,6 +22,7 @@
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import "Dropbox.h"
|
||||
#import "Invite+Private.h"
|
||||
#import "InviteController+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
@@ -52,35 +52,6 @@ RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to dynamically load custom fonts. The `UIAppFonts` key in the
|
||||
* plist file doesn't work for frameworks, so fonts have to be manually loaded.
|
||||
*/
|
||||
void loadCustomFonts(Class clazz) {
|
||||
NSBundle *bundle = [NSBundle bundleForClass:clazz];
|
||||
NSArray *fonts = [bundle objectForInfoDictionaryKey:@"JitsiMeetFonts"];
|
||||
|
||||
for (NSString *item in fonts) {
|
||||
NSString *fontName = [item stringByDeletingPathExtension];
|
||||
NSString *fontExt = [item pathExtension];
|
||||
NSString *fontPath = [bundle pathForResource:fontName ofType:fontExt];
|
||||
NSData *inData = [NSData dataWithContentsOfFile:fontPath];
|
||||
CFErrorRef error;
|
||||
CGDataProviderRef provider
|
||||
= CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
|
||||
CGFontRef font = CGFontCreateWithDataProvider(provider);
|
||||
|
||||
if (!CTFontManagerRegisterGraphicsFont(font, &error)) {
|
||||
CFStringRef errorDescription = CFErrorCopyDescription(error);
|
||||
|
||||
NSLog(@"Failed to load font: %@", errorDescription);
|
||||
CFRelease(errorDescription);
|
||||
}
|
||||
CFRelease(font);
|
||||
CFRelease(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to register a fatal error handler for React. Our handler
|
||||
* won't kill the process, it will swallow JS errors and print stack traces
|
||||
@@ -137,6 +108,8 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
// Store launch options, will be used when we create the bridge.
|
||||
_launchOptions = [launchOptions copy];
|
||||
|
||||
[Dropbox setAppKey];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -192,10 +165,13 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
if ([Dropbox application:app openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// XXX At least twice we received bug reports about malfunctioning loadURL
|
||||
// in the Jitsi Meet SDK while the Jitsi Meet app seemed to functioning as
|
||||
// expected in our testing. But that was to be expected because the app does
|
||||
@@ -205,10 +181,14 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [RCTLinkingManager application:application
|
||||
openURL:url
|
||||
sourceApplication:sourceApplication
|
||||
annotation:annotation];
|
||||
return [RCTLinkingManager application:app openURL:url options:options];
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
return [self application:application openURL:url options:@{}];
|
||||
}
|
||||
|
||||
#pragma mark Initializers
|
||||
@@ -400,9 +380,6 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
= [[RCTBridgeWrapper alloc] initWithLaunchOptions:_launchOptions];
|
||||
views = [NSMapTable strongToWeakObjectsMapTable];
|
||||
|
||||
// Dynamically load custom bundled fonts.
|
||||
loadCustomFonts(self.class);
|
||||
|
||||
// Register a fatal error handler for React.
|
||||
registerFatalErrorHandler();
|
||||
});
|
||||
|
||||
27
ios/sdk/src/dropbox/Dropbox.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 <React/RCTBridge.h>
|
||||
|
||||
@interface Dropbox : NSObject<RCTBridgeModule>
|
||||
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
|
||||
+ (void)setAppKey;
|
||||
|
||||
@end
|
||||
173
ios/sdk/src/dropbox/Dropbox.m
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright @ 2017-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 <React/RCTBridgeModule.h>
|
||||
#import <ObjectiveDropboxOfficial/ObjectiveDropboxOfficial.h>
|
||||
|
||||
#import "Dropbox.h"
|
||||
|
||||
RCTPromiseResolveBlock currentResolve = nil;
|
||||
RCTPromiseRejectBlock currentReject = nil;
|
||||
|
||||
@implementation Dropbox
|
||||
|
||||
+ (NSString *)getAppKey{
|
||||
NSArray *urlTypes
|
||||
= [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"];
|
||||
|
||||
for (NSDictionary<NSString *, NSArray *> *urlType in urlTypes) {
|
||||
NSArray *urlSchemes = urlType[@"CFBundleURLSchemes"];
|
||||
|
||||
if (urlSchemes) {
|
||||
for (NSString *urlScheme in urlSchemes) {
|
||||
if (urlScheme && [urlScheme hasPrefix:@"db-"]) {
|
||||
return [urlScheme substringFromIndex:3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport {
|
||||
BOOL enabled = [Dropbox getAppKey] != nil;
|
||||
|
||||
return @{
|
||||
@"ENABLED": [NSNumber numberWithBool:enabled]
|
||||
};
|
||||
};
|
||||
|
||||
RCT_EXPORT_METHOD(authorize:(RCTPromiseResolveBlock)resolve
|
||||
reject:(__unused RCTPromiseRejectBlock)reject) {
|
||||
currentResolve = resolve;
|
||||
currentReject = reject;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[DBClientsManager authorizeFromController:[UIApplication sharedApplication]
|
||||
controller:[[self class] topMostController]
|
||||
openURL:^(NSURL *url) {
|
||||
[[UIApplication sharedApplication] openURL:url];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getDisplayName: (NSString *)token
|
||||
resolve: (RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
DBUserClient *client = [[DBUserClient alloc] initWithAccessToken:token];
|
||||
[[client.usersRoutes getCurrentAccount] setResponseBlock:^(DBUSERSFullAccount *result, DBNilObject *routeError, DBRequestError *networkError) {
|
||||
if (result) {
|
||||
resolve(result.name.displayName);
|
||||
} else {
|
||||
NSString *msg = @"Failed!";
|
||||
if (networkError) {
|
||||
msg = [NSString stringWithFormat:@"Failed! Error: %@", networkError];
|
||||
}
|
||||
reject(@"getDisplayName", @"Failed", nil);
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getSpaceUsage: (NSString *)token
|
||||
resolve: (RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
DBUserClient *client = [[DBUserClient alloc] initWithAccessToken:token];
|
||||
[[client.usersRoutes getSpaceUsage] setResponseBlock:^(DBUSERSSpaceUsage *result, DBNilObject *routeError, DBRequestError *networkError) {
|
||||
if (result) {
|
||||
DBUSERSSpaceAllocation *allocation = result.allocation;
|
||||
NSNumber *allocated = 0;
|
||||
NSNumber *used = 0;
|
||||
if ([allocation isIndividual]) {
|
||||
allocated = allocation.individual.allocated;
|
||||
used = result.used;
|
||||
} else if ([allocation isTeam]) {
|
||||
allocated = allocation.team.allocated;
|
||||
used = allocation.team.used;
|
||||
}
|
||||
id objects[] = { used, allocated };
|
||||
id keys[] = { @"used", @"allocated" };
|
||||
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects
|
||||
forKeys:keys
|
||||
count:2];
|
||||
resolve(dictionary);
|
||||
} else {
|
||||
NSString *msg = @"Failed!";
|
||||
if (networkError) {
|
||||
msg = [NSString stringWithFormat:@"Failed! Error: %@", networkError];
|
||||
}
|
||||
reject(@"getSpaceUsage", msg, nil);
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
if (currentReject == nil || currentResolve == nil) {
|
||||
return NO;
|
||||
}
|
||||
DBOAuthResult *authResult = [DBClientsManager handleRedirectURL:url];
|
||||
if (authResult) {
|
||||
if ([authResult isSuccess]) {
|
||||
currentResolve(authResult.accessToken.accessToken);
|
||||
currentResolve = nil;
|
||||
currentReject = nil;
|
||||
} else {
|
||||
NSString *msg;
|
||||
if ([authResult isError]) {
|
||||
msg = [NSString stringWithFormat:@"%@, error type: %ld",[authResult errorDescription], [authResult errorType]];
|
||||
} else {
|
||||
msg = @"OAuth canceled!";
|
||||
}
|
||||
currentReject(@"authorize", msg, nil);
|
||||
currentResolve = nil;
|
||||
currentReject = nil;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (UIViewController *)topMostController
|
||||
{
|
||||
UIViewController *topController
|
||||
= [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
|
||||
while (topController.presentedViewController) {
|
||||
topController = topController.presentedViewController;
|
||||
}
|
||||
|
||||
return topController;
|
||||
}
|
||||
|
||||
+ (void)setAppKey {
|
||||
NSString *appKey = [self getAppKey];
|
||||
|
||||
if (appKey) {
|
||||
[DBClientsManager setupWithAppKey:appKey];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -136,13 +136,13 @@ cd ..
|
||||
|
||||
mkdir -p /tmp/jitsi-meet/
|
||||
|
||||
xcodebuild archive -workspace ios/jitsi-meet.xcworkspace -scheme jitsi-meet -configuration Release -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive
|
||||
xcodebuild archive -quiet -workspace ios/jitsi-meet.xcworkspace -scheme jitsi-meet -configuration Release -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive
|
||||
|
||||
sed -e "s/YOUR_TEAM_ID/${IOS_TEAM_ID}/g" ios/travis-ci/build-ipa.plist.template > ios/travis-ci/build-ipa.plist
|
||||
|
||||
IPA_EXPORT_DIR=/tmp/jitsi-meet/jitsi-meet-ipa
|
||||
|
||||
xcodebuild -exportArchive -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive -exportPath $IPA_EXPORT_DIR -exportOptionsPlist ios/travis-ci/build-ipa.plist
|
||||
xcodebuild -quiet -exportArchive -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchive -exportPath $IPA_EXPORT_DIR -exportOptionsPlist ios/travis-ci/build-ipa.plist
|
||||
|
||||
echo "Will try deploy the .ipa to: ${IPA_DEPLOY_LOCATION}"
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
{
|
||||
"en": "Anglais",
|
||||
"az": "Azerbaïdjanais",
|
||||
"bg": "Bulgare",
|
||||
"cs": "Tchèque",
|
||||
"de": "Allemand",
|
||||
"el": "Grec",
|
||||
"eo": "Espéranto",
|
||||
"es": "Espagnol",
|
||||
"fr": "Français",
|
||||
"hy": "Arménien",
|
||||
"it": "Italien",
|
||||
"ja": "Japonais",
|
||||
"ko": "Coréen",
|
||||
"nb": "Norvégien Bokmal",
|
||||
"oc": "Occitan",
|
||||
"pl": "Polonais",
|
||||
"ptBR": "Portugais (Brésil)",
|
||||
@@ -14,7 +21,6 @@
|
||||
"sl": "Slovène",
|
||||
"sv": "Suédois",
|
||||
"tr": "Turc",
|
||||
"zhCN": "Chinois (Chine)",
|
||||
"nb": "Norvégien Bokmal",
|
||||
"eo": "Espéranto"
|
||||
"vi": "Vietnamien",
|
||||
"zhCN": "Chinois (Chine)"
|
||||
}
|
||||
@@ -1,11 +1,18 @@
|
||||
{
|
||||
"en": "Inglês",
|
||||
"az": "Azerbaijanês",
|
||||
"bg": "Búlgaro",
|
||||
"cs": "Checo",
|
||||
"de": "Alemão",
|
||||
"el": "Grego",
|
||||
"eo": "Esperanto",
|
||||
"es": "Espanhol",
|
||||
"fr": "Francês",
|
||||
"hy": "Armênio",
|
||||
"it": "Italiano",
|
||||
"ja": "Japonês",
|
||||
"ko": "Coreano",
|
||||
"nb": "Bokmal norueguês",
|
||||
"oc": "Occitano",
|
||||
"pl": "Polonês",
|
||||
"ptBR": "Português (Brasil)",
|
||||
@@ -14,7 +21,6 @@
|
||||
"sl": "Esloveno",
|
||||
"sv": "Sueco",
|
||||
"tr": "Turco",
|
||||
"zhCN": "Chinês (China)",
|
||||
"nb": "Bokmal norueguês",
|
||||
"eo": "Esperanto"
|
||||
"vi": "Vietnamita",
|
||||
"zhCN": "Chinês (China)"
|
||||
}
|
||||
@@ -15,6 +15,6 @@
|
||||
"sv": "Шведский",
|
||||
"tr": "Турецкий",
|
||||
"zhCN": "Китайский (Китай)",
|
||||
"nb": "",
|
||||
"eo": ""
|
||||
"nb": "Норвежский букмол",
|
||||
"eo": "Эсперанто"
|
||||
}
|
||||
20
lang/languages-zhTW.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"en": "English",
|
||||
"bg": "Bulgarian",
|
||||
"de": "German",
|
||||
"es": "Spanish",
|
||||
"fr": "French",
|
||||
"hy": "Armenian",
|
||||
"it": "Italian",
|
||||
"oc": "Occitan",
|
||||
"pl": "Polish",
|
||||
"ptBR": "Portuguese (Brazil)",
|
||||
"ru": "Russian",
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sv": "Swedish",
|
||||
"tr": "Turkish",
|
||||
"zhCN": "中文 简体 (中国)",
|
||||
"nb": "Norwegian Bokmal",
|
||||
"eo": "Esperanto"
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"contactlist": "__count__ Membres",
|
||||
"contactlist_plural": "",
|
||||
"contactlist_plural": "__count__ Membres",
|
||||
"passwordSetRemotely": "défini par un autre membre",
|
||||
"poweredby": "Produit par",
|
||||
"inviteUrlDefaultMsg": "Votre conférence est en cours de création...",
|
||||
@@ -23,6 +22,7 @@
|
||||
"react-nativeGrantPermissions": "Sélectionnez <b><i>Autoriser</i></b> lorsque votre navigateur demande des autorisations.",
|
||||
"chromeGrantPermissions": "Sélectionnez <b><i>Autoriser</i></b> lorsque votre navigateur demande des autorisations.",
|
||||
"androidGrantPermissions": "Sélectionnez <b><i>Autoriser</i></b> lorsque votre navigateur demande des autorisations.",
|
||||
"electronGrantPermissions": "Merci d'autoriser le partage de votre camera et microphone",
|
||||
"firefoxGrantPermissions": "Sélectionnez <b><i>Partager le périphérique sélectionné</i></b> lorsque votre navigateur demande des autorisations.",
|
||||
"operaGrantPermissions": "Sélectionnez <b><i>Autoriser</i></b> lorsque votre navigateur demande des autorisations.",
|
||||
"iexplorerGrantPermissions": "Sélectionnez <b><i>OK</i></b> quand le navigateur demande les permissions.",
|
||||
@@ -35,57 +35,42 @@
|
||||
"raiseHand": "Lever ou baisser la main",
|
||||
"pushToTalk": "Appuyer pour parler",
|
||||
"toggleScreensharing": "Basculer entre la caméra et le partage d'écran",
|
||||
"toggleFilmstrip": "Afficher ou cacher la vidéo",
|
||||
"toggleShortcuts": "Afficher ou masquer ce menu d'aide",
|
||||
"toggleFilmstrip": "Afficher ou masquer les vignettes vidéos",
|
||||
"toggleShortcuts": "Afficher ou masquer les raccourcis clavier",
|
||||
"focusLocal": "Épingler ma vidéo",
|
||||
"focusRemote": "Épingler la vidéo des autres",
|
||||
"focusRemote": "Épingler la vidéo de quelqu'un d'autre",
|
||||
"toggleChat": "Ouvrir ou fermer le panneau de conversation",
|
||||
"mute": "Activer ou désactiver le microphone",
|
||||
"fullScreen": "Activer ou Désactiver le plein écran",
|
||||
"fullScreen": "Activer / Désactiver le mode plein écran",
|
||||
"videoMute": "Démarrer ou arrêter votre caméra",
|
||||
"showSpeakerStats": "Afficher les statistiques de l'interlocuteur"
|
||||
"showSpeakerStats": "Afficher les statistiques de l'interlocuteur",
|
||||
"localRecording": "Afficher ou masquer les commandes de l'enregistrement local"
|
||||
},
|
||||
"welcomepage": {
|
||||
"disable": "Ne plus afficher cette page",
|
||||
"feature1": {
|
||||
"content": "Aucun téléchargement requis. __app__ s'utilise directement depuis votre navigateur. Partager simplement l'URL de votre conférence avec les autres pour commencer.",
|
||||
"title": "Simple à utiliser"
|
||||
"accessibilityLabel": {
|
||||
"join": "Touchez pour rejoindre",
|
||||
"roomname": "Saisissez un nom de salle"
|
||||
},
|
||||
"feature2": {
|
||||
"content": "Les vidéo conférences à plusieurs participants nécessitent moins de 128 kbps. Le partage d'écran et les conférences avec seulement de l'audio sont possibles avec beaucoup moins de débit.",
|
||||
"title": "Bande passante faible"
|
||||
},
|
||||
"feature3": {
|
||||
"content": "__app__ est sous licence Apache. Vous êtes libre de télécharger, d'utiliser, de modifier et de partager __app__ selon cette licence libre.",
|
||||
"title": "Open source"
|
||||
},
|
||||
"feature4": {
|
||||
"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": {
|
||||
"content": "C'est facile de partager votre écran avec d'autres personnes. __app__ est idéal pour les présentations en ligne, les cours, et les sessions de support technique.",
|
||||
"title": "Partage d'écran"
|
||||
},
|
||||
"feature6": {
|
||||
"content": "Besoin de confidentialité ? Les salles de conférence __app__ peuvent être sécurisées par un mot de passe pour exclure les invités non désirées, et prévenir des interruptions.",
|
||||
"title": "Salles sécurisées"
|
||||
},
|
||||
"feature7": {
|
||||
"content": "__app__ propose Etherpad, un éditeur de texte collaboratif en temps réel qui est parfait pour les procès-verbaux, l'édition d'articles et plus encore.",
|
||||
"title": "Notes partagées"
|
||||
},
|
||||
"feature8": {
|
||||
"content": "Apprenez plus au sujet de vos utilisateurs avec une intégration facile de Piwik, Google Analytics et d'autres systèmes de statistiques et supervision d'utilisation.",
|
||||
"title": "Statistiques d'utilisation"
|
||||
"appDescription": "Allez-y, chat vidéo avec toute l'équipe. En fait, invitez tout le monde que vous connaissez. __app__ est une solution de visioconférence entièrement cryptée et 100% open source que vous pouvez utiliser toute la journée, tous les jours, gratuitement— aucun compte requis.",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Voix",
|
||||
"video": "Vidéo"
|
||||
},
|
||||
"calendar": "Calendrier",
|
||||
"connectCalendarText": "Connectez votre calendrier pour voir toutes vos réunion dans __app__. Ajoutez les réunions __app__ dans votre calendrier pour les lancer en un seul clic.",
|
||||
"connectCalendarButton": "Connecter votre calendrier",
|
||||
"enterRoomTitle": "Démarrer une nouvelle réunion",
|
||||
"go": "Créer",
|
||||
"join": "REJOINDRE",
|
||||
"privacy": "Confidentialité",
|
||||
"recentList": "Récent",
|
||||
"recentListDelete": "Supprimer",
|
||||
"recentListEmpty": "Votre liste récente est actuellement vide. Discuter avec votre équipe et vous trouverez toutes vos réunions récentes ici.",
|
||||
"roomname": "Saisissez un nom de salle",
|
||||
"roomnamePlaceHolder": "nom de la conférence",
|
||||
"roomnameHint": "Entrez le nom ou l'URL de la salle que vous souhaitez rejoindre. Vous pouvez faire un nom, laissez les gens que vous rencontrerez le savoir afin qu'ils entrent le même nom.",
|
||||
"sendFeedback": "Envoyer votre avis",
|
||||
"terms": "Termes"
|
||||
"terms": "Termes",
|
||||
"title": "Vidéoconférence Sécurisée, entièrement en vedette et gratuite"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
@@ -97,52 +82,95 @@
|
||||
"rejoinKeyTitle": "Rejoindre"
|
||||
},
|
||||
"toolbar": {
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "Activer/désactiver le mode voix uniquement",
|
||||
"audioRoute": "",
|
||||
"callQuality": "Accorder la qualité des appels",
|
||||
"chat": "Afficher/masquer la discussion instantanée",
|
||||
"cc": "Activer/désactiver les sous-titres",
|
||||
"document": "Activer/désactiver le document partagé",
|
||||
"feedback": "Laisser des commentaires",
|
||||
"fullScreen": "Activer/désactiver le plein écran",
|
||||
"hangup": "Quitter la conversation",
|
||||
"invite": "Inviter des participants",
|
||||
"localRecording": "Activer/désactiver les contrôles d'enregistrement local",
|
||||
"lockRoom": "Activer/Désactiver le verrouillage de la session",
|
||||
"moreActions": "Activer/désactiver le menu d'actions supplémentaires",
|
||||
"moreActionsMenu": "Menu d'actions supplémentaires",
|
||||
"mute": "Activer/désactiver l'audio",
|
||||
"pip": "Activer/désactiver le mode Picture in Picture",
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever/baisser la main",
|
||||
"recording": "Activer/désactiver l'enregistrement",
|
||||
"Settings": "Afficher/masquer le menu des paramètres",
|
||||
"sharedvideo": "Démarrer/arrêter le partage de vidéo Youtube",
|
||||
"shareRoom": "Inviter quelqu'un",
|
||||
"shareYourScreen": "Activer/désactiver le partage d’écran",
|
||||
"shortcuts": "Afficher/masquer les raccourcis",
|
||||
"speakerStats": "Afficher/cacher les statistiques de parole",
|
||||
"toggleCamera": "Activer/désactiver la caméra",
|
||||
"tileView": "Activer/désactiver la vue mosaïque",
|
||||
"videomute": "Activer/désactiver la vidéo"
|
||||
},
|
||||
"addPeople": "Ajouter des personnes à votre appel",
|
||||
"audioonly": "Activer / Désactiver le mode audio uniquement (économiser de la bande passante)",
|
||||
"audioOnlyOn": "Activer/désactiver le mode audio uniquement (économiser de la bande passante)",
|
||||
"audioOnlyOff": "Désactiver le mode audio uniquement",
|
||||
"audioRoute": "Sélectionner le périphérique audio",
|
||||
"callQuality": "Accorder la qualité des appels",
|
||||
"enterFullScreen": "Afficher en plein écran",
|
||||
"exitFullScreen": "Quitter le mode plein écran",
|
||||
"feedback": "Laisser des commentaires",
|
||||
"moreActions": "Plus d'actions",
|
||||
"mute": "Muet / Actif",
|
||||
"videomute": "Démarrer / Arrêter la caméra",
|
||||
"authenticate": "Authentifiez-vous",
|
||||
"lock": "Verrouiller / déverrouiller la conférence",
|
||||
"invite": "Partager le lien",
|
||||
"chat": "Ouvrir / Fermer le chat",
|
||||
"etherpad": "Ouvrir / Fermer le document partagé",
|
||||
"documentOpen": "Ouvrir le document partagé",
|
||||
"documentClose": "Fermer le document partagé",
|
||||
"shareRoom": "Partager le salon",
|
||||
"sharedvideo": "Partager une vidéo YouTube",
|
||||
"sharescreen": "Démarrer / Arrêter le partage d'écran",
|
||||
"stopSharedVideo": "Arrêter la vidéo YouTube",
|
||||
"fullscreen": "Activer / Désactiver le plein écran",
|
||||
"sip": "Appeler un numéro SIP",
|
||||
"Settings": "Paramètres",
|
||||
"hangup": "Quitter",
|
||||
"login": "Connexion",
|
||||
"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.",
|
||||
"toggleCamera": "Activer/désactiver la caméra",
|
||||
"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",
|
||||
"micDisabled": "Le microphone n'est pas disponible",
|
||||
"filmstrip": "Afficher / Masquer les vidéos",
|
||||
"pip": "Entrer en mode Picture-in-Picture",
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever / Baisser la main"
|
||||
},
|
||||
"unsupportedBrowser": {
|
||||
"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",
|
||||
"filmstrip": "Afficher / cacher les vidéos",
|
||||
"contactlist": "Voir et inviter des participants"
|
||||
"raiseHand": "Lever / Baisser la main",
|
||||
"shortcuts": "Afficher les raccourcis",
|
||||
"speakerStats": "Statistiques de l'interlocuteur",
|
||||
"tileViewToggle": "Activer/désactiver la vue mosaïque",
|
||||
"invite": "Inviter des participants"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Saisissez un pseudonyme dans le champ ci-dessous",
|
||||
"popover": "Choisissez un pseudonyme"
|
||||
},
|
||||
"error": "Erreur : votre message \"__originalText__\" n'a pas été envoyé. Raison : __error__",
|
||||
"messagebox": "Saisissez votre texte..."
|
||||
},
|
||||
"settings": {
|
||||
"calendar": {
|
||||
"about": "L'intégration de __appName__ avec votre calendrier permet d’accéder de manière sécurisée aux événement à venir.",
|
||||
"disconnect": "Se déconnecter",
|
||||
"microsoftSignIn": "Se connecter avec Microsoft",
|
||||
"signedIn": "Accès aux événements du calendrier __email__. Cliquez sur le bouton se déconnecter ci-dessous pour arrêter l'accès aux événements du calendrier.",
|
||||
"title": "Calendrier"
|
||||
},
|
||||
"title": "Paramètres",
|
||||
"update": "Mise à jour",
|
||||
"name": "Nom",
|
||||
@@ -152,11 +180,15 @@
|
||||
"selectMic": "Microphone",
|
||||
"selectAudioOutput": "Sortie audio",
|
||||
"followMe": "Tout le monde me suit",
|
||||
"language": "Langue",
|
||||
"loggedIn": "Connecté en tant que __name__",
|
||||
"noDevice": "Aucun",
|
||||
"cameraAndMic": "Caméra et microphone",
|
||||
"moderator": "MODÉRATEUR",
|
||||
"moderator": "Moderateur",
|
||||
"more": "Plus",
|
||||
"password": "DÉFINIR UN MOT DE PASSE",
|
||||
"audioVideo": "AUDIO ET VIDÉO"
|
||||
"audioVideo": "AUDIO ET VIDÉO",
|
||||
"devices": "Périphériques"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profil",
|
||||
@@ -176,7 +208,9 @@
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "État de la connexion",
|
||||
"connectedTo": "Connecté à :",
|
||||
"bitrate": "Débit :",
|
||||
"bridgeCount": "Nombre de serveurs :",
|
||||
"packetloss": "Perte de paquets :",
|
||||
"resolution": "Résolution :",
|
||||
"framerate": "Images par seconde",
|
||||
@@ -216,15 +250,20 @@
|
||||
"focus": "Focus de conférence",
|
||||
"focusFail": "__component__ n'est pas disponible - réessayez dans __ms__ sec",
|
||||
"grantedTo": "Droits modérateur accordés à __to__ !",
|
||||
"grantedToUnknown": "Droits modérateur accordés à $t(notify.somebody)!",
|
||||
"muted": "Vous avez commencé la conversation en muet.",
|
||||
"mutedTitle": "Vous êtes en muet !",
|
||||
"raisedHand": "Aimerait prendre la parole."
|
||||
"raisedHand": "Aimerait prendre la parole.",
|
||||
"suboptimalExperienceTitle": "Avertissement du navigateur",
|
||||
"suboptimalExperienceDescription": "Eer ... nous craignons que votre expérience avec __appName__ ne sera pas aussi excellente que nous le pensions. Nous cherchons des moyens d'améliorer cela, mais jusque là, essayez d'utiliser l'un des <a href='static/recommendedBrowsers.html' target='_blank'>navigateurs entièrement pris en charge</a>."
|
||||
},
|
||||
"dialog": {
|
||||
"add": "Ajouter",
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": "Diffusion en direct"
|
||||
},
|
||||
"allow": "Autoriser",
|
||||
"confirm": "Confirmer",
|
||||
"kickMessage": "Oups! Vous avez été renvoyé de la réunion !",
|
||||
"kickTitle": "Viré de la conférence",
|
||||
"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",
|
||||
@@ -237,7 +276,6 @@
|
||||
"copy": "Copier",
|
||||
"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.",
|
||||
"failedpermissions": "Échec d'obtention des permissions pour utiliser le micro et/ou la caméra.",
|
||||
"conferenceReloadTitle": "Malheureusement, un problème est survenu",
|
||||
@@ -248,6 +286,7 @@
|
||||
"rejoinNow": "Rejoindre maintenant",
|
||||
"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.",
|
||||
"lockRoom": "Verrouiller la réunion",
|
||||
"lockTitle": "Échec du verrouillage",
|
||||
"lockMessage": "Impossible de verrouiller la conférence.",
|
||||
"warning": "Avertissement",
|
||||
@@ -288,10 +327,6 @@
|
||||
"Save": "Sauvegarder",
|
||||
"recording": "Enregistrement",
|
||||
"recordingToken": "Saisissez un jeton d'enregistrement",
|
||||
"passwordCheck": "Voulez-vous vraiment supprimer votre mot de passe ?",
|
||||
"passwordMsg": "Saisissez un mot de passe pour verrouiller la conférence",
|
||||
"shareLink": "Partager le lien de la conférence",
|
||||
"yourPassword": "Saisissez un nouveau mot de passe",
|
||||
"Back": "Retour",
|
||||
"serviceUnavailable": "Service indisponible",
|
||||
"gracefulShutdown": "Le service est actuellement en maintenance. Réessayez plus tard.",
|
||||
@@ -299,29 +334,31 @@
|
||||
"reservationError": "Erreur du système de réservation",
|
||||
"reservationErrorMsg": "Code d'erreur: __code__, message: __msg__",
|
||||
"password": "Saisir le mot de passe",
|
||||
"unlockRoom": "Déverrouiller la réunion",
|
||||
"userPassword": "mot de passe utilisateur",
|
||||
"token": "jeton",
|
||||
"tokenAuthFailedTitle": "Échec de l'authentification",
|
||||
"tokenAuthFailed": "Désolé, vous n'êtes pas autorisé à rejoindre cette conversation.",
|
||||
"displayNameRequired": "Un nom d'utilisateur est requis",
|
||||
"enterDisplayName": "Veuillez saisir votre nom",
|
||||
"extensionRequired": "Extension requise :",
|
||||
"firefoxExtensionPrompt": "Vous devez installer une extension Firefox pour utiliser le partage d'écran. Merci d'essayer de nouveau après l'installation <a href='__url__'>depuis ce lien</a> !",
|
||||
"feedbackHelp": "Vos retours nous permettrons d'améliorer notre expérience vidéo.",
|
||||
"feedbackQuestion": "Informez-nous à propos de votre appel !",
|
||||
"thankYou": "Merci d'avoir utilisé __appName__ !",
|
||||
"sorryFeedback": "Nous sommes désolés d'apprendre cela. Voulez-vous nous en dire plus ?",
|
||||
"liveStreaming": "Direct",
|
||||
"streamKey": "Stream name/key",
|
||||
"startLiveStreaming": "Commencer le direct",
|
||||
"streamKey": "Clé Live stream",
|
||||
"startLiveStreaming": "Démarrer la diffusion en direct",
|
||||
"startRecording": "Commencer l'enregistrement",
|
||||
"stopStreamingWarning": "Désirez-vous vraiment arrêter le direct?",
|
||||
"stopRecordingWarning": "Désirez-vous vraiment arrêter l'enregistrement?",
|
||||
"stopLiveStreaming": "Arrêter le direct",
|
||||
"stopLiveStreaming": "Arrêter la diffusion en direct",
|
||||
"stopRecording": "Arrêter l'enregistrement",
|
||||
"doNotShowMessageAgain": "Ne plus afficher ce message",
|
||||
"permissionDenied": "Permission refusée",
|
||||
"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",
|
||||
"screenSharingFirefoxPermissionDeniedError": "Quelque chose s'est mal passé pendant que nous essayions de partager votre écran. S'il vous plaît assurez-vous que vous nous avez donné la permission de le faire.",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "Oups! Nous ne pouvions pas démarrer le partage d'écran!",
|
||||
"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.",
|
||||
@@ -344,6 +381,10 @@
|
||||
"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",
|
||||
"liveStreamingDisabledTooltip": "La diffusion en direct est désactivé",
|
||||
"liveStreamingDisabledForGuestTooltip": "Les invités ne peuvent démarrer la diffusion en direct.",
|
||||
"recordingDisabledTooltip": "L'enregistrement est désactivé.",
|
||||
"recordingDisabledForGuestTooltip": "Les invités ne peuvent enregistrer.",
|
||||
"remoteControlTitle": "Contrôle de bureau à distance",
|
||||
"remoteControlRequestMessage": "Voulez-vous autoriser __user__ à contrôler votre bureau?",
|
||||
"remoteControlShareScreenWarning": "Si vous appuyez sur \"Autoriser\" vous allez partager votre écran!",
|
||||
@@ -354,8 +395,11 @@
|
||||
"remoteControlStopMessage": "La prise en main à distance est terminée!",
|
||||
"close": "Fermer",
|
||||
"shareYourScreen": "Partagez votre écran",
|
||||
"shareYourScreenDisabled": "Le partage d’écran est désactivé.",
|
||||
"shareYourScreenDisabledForGuest": "Les invités ne peuvent partager l'écran",
|
||||
"yourEntireScreen": "Votre écran entier",
|
||||
"applicationWindow": "Fenêtre d'application"
|
||||
"applicationWindow": "Fenêtre d'application",
|
||||
"transcribing": "Transcription"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
@@ -385,6 +429,22 @@
|
||||
],
|
||||
"and": "et"
|
||||
},
|
||||
"share": {
|
||||
"mainText": [
|
||||
"Cliquez sur le lien suivant pour rejoindre une conférence :",
|
||||
"__roomUrl__"
|
||||
],
|
||||
"dialInfoText": [
|
||||
"",
|
||||
"",
|
||||
"=====",
|
||||
"",
|
||||
"Voulez-vous appeler depuis votre téléphone?",
|
||||
"",
|
||||
"__defaultDialInNumber__Cliquez sur ce lien pour voir les numéros de téléphone pour cette conférence",
|
||||
"__dialInfoPageUrl__"
|
||||
]
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Erreur",
|
||||
"CONNECTING": "Connexion en cours",
|
||||
@@ -398,30 +458,67 @@
|
||||
"ATTACHED": "Attachée"
|
||||
},
|
||||
"recording": {
|
||||
"beta": "BETA",
|
||||
"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.",
|
||||
"expandedOff": "L'enregistrement a été arrêté",
|
||||
"expandedOn": "Cette conférence est actuellement en cours d'enregistrement.",
|
||||
"expandedPending": "Démarrage de l'enregistrement...",
|
||||
"failedToStart": "L'enregistrement n'as pas réussi à démarrer",
|
||||
"live": "DIRECT",
|
||||
"off": "Enregistrement arrêté",
|
||||
"on": "Enregistrement",
|
||||
"pending": "Enregistrement en attente de participant...",
|
||||
"pending": "Préparation de l'enregistrement de la réunion...",
|
||||
"rec": "REC",
|
||||
"authDropboxText": "Téléchargement vers Dropbox",
|
||||
"serviceName": "Service d'enregistrement",
|
||||
"signOut": "Se déconnecter",
|
||||
"signIn": "s'identifier",
|
||||
"loggedIn": "Connecté en tant que __userName__",
|
||||
"availableSpace": "Espace disponible: __spaceLeft_ Mo (approximativement __duration__ minutes d'enregistrement)",
|
||||
"startRecordingBody": "Voulez-vous vraiment démarrer l'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"
|
||||
},
|
||||
"transcribing": {
|
||||
"pending": "Préparation de la transcription de la réunion...",
|
||||
"off": "La transcription désactivée",
|
||||
"error": "Échec de la transcription. Veuillez réessayer.",
|
||||
"expandedLabel": "La transcription est actuellement activée",
|
||||
"failedToStart": "Échec de démarrage de la transcription",
|
||||
"tr": "TR",
|
||||
"labelToolTip": "La transcription de la réunion est en cours",
|
||||
"ccButtonTooltip": "Afficher/masquer les sous-titres",
|
||||
"start": "Afficher/masquer les sous-titres",
|
||||
"stop": "Désactiver le sous-titrage"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"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",
|
||||
"changeSignIn": "Changer de compte.",
|
||||
"choose": "Choisir un flux live",
|
||||
"chooseCTA": "Choisissez une option de diffusion. Vous êtes actuellement connecté comme __email__.",
|
||||
"enterStreamKey": "Entrez votre clé de flux live Youtube ici",
|
||||
"error": "Le Streaming a échoué. Veuillez réessayer.",
|
||||
"errorAPI": "Une erreur s'est produite lors de l'accès à vos diffusions YouTube. Veuillez réessayer de vous connecter.",
|
||||
"errorLiveStreamNotEnabled": "La diffusion en direct n'est pas activée pour __email__. Merci de l'activer ou de vous connecter avec un compte où elle est déjà activée.",
|
||||
"expandedOff": "La diffusion en direct a été arrêtée",
|
||||
"expandedOn": "La conférence est en cours de diffusion sur YouTube.",
|
||||
"expandedPending": "La diffusion en direct a commencé...",
|
||||
"failedToStart": "Le Streaming n'as pas réussi à démarrer",
|
||||
"off": "Le Streaming a été arrêter",
|
||||
"off": "Le Streaming a été arrêté",
|
||||
"on": "Direct",
|
||||
"pending": "Commencer le direct...",
|
||||
"serviceName": "Service de diffusion en direct",
|
||||
"streamIdRequired": "Merci de renseigner le stream id pour lancer le streaming.",
|
||||
"streamIdHelp": "Où puis-je trouver ceci?",
|
||||
"signedInAs": "Vous êtes connecté en tant que :",
|
||||
"signIn": "Se connecter avec Google",
|
||||
"signOut": "Se déconnecter",
|
||||
"signInCTA": "Connectez vous ou entrez votre clé de flux live provenant de Youtube.",
|
||||
"start": "Démarrer la diffusion en direct",
|
||||
"streamIdHelp": "Qu'est-ce que c'est?",
|
||||
"unavailableTitle": "Le Streaming est indisponible"
|
||||
},
|
||||
"videoSIPGW": {
|
||||
@@ -449,28 +546,20 @@
|
||||
"noPermission": "Permission non accordée",
|
||||
"previewUnavailable": "Aperçu non disponible",
|
||||
"selectADevice": "Sélectionner un périphérique",
|
||||
"testAudio": "Son de test"
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "Ajouter un mot de passe",
|
||||
"callNumber": "Appeler le __number__",
|
||||
"enterID": "Saisissez l'identifiant: __conferenceID__ suivi de # pour rejoindre avec un téléphone",
|
||||
"howToDialIn": "Pour rejoindre avec un téléphone, utilisez un des des numéros suivants et l'identifiant de la conférence",
|
||||
"hidePassword": "Cacher le mot de passe",
|
||||
"inviteTo": "inviter des participants à __conferenceName__",
|
||||
"invitedYouTo": "__userName__ vous a invité(e) à la conférence __inviteURL__",
|
||||
"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."
|
||||
"testAudio": "Lire un audio de test"
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "VOIX",
|
||||
"audioOnlyExpanded": "Vous êtes en mode audio uniquement. Ce mode économise de la bande passant mais vous ne pourrez pas voir la vidéo des autres participants.",
|
||||
"callQuality": "Qualité de l'appel",
|
||||
"hd": "HD",
|
||||
"hdTooltip": "Regardez la vidéo en haute définition",
|
||||
"highDefinition": "Haute définition",
|
||||
"labelTooltipVideo": "Qualité vidéo actuelle",
|
||||
"labelTooltipAudioOnly": "Mode audio uniquement activé",
|
||||
"labelTooiltipNoVideo": "Aucune vidéo",
|
||||
"labelTooltipVideo": "Qualité vidéo actuelle",
|
||||
"ld": "BD",
|
||||
"ldTooltip": "Regardez la vidéo en basse définition",
|
||||
"lowDefinition": "Basse définition",
|
||||
"onlyAudioAvailable": "Seul l'audio est disponible",
|
||||
"onlyAudioSupported": "Nous ne supportons que l'audio sur ce navigateur.",
|
||||
@@ -478,21 +567,31 @@
|
||||
"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",
|
||||
"sdTooltip": "Regardez la vidéo en définition standard",
|
||||
"standardDefinition": "Moyenne Définition",
|
||||
"qualityButtonTip": "Changer la qualité de vidéo reçue"
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "Composer",
|
||||
"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!"
|
||||
"statusMessage": "est maintenant __status__"
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "Ajouter",
|
||||
"add": "Inviter",
|
||||
"countryNotSupported": "Nous ne supportons pas encore cette destination.",
|
||||
"countryReminder": "Appel hors États-Unis? Veuillez commencer avec le code du pays!",
|
||||
"disabled": "Vous ne pouvez pas inviter quelqu'un.",
|
||||
"footerText": "Appels sortants désactivés",
|
||||
"invite": "Inviter",
|
||||
"loading": "Rechercher des personnes et des numéros de téléphone",
|
||||
"loadingNumber": "Validation du numéro de téléphone",
|
||||
"loadingPeople": "Recherche de personnes à inviter",
|
||||
"noResults": "Aucun résultat de recherche correspondant",
|
||||
"searchPlaceholder": "Rechercher des personnes et des salons à ajouter",
|
||||
"title": "Ajouter des personnes à votre appel",
|
||||
"noValidNumbers": "Veuillez entrer un numéro de téléphone",
|
||||
"notAvailable": "Vous ne pouvez pas inviter quelqu'un.",
|
||||
"searchNumbers": "Ajouter des numéros de téléphone",
|
||||
"searchPeople": "Rechercher une personne",
|
||||
"searchPeopleAndNumbers": "Rechercher des personnes ou ajouter leurs numéros de téléphone",
|
||||
"telephone": "Téléphone: __number__",
|
||||
"title": "Inviter une personne à cette réunion",
|
||||
"failedToAdd": "Échec de l'ajout de membres"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
@@ -511,14 +610,131 @@
|
||||
"average": "Moyen",
|
||||
"bad": "Mauvais",
|
||||
"good": "Bien",
|
||||
"rateExperience": "Veuillez évaluer votre réunion.",
|
||||
"detailsLabel": "Dites nous en plus à ce sujet.",
|
||||
"rateExperience": "Veuillez évaluer votre expérience.",
|
||||
"veryBad": "Très mauvais",
|
||||
"veryGood": "Très bon"
|
||||
},
|
||||
"info": {
|
||||
"copy": "Copier le lien",
|
||||
"invite": "Inviter à __app__",
|
||||
"title": "Informations sur la conférence",
|
||||
"tooltip": "Obtenir des informations d'accès"
|
||||
"accessibilityLabel": "Afficher les informations",
|
||||
"addPassword": "Ajouter un mot de passe",
|
||||
"cancelPassword": "Annuler mot de passe",
|
||||
"conferenceURL": "Lien:",
|
||||
"country": "Pays",
|
||||
"dialANumber": "Pour rejoindre votre réunion, composez l'un de ces numéros, puis entrez ce code PIN: __conferenceID __ #",
|
||||
"dialInNumber": "Composer:",
|
||||
"dialInConferenceID": "PIN:",
|
||||
"dialInNotSupported": "Désolé, les appels entrants ne sont pas supportés.",
|
||||
"genericError": "Oups, quelque chose a mal tourné.",
|
||||
"inviteLiveStream": "Pour voir la diffusion en direct de cette réunion, cliquez sur ce lien : __url__",
|
||||
"invitePhone": "Pour joindre par téléphone, composez __number__ et saisissez ce code PIN : __conferenceID__ #",
|
||||
"invitePhoneAlternatives": "Pour voir plus de numéros de téléphone, suivez ce lien : __url__",
|
||||
"inviteURL": "Pour rejoindre la vidéoconférence, cliquez sur ce lien : __url__",
|
||||
"liveStreamURL": "Diffusion en direct :",
|
||||
"moreNumbers": "Plus de numéros ",
|
||||
"noNumbers": "Numéros à composer non trouvés",
|
||||
"noPassword": "Aucun",
|
||||
"noRoom": "Aucune réunion n'a été spécifiée pour l'appel entrant.",
|
||||
"numbers": "Numéros d'appel",
|
||||
"password": "Mot de passe:",
|
||||
"title": "Partager",
|
||||
"tooltip": "Partager le lien et les informations de connexion pour cette conférence"
|
||||
},
|
||||
"settingsView": {
|
||||
"alertOk": "D'accord",
|
||||
"alertTitle": "Avertissement",
|
||||
"alertURLText": "L'URL du serveur est invalide",
|
||||
"conferenceSection": "Conférence",
|
||||
"displayName": "Pseudo",
|
||||
"email": "Email",
|
||||
"header": "Paramètres",
|
||||
"profileSection": "Profil",
|
||||
"serverURL": "URL du serveur",
|
||||
"startWithAudioMuted": "Commencez avec la vidéo en sourdine",
|
||||
"startWithVideoMuted": "Commencez avec la vidéo en sourdine"
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "Ajouter un lien de conférence",
|
||||
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement?",
|
||||
"confirmAddLinkTitle": "Calendrier",
|
||||
"join": "Joindre",
|
||||
"joinTooltip": "Rejoindre la réunion",
|
||||
"nextMeeting": "prochaine réunion",
|
||||
"noEvents": "Il n'y a pas d’événement à venir.",
|
||||
"ongoingMeeting": "La réunion en cours",
|
||||
"permissionButton": "Afficher les réglages",
|
||||
"permissionMessage": "La permission du calendrier est requise pour afficher vos réunions dans l'application.",
|
||||
"refresh": "Rafraîchir le calendrier",
|
||||
"today": "Aujourd'hui"
|
||||
},
|
||||
"recentList": {
|
||||
"joinPastMeeting": "Rejoindre une réunion précédente"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Tirer pour recharger"
|
||||
},
|
||||
"deepLinking": {
|
||||
"title": "Lancement de votre réunion dans __app __ en cours...",
|
||||
"description": "Rien ne s'est passé? Nous avons essayé de lancer votre réunion dans l'application de bureau __app__. Essayez à nouveau ou lancez-la dans l'application web __app__.",
|
||||
"tryAgainButton": "Réessayez sur le bureau",
|
||||
"launchWebButton": "Lancer dans le navigateur",
|
||||
"appNotInstalled": "Vous avez besoin de l'application mobile __app__ pour participer à cette réunion avec votre téléphone.",
|
||||
"downloadApp": "Télécharger l'application",
|
||||
"openApp": "Continuer vers l'application"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"invited": "Invité(e)",
|
||||
"ringing": "Appel en cours...",
|
||||
"calling": "Appel...",
|
||||
"initializingCall": "Lancement de l'appel...",
|
||||
"connected": "Connecté",
|
||||
"connecting": "Connexion en cours...",
|
||||
"connecting2": "Connexion en cours*...",
|
||||
"disconnected": "Déconnecté",
|
||||
"busy": "Occupé",
|
||||
"rejected": "Rejeté",
|
||||
"ignored": "Ignoré",
|
||||
"expired": "Expiré"
|
||||
},
|
||||
"dateUtils": {
|
||||
"today": "Aujourd'hui",
|
||||
"yesterday": "Hier",
|
||||
"earlier": "Plus tôt"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "Répondre",
|
||||
"audioCallTitle": "Appel entrant",
|
||||
"decline": "Rejeter",
|
||||
"productLabel": "de Jitsi Meet",
|
||||
"videoCallTitle": "Appel vidéo entrant"
|
||||
},
|
||||
"localRecording": {
|
||||
"localRecording": "Enregistrement local",
|
||||
"dialogTitle": "Commandes de l'enregistrement local",
|
||||
"start": "Démarrer l'enregistrement",
|
||||
"stop": "Arrêter l'enregistrement",
|
||||
"moderator": "Moderateur",
|
||||
"me": "Moi",
|
||||
"duration": "Durée",
|
||||
"durationNA": "N/A",
|
||||
"encoding": "Encodage",
|
||||
"participantStats": "Statistiques du participant",
|
||||
"participant": "Participant",
|
||||
"sessionToken": "Token de la session",
|
||||
"clientState": {
|
||||
"on": "Actif",
|
||||
"off": "Inactif",
|
||||
"unknown": "Inconnu"
|
||||
},
|
||||
"messages": {
|
||||
"engaged": "Enregistrement local engagé.",
|
||||
"finished": "L'enregistrement de la session __token__ s'est terminé. Merci d'envoyer le fichier au modérateur.",
|
||||
"finishedModerator": "L'enregistrement de la session __token__ s'est terminé. La piste a bien été sauvegardée. Merci de demander aux autres participants de soumettre leurs enregistrements.",
|
||||
"notModerator": "Vous n'êtes pas le modérateur. Vous ne pouvez pas démarrer ou arrêter un enregistrement local."
|
||||
},
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"label": "ENR-LOC",
|
||||
"labelToolTip": "L'enregistrement local est engagé"
|
||||
}
|
||||
}
|
||||
@@ -35,31 +35,39 @@
|
||||
"raiseHand": "Erga ou baixe sua mão",
|
||||
"pushToTalk": "Pressione para falar",
|
||||
"toggleScreensharing": "Trocar entre câmera e compartilhamento de tela",
|
||||
"toggleFilmstrip": "Mostrar ou ocultar a barra lateral",
|
||||
"toggleShortcuts": "Mostrar ou ocultar este menu de ajuda",
|
||||
"toggleFilmstrip": "Mostrar ou ocultar miniaturas de vídeo",
|
||||
"toggleShortcuts": "Mostrar ou ocultar atalhos de teclado",
|
||||
"focusLocal": "Focar no seu vídeo",
|
||||
"focusRemote": "Focar no vídeo de outro participante",
|
||||
"toggleChat": "Abrir ou fechar o painel de bate-papo",
|
||||
"mute": "Deixar mudo ou não o microfone",
|
||||
"fullScreen": "Entrar ou sair da tela cheia",
|
||||
"videoMute": "Iniciar ou parar sua câmera",
|
||||
"showSpeakerStats": "Exibir estatísticas do alto falante"
|
||||
"showSpeakerStats": "Exibir estatísticas do alto falante",
|
||||
"localRecording": "Mostrar ou ocultar controles de gravação local"
|
||||
},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
"join": "Toque para entrar",
|
||||
"roomname": "Digite o nome da sala"
|
||||
},
|
||||
"appDescription": "Vá em frente, converse por vídeo com toda a equipe. De fato, convide todos que você conhece. __app__ é uma solução de videoconferência totalmente criptografada e 100% de código aberto que você pode usar todos os dias, a cada dia, gratuitamente — sem necessidade de conta.",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Voz",
|
||||
"video": "Vídeo"
|
||||
},
|
||||
"calendar": "Calendário",
|
||||
"connectCalendarText": "Conecte seu calendário para ver todas as suas reuniões no __app__. Além disso, adicione reuniões no __app__ ao seu calendário e inicie-as com um clique.",
|
||||
"connectCalendarButton": "Conectar seu calendário",
|
||||
"go": "IR",
|
||||
"join": "Entrar",
|
||||
"privacy": "Política de Privacidade",
|
||||
"recentList": "Hitstórico",
|
||||
"roomname": "Digite o nome da sala",
|
||||
"roomnameHint": "Digite o nome ou a URL da sala que você deseja entrar. Você pode digitar um nome, e apenas deixe para as pessoas que você quer se reunir digitem o mesmo nome.",
|
||||
"sendFeedback": "Enviar comentários",
|
||||
"terms": "Termos",
|
||||
"title": "Vídeo conferência mais segura, mais flexível e completamente livre "
|
||||
"title": "Vídeo conferência mais segura, mais flexível e completamente livre."
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
@@ -71,8 +79,41 @@
|
||||
"rejoinKeyTitle": "Reconectar"
|
||||
},
|
||||
"toolbar": {
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "Alternar para apenas áudio",
|
||||
"audioRoute": "",
|
||||
"callQuality": "Gerenciar qualidade da chamada",
|
||||
"chat": "Alternar para janela de chat",
|
||||
"cc": "Alternar legendas",
|
||||
"document": "Alternar para documento compartilhado",
|
||||
"feedback": "Deixar feedback",
|
||||
"fullScreen": "Alternar para tela cheia",
|
||||
"hangup": "Sair da chamada",
|
||||
"invite": "Convidar pessoas",
|
||||
"localRecording": "Alternar controles de gravação local",
|
||||
"lockRoom": "Alternar trava da sala",
|
||||
"moreActions": "Alternar mais menu de ações",
|
||||
"moreActionsMenu": "Menu de mais ações",
|
||||
"mute": "Alternar mudo do áudio",
|
||||
"pip": "Alternar modo Picture-in-Picture",
|
||||
"profile": "Editar seu perfil",
|
||||
"raiseHand": "Alternar levantar a mão",
|
||||
"recording": "Alternar gravação",
|
||||
"Settings": "Alternar configurações",
|
||||
"sharedvideo": "Alternar compartilhamento de vídeo do Youtube",
|
||||
"shareRoom": "Convidar alguém",
|
||||
"shareYourScreen": "Alternar compartilhamento de tela",
|
||||
"shortcuts": "Alternar atalhos",
|
||||
"speakerStats": "Alternar estatísticas do apresentador",
|
||||
"toggleCamera": "",
|
||||
"tileView": "",
|
||||
"videomute": "Alternar mudo do vídeo"
|
||||
},
|
||||
"addPeople": "Adicionar pessoas à sua chamada",
|
||||
"audioonly": "Ativar / desativar modo somente áudio (economiza banda)",
|
||||
"audioOnlyOn": "Ativar modo somente áudio (economiza banda)",
|
||||
"audioOnlyOff": "Desativar modo somente áudio",
|
||||
"audioRoute": "Selecionar o dispositivo de som",
|
||||
"callQuality": "Gerenciar qualidade da chamada",
|
||||
"enterFullScreen": "Ver em tela cheia",
|
||||
"exitFullScreen": "Sair da tela cheia",
|
||||
@@ -86,8 +127,8 @@
|
||||
"etherpad": "Abrir ou fechar o documento compartilhado",
|
||||
"documentOpen": "Abrir documento compartilhado",
|
||||
"documentClose": "Fechar documento compartilhado",
|
||||
"shareRoom": "Compartilhar sala",
|
||||
"sharedvideo": "Compartilhar um vídeo do YouTube",
|
||||
"sharescreen": "Compartilhamento de tela",
|
||||
"stopSharedVideo": "Parar vídeo do YouTube",
|
||||
"fullscreen": "Entrar ou sair da tela cheia",
|
||||
"sip": "Chamar número SIP",
|
||||
@@ -96,16 +137,20 @@
|
||||
"login": "Iniciar sessão",
|
||||
"logout": "Encerrar sessão",
|
||||
"sharedVideoMutedPopup": "Seu vídeo compartilhado foi silenciado para que você possa conversar com os outros membros.",
|
||||
"toggleCamera": "Alternar câmera",
|
||||
"micMutedPopup": "Seu microfone foi silenciado para que você aproveite plenamente seu vídeo compartilhado.",
|
||||
"talkWhileMutedPopup": "Tentando falar? Você está em mudo.",
|
||||
"unableToUnmutePopup": "Você não pode sair do mudo enquanto seu vídeo compartilhado está ativo.",
|
||||
"cameraDisabled": "A câmera não está disponível",
|
||||
"micDisabled": "O microfone não está disponível",
|
||||
"filmstrip": "Mostrar / ocultar vídeos",
|
||||
"pip": "Entrar em modo Quadro-a-Quadro",
|
||||
"profile": "Editar seu perfil",
|
||||
"raiseHand": "Erguer o baixar sua mão",
|
||||
"raiseHand": "Erguer / Baixar sua mão",
|
||||
"shortcuts": "Ver atalhos",
|
||||
"speakerStats": "Estatísticas do Apresentador"
|
||||
"speakerStats": "Estatísticas do Apresentador",
|
||||
"tileViewToggle": "Alternar visualização em blocos",
|
||||
"invite": "Convidar pessoas"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
@@ -115,6 +160,13 @@
|
||||
"messagebox": "Digite um texto..."
|
||||
},
|
||||
"settings": {
|
||||
"calendar": {
|
||||
"about": "A integração do calendário __appName__ é usada para acessar com segurança o seu calendário para que ele possa ler os próximos eventos.",
|
||||
"disconnect": "Desconectar",
|
||||
"microsoftSignIn": "Entrar com Microsoft",
|
||||
"signedIn": "Atualmente acessando eventos do calendário para __email__. Clique no botão Desconectar abaixo para parar de acessar os eventos da agenda.",
|
||||
"title": "Calendário"
|
||||
},
|
||||
"title": "Configurações",
|
||||
"update": "Atualizar",
|
||||
"name": "Nome",
|
||||
@@ -124,11 +176,15 @@
|
||||
"selectMic": "Microfone",
|
||||
"selectAudioOutput": "Saída de áudio",
|
||||
"followMe": "Todos me seguem",
|
||||
"language": "Idioma",
|
||||
"loggedIn": "Conectado como __name__",
|
||||
"noDevice": "Nenhum",
|
||||
"cameraAndMic": "Câmera e microfone",
|
||||
"moderator": "MODERADOR",
|
||||
"moderator": "Moderador",
|
||||
"more": "Mais",
|
||||
"password": "DEFINIR SENHA",
|
||||
"audioVideo": "ÁUDIO E VÍDEO"
|
||||
"audioVideo": "ÁUDIO E VÍDEO",
|
||||
"devices": "Dispositivos"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Perfil",
|
||||
@@ -187,7 +243,6 @@
|
||||
"focus": "Foco da conferência",
|
||||
"focusFail": "__component__ não disponĩvel - tente em __ms__ seg.",
|
||||
"grantedTo": "Direitos de moderador concedido para __to__!",
|
||||
"grantedToUnknown": "Direitos de moderador concedido para $t(notify.somebody)!",
|
||||
"muted": "Você iniciou uma conversa em mudo.",
|
||||
"mutedTitle": "Você está mudo!",
|
||||
"raisedHand": "Gostaria de falar.",
|
||||
@@ -195,7 +250,11 @@
|
||||
"suboptimalExperienceDescription": "Eer ... temos medo de que sua experiência com o __appName__ não seja tão boa aqui. Estamos procurando maneiras de melhorar isso, mas até lá tente usar um dos <a href='static/recommendedBrowsers.html' target='_blank'> navegadores totalmente compatíveis</a>."
|
||||
},
|
||||
"dialog": {
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": "Transmissão ao vivo"
|
||||
},
|
||||
"allow": "Permitir",
|
||||
"confirm": "Confirmar",
|
||||
"kickMessage": "Ouch! Você foi expulso da reunião!",
|
||||
"popupErrorTitle": "Popup bloqueado",
|
||||
"popupError": "Seu navegador está bloqueando janelas popup deste site. Habilite os popups nas configurações de segurança no seu navegador e tente novamente.",
|
||||
@@ -219,6 +278,7 @@
|
||||
"rejoinNow": "Reconectar agora",
|
||||
"maxUsersLimitReachedTitle": "Limite máximo de membros alcançado",
|
||||
"maxUsersLimitReached": "O limite para o máximo do número de membros foi alcançado. A conferência está cheia. Contate o dono da reunião ou tente de novo depois!",
|
||||
"lockRoom": "Travar sala",
|
||||
"lockTitle": "Bloqueio falhou",
|
||||
"lockMessage": "Falha ao travar a conferência.",
|
||||
"warning": "Atenção",
|
||||
@@ -266,6 +326,7 @@
|
||||
"reservationError": "Erro de sistema de reserva",
|
||||
"reservationErrorMsg": "Código do erro: __code__, mensagem: __msg__",
|
||||
"password": "Insira a senha",
|
||||
"unlockRoom": "Destravar sala",
|
||||
"userPassword": "senha do usuário",
|
||||
"token": "token",
|
||||
"tokenAuthFailedTitle": "Falha de autenticação",
|
||||
@@ -278,11 +339,11 @@
|
||||
"sorryFeedback": "Sentimos muito pelos transtornos. Poderia nos das mais detalhes?",
|
||||
"liveStreaming": "Transmissão ao Vivo",
|
||||
"streamKey": "Chave para transmissão ao vivo",
|
||||
"startLiveStreaming": "Ir ao vivo agora",
|
||||
"startLiveStreaming": "Iniciar transmissão ao vivo",
|
||||
"startRecording": "Parar gravação",
|
||||
"stopStreamingWarning": "Tem certeza que deseja parar a transmissão ao vivo?",
|
||||
"stopRecordingWarning": "Tem certeza que deseja parar a gravação?",
|
||||
"stopLiveStreaming": "Parar a transmissão ao vivo",
|
||||
"stopLiveStreaming": "Parar transmissão ao vivo",
|
||||
"stopRecording": "Parar a gravação",
|
||||
"doNotShowMessageAgain": "Não mostre esta mensagem novamente",
|
||||
"permissionDenied": "Permissão Negada",
|
||||
@@ -312,6 +373,10 @@
|
||||
"muteParticipantTitle": "Silenciar esse membro?",
|
||||
"muteParticipantBody": "Você não está habilitado para tirar o mudo deles, mas eles podem tirar o mudo deles mesmos a qualquer tempo.",
|
||||
"muteParticipantButton": "Mudo",
|
||||
"liveStreamingDisabledTooltip": "Iniciar transmissão ao vivo desativada.",
|
||||
"liveStreamingDisabledForGuestTooltip": "Visitantes não podem iniciar transmissão ao vivo.",
|
||||
"recordingDisabledTooltip": "Iniciar gravação desativada.",
|
||||
"recordingDisabledForGuestTooltip": "Visitantes não podem iniciar gravações.",
|
||||
"remoteControlTitle": "Conexão de área de trabalho remota",
|
||||
"remoteControlRequestMessage": "Deseja permitir que __user__ controle remotamente sua área de trabalho?",
|
||||
"remoteControlShareScreenWarning": "Note que se você pressionar \"Permitir\" você vai compartilhar sua tela!",
|
||||
@@ -322,8 +387,11 @@
|
||||
"remoteControlStopMessage": "A sessão de controle remoto terminou!",
|
||||
"close": "Fechar",
|
||||
"shareYourScreen": "Compartilhar sua tela",
|
||||
"shareYourScreenDisabled": "Compartilhamento de tela desativada.",
|
||||
"shareYourScreenDisabledForGuest": "Visitantes não podem compartilhar tela.",
|
||||
"yourEntireScreen": "Toda sua tela",
|
||||
"applicationWindow": "Janela de aplicativo"
|
||||
"applicationWindow": "Janela de aplicativo",
|
||||
"transcribing": "Transcrevendo"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
@@ -353,6 +421,22 @@
|
||||
],
|
||||
"and": "e"
|
||||
},
|
||||
"share": {
|
||||
"mainText": [
|
||||
"Clique no seguinte link para entrar na reunião:",
|
||||
"__roomUrl__"
|
||||
],
|
||||
"dialInfoText": [
|
||||
"",
|
||||
"",
|
||||
"=====",
|
||||
"",
|
||||
"Quer apenas ligar no seu telefone?",
|
||||
"",
|
||||
"Clique neste link para ver a discagem nos números de telefone para esta reunião",
|
||||
"__dialInfoPageUrl__"
|
||||
]
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Erro",
|
||||
"CONNECTING": "Conectando",
|
||||
@@ -366,18 +450,37 @@
|
||||
"ATTACHED": "Anexado"
|
||||
},
|
||||
"recording": {
|
||||
"beta": "BETA",
|
||||
"busy": "Estamos trabalhando para liberar recursos de gravação. Tente novamente em alguns minutos.",
|
||||
"busyTitle": "Todas as gravações estão atualmente ocupadas",
|
||||
"buttonTooltip": "Iniciar / parar gravação",
|
||||
"error": "A gravação falhou. Tente novamente.",
|
||||
"failedToStart": "Falha ao iniciar a gravação",
|
||||
"live": "AOVIVO",
|
||||
"off": "Gravação parada",
|
||||
"on": "Gravando",
|
||||
"pending": "Aguardando um participante para iniciar a gravação...",
|
||||
"pending": "Preparando para gravar a reunião...",
|
||||
"rec": "GRAVAR",
|
||||
"authDropboxText": "Enviar sua gravação para o Dropbox.",
|
||||
"authDropboxCompletedText": "Seu arquivo de gravação aparecerá no seu Dropbox logo após terminar a gravação.",
|
||||
"serviceName": "Serviço de gravação",
|
||||
"signOut": "Sair",
|
||||
"signIn": "entrar",
|
||||
"loggedIn": "Conectado como __userName__",
|
||||
"availableSpace": "Espaço disponível: __spaceLeft__ MB (aproximadamente __duration__ minutos de gravação)",
|
||||
"startRecordingBody": "Tem certeza que deseja iniciar a gravação?",
|
||||
"unavailable": "Oops! O __serviceName__ está indisponível. Estamos trabalhando para resolver o problema. Por favor, tente mais tarde.",
|
||||
"unavailableTitle": "Gravação indisponível"
|
||||
},
|
||||
"transcribing": {
|
||||
"pending": "Preparando a transcrição da reunião...",
|
||||
"off": "Transcrição parada",
|
||||
"error": "Transcrição falhou. Tente novamente.",
|
||||
"failedToStart": "Transcrição falhou ao iniciar",
|
||||
"tr": "TR",
|
||||
"labelToolTip": "A reunião esta sendo transcrita",
|
||||
"ccButtonTooltip": "Iniciar / Parar de mostrar as legendas"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "Estamos trabalhando para liberar os recursos de transmissão. Tente novamente em alguns minutos.",
|
||||
"busyTitle": "Todas as transmissões estão atualmente ocupadas",
|
||||
@@ -388,6 +491,7 @@
|
||||
"enterStreamKey": "Insira sua chave de transmissão ao vivo do YouTube aqui.",
|
||||
"error": "Falha na transmissão ao vivo. Tente de novo.",
|
||||
"errorAPI": "Ocorreu um erro ao acessar suas transmissões do YouTube. Por favor tente logar novamente.",
|
||||
"errorLiveStreamNotEnabled": "Transmissão ao vivo não está ativada em __email__. Ative a transmissão ao vivo ou registre numa conta com transmissão ao vivo ativada.",
|
||||
"failedToStart": "Falha ao iniciar a transmissão ao vivo",
|
||||
"off": "Transmissão ao vivo encerrada",
|
||||
"on": "Transmissão ao Vivo",
|
||||
@@ -424,9 +528,10 @@
|
||||
"noPermission": "Permissão não concedida",
|
||||
"previewUnavailable": "Visualização indisponível",
|
||||
"selectADevice": "Selecione um dispositivo",
|
||||
"testAudio": "Testar o som"
|
||||
"testAudio": "Tocar um som de teste"
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "AUD",
|
||||
"callQuality": "Qualidade da Chamada",
|
||||
"hd": "HD",
|
||||
"hdTooltip": "Ver vídeo em alta definição",
|
||||
@@ -455,6 +560,7 @@
|
||||
"countryNotSupported": "Ainda não suportamos este destino.",
|
||||
"countryReminder": "Ligando fora dos EUA? Por favor, certifique-se de começar com o código do país!",
|
||||
"disabled": "Você não pode convidar pessoas.",
|
||||
"footerText": "Discagem está desativada.",
|
||||
"invite": "Convidar",
|
||||
"loading": "Procurando por pessoas e números de telefone",
|
||||
"loadingNumber": "Validando o número de telefone",
|
||||
@@ -491,6 +597,7 @@
|
||||
"veryGood": "Muito boa"
|
||||
},
|
||||
"info": {
|
||||
"accessibilityLabel": "Mostrar info",
|
||||
"addPassword": "Adicionar uma senha",
|
||||
"cancelPassword": "Cancelar senha",
|
||||
"conferenceURL": "Link:",
|
||||
@@ -512,7 +619,7 @@
|
||||
"numbers": "Números de discagem",
|
||||
"password": "Senha:",
|
||||
"title": "Compartilhar",
|
||||
"tooltip": "Obtenha informações de acesso sobre a reunião"
|
||||
"tooltip": "Compartilhar link e discagem para esta reunião"
|
||||
},
|
||||
"settingsView": {
|
||||
"alertOk": "OK",
|
||||
@@ -528,17 +635,17 @@
|
||||
"startWithVideoMuted": "Iniciar sem vídeo"
|
||||
},
|
||||
"calendarSync": {
|
||||
"later": "Depois",
|
||||
"next": "Recebendo",
|
||||
"addMeetingURL": "Adicionar um link da reunião",
|
||||
"today": "Hoje",
|
||||
"nextMeeting": "próxima reunião",
|
||||
"now": "Agora",
|
||||
"noEvents": "Não há eventos próximos agendados.",
|
||||
"ongoingMeeting": "reunião em progresso",
|
||||
"permissionButton": "Abrir configurações",
|
||||
"permissionMessage": "Permissão do calendário é requerida para listar suas reuniões na aplicação."
|
||||
"permissionMessage": "Permissão do calendário é requerida para ver suas reuniões na aplicação.",
|
||||
"refresh": "Atualizar calendário"
|
||||
},
|
||||
"recentList": {
|
||||
"today": "Hoje",
|
||||
"yesterday": "Ontem",
|
||||
"earlier": "Mais cedo"
|
||||
"joinPastMeeting": "Entrar em uma reunião passada"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Puxe para atualizar"
|
||||
@@ -551,5 +658,60 @@
|
||||
"appNotInstalled": "Você precisa do aplicativo móvel __app__ para participar da reunião no seu telefone.",
|
||||
"downloadApp": "Baixe o Aplicativo",
|
||||
"openApp": "Continue na aplicação"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"invited": "Convidar",
|
||||
"ringing": "Toque...",
|
||||
"calling": "Chamando...",
|
||||
"initializingCall": "Iniciando Chamada...",
|
||||
"connected": "Conectado",
|
||||
"connecting": "Conectando...",
|
||||
"connecting2": "Conectando*...",
|
||||
"disconnected": "Desconectado",
|
||||
"busy": "Ocupado",
|
||||
"rejected": "Rejeitado",
|
||||
"ignored": "Ignorado",
|
||||
"expired": "Expirado"
|
||||
},
|
||||
"dateUtils": {
|
||||
"today": "Hoje",
|
||||
"yesterday": "Ontem",
|
||||
"earlier": "Mais cedo"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "Responder",
|
||||
"audioCallTitle": "Chamada chegando",
|
||||
"decline": "Dispensar",
|
||||
"productLabel": "do Jitsi Meet",
|
||||
"videoCallTitle": "Chamada de vídeo chegando"
|
||||
},
|
||||
"localRecording": {
|
||||
"localRecording": "Gravação local",
|
||||
"dialogTitle": "Controles da Gravação Local",
|
||||
"start": "Iniciar gravação",
|
||||
"stop": "Parar a Gravação",
|
||||
"moderator": "Moderador",
|
||||
"me": "Eu",
|
||||
"duration": "Duração",
|
||||
"durationNA": "N/A",
|
||||
"encoding": "Codificando",
|
||||
"participantStats": "Estatísticas dos Participantes",
|
||||
"participant": "Participante",
|
||||
"sessionToken": "Token de Sessão",
|
||||
"clientState": {
|
||||
"on": "On",
|
||||
"off": "Off",
|
||||
"unknown": "Desconhecido"
|
||||
},
|
||||
"messages": {
|
||||
"engaged": "Gravação local iniciada.",
|
||||
"finished": "Sessão de gravação __token__ terminada. Por favor, envie o arquivo gravado para o moderador.",
|
||||
"finishedModerator": "Sessão de gravação __token__ terminada. A gravação da faixa local foi salva. Por favor, peça aos outros participantes para enviar suas gravações.",
|
||||
"notModerator": "Você não é o moderador. Você não pode iniciar ou parar a gravação local."
|
||||
},
|
||||
"yes": "Sim",
|
||||
"no": "Não",
|
||||
"label": "LOR",
|
||||
"labelToolTip": "Gravação local está envolvida"
|
||||
}
|
||||
}
|
||||
@@ -1,183 +1,148 @@
|
||||
{
|
||||
"contactlist": "Участники (__pcount__)",
|
||||
"addParticipants": "Поделиться ссылкой",
|
||||
"roomLocked": "Вызывающие должны ввести пароль",
|
||||
"roomUnlocked": "Любой, владеющий ссылкой, может присоединиться",
|
||||
"contactlist_plural": "Участников: __count__",
|
||||
"passwordSetRemotely": "установлен другим участником",
|
||||
"connectionsettings": "Настройки подключения",
|
||||
"poweredby": "работает на",
|
||||
"feedback": {
|
||||
"average": "",
|
||||
"bad": "",
|
||||
"good": "",
|
||||
"rateExperience": "Пожалуйста, оцените ваш опыт встречи.",
|
||||
"veryBad": "",
|
||||
"veryGood": ""
|
||||
},
|
||||
"inviteUrlDefaultMsg": "Ваша конференция создается в данный момент...",
|
||||
"me": "Я",
|
||||
"speaker": "Говорящий",
|
||||
"inviteUrlDefaultMsg": "Подготавливаем вашу встречу...",
|
||||
"me": "я",
|
||||
"speaker": "Колонка",
|
||||
"raisedHand": "Хочет говорить",
|
||||
"defaultNickname": "напр. Яна Цветочкина",
|
||||
"defaultNickname": "напр. Яна Цветкова",
|
||||
"defaultLink": "напр. __url__",
|
||||
"callingName": "__name__",
|
||||
"audioDevices": {
|
||||
"bluetooth": "Bluetooth",
|
||||
"headphones": "Наушники",
|
||||
"phone": "Телефон",
|
||||
"speaker": "Колонка"
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "",
|
||||
"featureToggleDisabled": ""
|
||||
"audioOnly": "Только звук",
|
||||
"featureToggleDisabled": "Переключение функции __feature__ недоступно в режиме \"только звук\""
|
||||
},
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "",
|
||||
"chromeGrantPermissions": "",
|
||||
"androidGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "",
|
||||
"operaGrantPermissions": "",
|
||||
"iexplorerGrantPermissions": "",
|
||||
"safariGrantPermissions": "",
|
||||
"nwjsGrantPermissions": "Пожалуйста дайте разрешение на доступ к камере и микрофону",
|
||||
"edgeGrantPermissions": ""
|
||||
"react-nativeGrantPermissions": "Выберите <b><i>Разрешить</i></b>, когда браузер спросит о разрешениях.",
|
||||
"chromeGrantPermissions": "Выберите <b><i>Разрешить</i></b>, когда браузер спросит о разрешениях.",
|
||||
"androidGrantPermissions": "Выберите <b><i>Разрешить</i></b>, когда браузер спросит о разрешениях.",
|
||||
"electronGrantPermissions": "Пожалуйста, дайте разрешение на доступ к камере и микрофону",
|
||||
"firefoxGrantPermissions": "Выберите <b><i>Поделиться выбранным устройством</i></b>, когда браузер спросит о разрешениях.",
|
||||
"operaGrantPermissions": "Выберите <b><i>Разрешить</i></b>, когда браузер спросит о разрешениях.",
|
||||
"iexplorerGrantPermissions": "Выберите <b><i>OK</i></b>, когда браузер спросит о разрешениях.",
|
||||
"safariGrantPermissions": "Выберите <b><i>OK</i></b>, когда браузер спросит о разрешениях.",
|
||||
"nwjsGrantPermissions": "Пожалуйста, дайте разрешение на доступ к камере и микрофону",
|
||||
"edgeGrantPermissions": "Выберите <b><i>Да</i></b>, когда браузер спросит о разрешениях."
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"keyboardShortcuts": "Комбинации клавиш",
|
||||
"raiseHand": "Поднять или опустить руку",
|
||||
"pushToTalk": "Нажмите, чтобы говорить",
|
||||
"toggleScreensharing": "Переключиться между камерой и совместным использованием экрана",
|
||||
"toggleScreensharing": "Переключиться между камерой и показом экрана",
|
||||
"toggleFilmstrip": "Показать или скрыть видео",
|
||||
"toggleShortcuts": "Показать или скрыть это справочное меню",
|
||||
"focusLocal": "Фокус на ваше видео",
|
||||
"focusRemote": "Фокус на видео другого абонента",
|
||||
"toggleChat": "Открыть или закрыть чат",
|
||||
"mute": "Заглушить или включить микрофон",
|
||||
"fullScreen": "Войти или выйти из полноэкранного режима",
|
||||
"videoMute": "Включить или выключить вашу камеру",
|
||||
"showSpeakerStats": ""
|
||||
"focusRemote": "Фокус на видео другого участника",
|
||||
"toggleChat": "Чат (открыть/закрыть)",
|
||||
"mute": "Микрофон (вкл./выкл.)",
|
||||
"fullScreen": "Полноэкранный режим (вкл./выкл.)",
|
||||
"videoMute": "Камера (вкл./выкл.)",
|
||||
"showSpeakerStats": "Показать статистику выступающего"
|
||||
},
|
||||
"welcomepage": {
|
||||
"disable": "Не показывать эту страницу снова",
|
||||
"feature1": {
|
||||
"content": "Нет нужды что-либо скачивать. __app__ работает прямо из вашего браузера. Просто отправьте URL ссылку на вашу конференцию другим, чтобы начать общение.",
|
||||
"title": "Простой в использовании"
|
||||
"appDescription": "Попробуйте видеочат со всей командой. Приглашайте знакомых! __app__ — полностью зашифрованное решение для видеоконференций с открытым исходным кодом. Пользуйтесь каждый день, бесплатно и без регистрации.",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Календарь",
|
||||
"video": "Видео"
|
||||
},
|
||||
"feature2": {
|
||||
"content": "Многопользовательским видеоконференциям достаточно скорости передачи данных в 128 Кбит/с. Демонстрация экрана или аудиоконференции требуют и того меньше.",
|
||||
"title": "Низкие требования к ширине канала"
|
||||
},
|
||||
"feature3": {
|
||||
"content": "__app__ лицензирован под Apache License. Вы можете свободно скачивать, использовать, изменять это ПО в соответствии с условиями лицензии.",
|
||||
"title": "Исходный код открыт"
|
||||
},
|
||||
"feature4": {
|
||||
"content": "Нет никаких искусственных ограничений по количеству пользовательниц или участников конференций. Вас отграничивают только мощность сервера и качество соединения.",
|
||||
"title": "Количество пользовательниц не ограничено"
|
||||
},
|
||||
"feature5": {
|
||||
"content": "С лёгкостью можно пользоваться экраном совместно. __app__ идеально для онлайн презентаций, лекций и сеансов техподдержки.",
|
||||
"title": "Общий доступ к экрану"
|
||||
},
|
||||
"feature6": {
|
||||
"content": "Нужно больше приватности? __app__ конференц-комнаты могут быть защищены паролем, чтобы исключить незваных гостей или заминки.",
|
||||
"title": "Защищённые комнаты"
|
||||
},
|
||||
"feature7": {
|
||||
"content": "__app__ включает Etherpad, текстовый редактор для совместной работы над текстом в реальном времени, который замечательно подходит, чтобы вести протоколы или совместно писать статьи.",
|
||||
"title": "Поделиться заметками"
|
||||
},
|
||||
"feature8": {
|
||||
"content": "Узнайте больше о пользователях с помощью интеграции с Piwik, Google Analytics и другими системами мониторига и сбора статистики.",
|
||||
"title": "Статистика использования"
|
||||
},
|
||||
"go": "Вперед!",
|
||||
"join": "",
|
||||
"privacy": "",
|
||||
"roomname": "Введите название комнаты",
|
||||
"roomnamePlaceHolder": "",
|
||||
"sendFeedback": "",
|
||||
"terms": ""
|
||||
"calendar": "Календарь",
|
||||
"go": "ОК",
|
||||
"join": "ПРИСОЕДИНИТЬСЯ",
|
||||
"privacy": "Приватность",
|
||||
"roomname": "Укажите название комнаты",
|
||||
"roomnameHint": "Укажите название комнаты или ее адрес. Можете сами создать название и передать его будущим участникам встречи, чтобы они использовали именно его.",
|
||||
"sendFeedback": "Обратная связь",
|
||||
"terms": "Условия",
|
||||
"title": "Видеоконференции. Безопасно. Гибко. Полностью бесплатно"
|
||||
},
|
||||
"\u0005welcomepage": {},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
"title": "__app__ нуждается в использовании вашего микрофона и камеры."
|
||||
"policyText": " ",
|
||||
"title": "__app__ требуется доступ к микрофону и камере."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"title": "",
|
||||
"text": "",
|
||||
"rejoinKeyTitle": "Присоединиться повторно"
|
||||
"title": "Видеосвязь прервана. Причина: этот компьютер перешел в режим сна.",
|
||||
"text": "Для восстановления связи нажмите кнопку <i>Подключиться снова</i>.",
|
||||
"rejoinKeyTitle": "Подключиться снова"
|
||||
},
|
||||
"toolbar": {
|
||||
"addPeople": "",
|
||||
"audioonly": "",
|
||||
"mute": "Вкл. / Выкл. звук",
|
||||
"videomute": "Вкл / Выкл камеру",
|
||||
"addPeople": "Добавить людей к вашему сеансу связи",
|
||||
"audioonly": "Режим \"только звук\" (вкл./выкл.), экономит трафик",
|
||||
"callQuality": "Качество связи",
|
||||
"enterFullScreen": "Полный экран",
|
||||
"exitFullScreen": "Полный экран",
|
||||
"feedback": "Оставить отзыв",
|
||||
"moreActions": "Больше",
|
||||
"mute": "Звук (вкл./выкл.)",
|
||||
"videomute": "Камера",
|
||||
"authenticate": "Аутентифицировать",
|
||||
"lock": "Заблокировать / разблокировать комнату",
|
||||
"invite": "Поделиться ссылкой",
|
||||
"chat": "Открыть / Закрыть чат",
|
||||
"etherpad": "Открыть / Закрыть общий документ",
|
||||
"sharedvideo": "Поделиться YouTube видео",
|
||||
"sharescreen": "Начать / Завершить совместное использование экрана",
|
||||
"fullscreen": "Вкл / Выкл полноэкранный режим",
|
||||
"sip": "Набрать SIP номер",
|
||||
"lock": "Блокировка комнаты",
|
||||
"chat": "Чат",
|
||||
"etherpad": "Общий документ",
|
||||
"documentOpen": "Открыть общий документ",
|
||||
"documentClose": "Закрыть общий документ",
|
||||
"sharedvideo": "Видео YouTube",
|
||||
"sharescreen": "Показ экрана",
|
||||
"stopSharedVideo": "Остановить видео на YouTube",
|
||||
"fullscreen": "Полноэкранный режим (вкл./выкл.)",
|
||||
"sip": "Набрать номер SIP",
|
||||
"Settings": "Настройки",
|
||||
"hangup": "Покинуть",
|
||||
"hangup": "Выход",
|
||||
"login": "Войти",
|
||||
"logout": "Завершить сеанс",
|
||||
"dialpad": "Открыть / Закрыть клавиатуру для набора номера",
|
||||
"sharedVideoMutedPopup": "У видео, которым Вы поделились, отключён звук, чтобы вы могли говорить с остальными.",
|
||||
"micMutedPopup": "Ваш микрофон отключён, чтобы вы могли сосредоточиться на видео, которым поделились.",
|
||||
"talkWhileMutedPopup": "Пытаетесь говорить? Вы приглушены.",
|
||||
"sharedVideoMutedPopup": "Звук видео, которым вы делитесь, отключен, чтобы вы могли общаться с другими участниками.",
|
||||
"micMutedPopup": "Ваш микрофон отключен, чтобы вы могли слышать звук вашего видео.",
|
||||
"talkWhileMutedPopup": "Пытаетесь говорить? У вас отключен звук.",
|
||||
"unableToUnmutePopup": "Вы не можете включить звук, потому что включено видео.",
|
||||
"cameraDisabled": "Камера недоступна",
|
||||
"micDisabled": "Микрофон недоступен",
|
||||
"filmstrip": "Показать / Скрыть видео",
|
||||
"profile": "Редактировать ваш профиль",
|
||||
"raiseHand": "Поднять / Опустить вашу руку"
|
||||
},
|
||||
"unsupportedBrowser": {
|
||||
"appInstalled": "",
|
||||
"appNotInstalled": "",
|
||||
"downloadApp": "",
|
||||
"joinConversation": "",
|
||||
"startConference": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Открыть / Закрыть чат",
|
||||
"filmstrip": "Показать / Скрыть видео",
|
||||
"contactlist": "Просмотреть и пригласить участников"
|
||||
"filmstrip": "Видео (показать/скрыть)",
|
||||
"profile": "Редактировать профиль",
|
||||
"raiseHand": "Хочу говорить",
|
||||
"shortcuts": "Комбинации клавиш",
|
||||
"speakerStats": "Статистика"
|
||||
},
|
||||
"\u0005toolbar": {},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Введите имя в поле ниже",
|
||||
"popover": "Выберите имя"
|
||||
},
|
||||
"messagebox": "Введите текст.."
|
||||
"messagebox": "Пишите..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки",
|
||||
"update": "Обновить",
|
||||
"name": "Имя",
|
||||
"startAudioMuted": "Каждый начинает глушиться",
|
||||
"startVideoMuted": "Все начинают скрываться",
|
||||
"startAudioMuted": "Все начинают с выключенным звуком",
|
||||
"startVideoMuted": "Все начинают в скрытом режиме",
|
||||
"selectCamera": "Камера",
|
||||
"selectMic": "Микрофон",
|
||||
"selectAudioOutput": "Звуковой выход",
|
||||
"followMe": "Каждый следует за мной",
|
||||
"noDevice": "Нет",
|
||||
"followMe": "Все следуют за мной",
|
||||
"noDevice": "Все как я",
|
||||
"cameraAndMic": "Камера и микрофон",
|
||||
"moderator": "МОДЕРАТОР",
|
||||
"password": "УСТАНОВИТЬ ПАРОЛЬ",
|
||||
"audioVideo": "АУДИО И ВИДЕО"
|
||||
"audioVideo": "ЗВУК И ВИДЕО"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Профиль",
|
||||
"setDisplayNameLabel": "Установить ваше отображаемое имя",
|
||||
"setEmailLabel": "Установить электронную почту для gravatar",
|
||||
"setEmailInput": "Введите электронную почту"
|
||||
"setDisplayNameLabel": "Отображаемое имя",
|
||||
"setEmailLabel": "E-mail для gravatar",
|
||||
"setEmailInput": "Введите e-mail"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Нажми, чтобы<br/>поменять имя экрана",
|
||||
"moderator": "Хозяйка конференции.",
|
||||
"videomute": "Участник<br/>остановил камеру",
|
||||
"mute": "Без звука",
|
||||
"kick": "Прогнать",
|
||||
"moderator": "Модератор",
|
||||
"videomute": "Участник отключил камеру",
|
||||
"mute": "Участник отключил звук",
|
||||
"kick": "Выкинуть",
|
||||
"muted": "Звук выключен",
|
||||
"domute": "Выключить звук",
|
||||
"flip": "Отразить",
|
||||
@@ -185,208 +150,221 @@
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "Данные соединения",
|
||||
"bitrate": "Битрейт",
|
||||
"packetloss": "Потеря пакетов:",
|
||||
"bitrate": "Битрейт:",
|
||||
"packetloss": "Потери пакетов:",
|
||||
"resolution": "Разрешение:",
|
||||
"framerate": "",
|
||||
"less": "Свернуть",
|
||||
"more": "Показать больше",
|
||||
"framerate": "Частота кадров:",
|
||||
"less": "Меньше",
|
||||
"more": "Больше",
|
||||
"address": "Адрес:",
|
||||
"remoteport": "Удалённый порт:",
|
||||
"remoteport_plural_2": "",
|
||||
"remoteport_plural_5": "",
|
||||
"localport": "Локальный порт:",
|
||||
"remoteport": "Удаленные порты:",
|
||||
"remoteport_plural_2": "Удаленные порты:",
|
||||
"remoteport_plural_5": "Удаленные порты:",
|
||||
"localport": "Локальные порты:",
|
||||
"localport_plural_2": "Локальные порты:",
|
||||
"localport_plural_5": "",
|
||||
"localaddress": "Локальный адрес:",
|
||||
"localport_plural_5": "Локальные порты:",
|
||||
"localaddress": "Локальные адреса:",
|
||||
"localaddress_plural_2": "Локальные адреса:",
|
||||
"localaddress_plural_5": "",
|
||||
"remoteaddress": "Удалённый адрес:",
|
||||
"remoteaddress_plural_2": "",
|
||||
"remoteaddress_plural_5": "",
|
||||
"transport": "Метод отправки:",
|
||||
"bandwidth": "Средняя скорость соединения:",
|
||||
"na": "Вернитесь сюда за информацией о соединении, когда конференция начнётся",
|
||||
"turn": ""
|
||||
"localaddress_plural_5": "Локальные адреса:",
|
||||
"remoteaddress": "Удаленные адреса:",
|
||||
"remoteaddress_plural_2": "Удаленные адреса:",
|
||||
"remoteaddress_plural_5": "Удаленные адреса:",
|
||||
"transport": "Методы отправки:",
|
||||
"transport_plural_2": "Методы отправки:",
|
||||
"transport_plural_5": "Методы отправки:",
|
||||
"bandwidth": "Средняя скорость:",
|
||||
"na": "Во время беседы вы можете следить за данными о соединении",
|
||||
"turn": " (повернуть)",
|
||||
"quality": {
|
||||
"good": "Хорошо",
|
||||
"inactive": "не активно",
|
||||
"lost": "потеряно",
|
||||
"nonoptimal": "не оптимально",
|
||||
"poor": "плохо"
|
||||
},
|
||||
"status": "Связь:"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "соединение разорвано",
|
||||
"moderator": "Получены права для модерации!",
|
||||
"connected": "подключено",
|
||||
"moderator": "Получены права модератора!",
|
||||
"connectedOneMember": "__name__ подключен",
|
||||
"connectedTwoMembers": "__first__ и __second__ подключены",
|
||||
"connectedThreePlusMembers": "__name__ и другие (__count__) подключены",
|
||||
"somebody": "Кто-то",
|
||||
"me": "Я",
|
||||
"focus": "Фокусировка конференции",
|
||||
"focusFail": "__component__ недоступен - повторите через __ms__ секунд",
|
||||
"grantedTo": "Теперь модерирует __to__!",
|
||||
"grantedToUnknown": "",
|
||||
"muted": "Вы начали конференцию без звука.",
|
||||
"focus": "Фокус встречи",
|
||||
"focusFail": "__component__ недоступен, повторите через __ms__ с",
|
||||
"grantedTo": "__to__ получил права модератора!",
|
||||
"grantedToUnknown": "К сожалению, что-то пошло не так.",
|
||||
"muted": "Вы начали разговор без звука.",
|
||||
"mutedTitle": "Вы без звука!",
|
||||
"raisedHand": "Хочу высказаться."
|
||||
"raisedHand": "Хочет говорить.",
|
||||
"suboptimalExperienceTitle": "К сожалению, этот браузер может не подойти для работы с __appName__. Мы работаем над проблемой, а пока попробуйте один из <a href='static/recommendedBrowsers.html 'target='_blank'>полностью поддерживаемых браузеров</a>.",
|
||||
"suboptimalExperienceDescription": "К сожалению, этот браузер не очень подходит для работы с __appName__. Мы работаем над проблемой, а пока попробуйте один из <a href='static/recommendedBrowsers.html 'target='_blank'>полностью поддерживаемых браузеров</a>."
|
||||
},
|
||||
"dialog": {
|
||||
"add": "Добавить",
|
||||
"allow": "",
|
||||
"kickMessage": "Фигасе! Вас прогнали со встречи!",
|
||||
"popupError": "Ваш браузер блокирует всплывающие окна на этом сайте. Пожалуйста разрешите всплывающие окна в настройках безопасности и попробуйте снова.",
|
||||
"allow": "Разрешить",
|
||||
"kickMessage": "Вас выкинули из комнаты!",
|
||||
"popupErrorTitle": "Заблокировано всплывающее окно",
|
||||
"popupError": "Ваш браузер блокирует всплывающие окна этого сайта. Пожалуйста, разрешите всплывающие окна в настройках безопасности браузера и попробуйте снова.",
|
||||
"passwordErrorTitle": "Ошибка пароля",
|
||||
"passwordError": "Этот разговор сейчас защищён паролем. Только хозяйка конференции может устанавливать пароль.",
|
||||
"passwordError2": "Эта конференция защищена паролем. Только хозяйка конференции может устанавливать пароль.",
|
||||
"connectError": "Ёпрст! Что-то пошло не так и мы не можем связаться с конференцией.",
|
||||
"connectErrorWithMsg": "Ёпрст! Что-то пошло не так и мы не можем связаться с конференцией: __msg__",
|
||||
"incorrectPassword": "Неверный пароль",
|
||||
"connecting": "Идёт подключение",
|
||||
"passwordError": "Эта встреча защищена паролем. Только организатор встречи может устанавливать пароль.",
|
||||
"passwordError2": "Эта встреча не защищена паролем. Только организатор встречи может устанавливать пароль.",
|
||||
"connectError": "Ошибка. Невозможно установить связь для вашей встречи.",
|
||||
"connectErrorWithMsg": "Ошибка. Невозможно установить связь для вашей встречи: __msg__",
|
||||
"incorrectPassword": "Ошибка имени пользователя или пароля",
|
||||
"connecting": "Подключение",
|
||||
"copy": "Копировать",
|
||||
"contactSupport": "Связь с поддержкой",
|
||||
"error": "Ошибка",
|
||||
"createPassword": "Создать пароль",
|
||||
"detectext": "Ошибка при попытке определить расширение для совместного использования экрана.",
|
||||
"failtoinstall": "Невозможно установить расширение для совместного использования рабочего стола",
|
||||
"failedpermissions": "Невозможно получить права на использование локального микрофона и/или камеры.",
|
||||
"conferenceReloadTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"rejoinNow": "",
|
||||
"maxUsersLimitReached": "Достигнут максимум количества участников конференции. Конференция заполнена. Пожалуйста попробуйте позже!",
|
||||
"detectext": "Ошибка связи с расширением для показа экрана.",
|
||||
"failedpermissions": "Ошибка доступа к локальному микрофону и/или камере.",
|
||||
"conferenceReloadTitle": "К сожалению, что-то пошло не так.",
|
||||
"conferenceReloadMsg": "Мы стараемся это исправить. Восстановление связи через __seconds__ с.",
|
||||
"conferenceDisconnectTitle": "Вы отключены.",
|
||||
"conferenceDisconnectMsg": "Следует проверить интернет-соединение. Попытка восстановления связи через __seconds__ с.",
|
||||
"dismiss": "Отклонить",
|
||||
"rejoinNow": "Подключиться снова",
|
||||
"maxUsersLimitReachedTitle": "Достигнут лимит числа участников",
|
||||
"maxUsersLimitReached": "Достигнуто максимальное число участников. Больше нельзя. Пожалуйста, свяжитесь с организатором встречи или попробуйте позже!",
|
||||
"lockTitle": "Блокировка не удалась",
|
||||
"lockMessage": "Не удалось запереть конференцию",
|
||||
"warning": "Внимание",
|
||||
"passwordNotSupported": "Пароли для комнат сейчас не поддерживаются.",
|
||||
"passwordNotSupportedTitle": "Пароль не поддержвается",
|
||||
"passwordNotSupported": "Установка пароля не поддерживается.",
|
||||
"internalErrorTitle": "Внутренняя ошибка",
|
||||
"internalError": "Ой! Что-то пошло не так. Возникла следующая ошибка: [setRemoteDescription]",
|
||||
"unableToSwitch": "Невозможно сменить видео трансляцию.",
|
||||
"SLDFailure": "Ёпрст! Что-то пошло не так и мы не можем отключить звук! (ошибка SLD)",
|
||||
"SRDFailure": "Ёпрст! Что-то пошло не так и мы не можем остановить видео! (ошибка SRD)",
|
||||
"oops": "Ёпрст!",
|
||||
"currentPassword": "Текущим паролем является",
|
||||
"internalError": "Что-то пошло не так. Ошибка: __error__",
|
||||
"unableToSwitch": "Невозможно сменить трансляцию видео.",
|
||||
"SLDFailure": "Что-то пошло не так, невозможно выключить звук! (ошибка SLD)",
|
||||
"SRDFailure": "Что-то пошло не так, невозможно выключить видео! (ошибка SRD)",
|
||||
"oops": "Ой!",
|
||||
"currentPassword": "Текущий пароль",
|
||||
"passwordLabel": "Пароль",
|
||||
"defaultError": "Какая-то ошибка",
|
||||
"defaultError": "Произошла ошибка",
|
||||
"passwordRequired": "Требуется пароль",
|
||||
"Ok": "Ok",
|
||||
"done": "Готово",
|
||||
"Remove": "Удалить",
|
||||
"removePassword": "Удалить пароль",
|
||||
"shareVideoTitle": "Поделиться видео",
|
||||
"shareVideoLinkError": "Пожалуйста введите корректную youtube ссылку.",
|
||||
"removeSharedVideoTitle": "Удалить общее видео",
|
||||
"removeSharedVideoMsg": "Вы уверрены, что хотите удалить ваше расшаренное видео?",
|
||||
"alreadySharedVideoMsg": "Другая участница сейчас делится видео. В этой конференции можно делиться только одним видео одновременно.",
|
||||
"WaitingForHost": "Ожидание хоста...",
|
||||
"WaitForHostMsg": "Конференция <b>__room__ </b> ещё не началась. Если вы её хост - аутентифицируйтесь. Или сидите ждите хоста.",
|
||||
"IamHost": "Я хост",
|
||||
"Cancel": "Отменить",
|
||||
"Submit": "Принять",
|
||||
"shareVideoLinkError": "Пожалуйста, укажите корректную ссылку Youtube.",
|
||||
"removeSharedVideoTitle": "Убрать видео",
|
||||
"removeSharedVideoMsg": "Уверены, что хотите убрать видео, которым поделились?",
|
||||
"alreadySharedVideoMsg": "Другой участник уже показывает свое видео. Нельзя показывать два видео одновременно.",
|
||||
"alreadySharedVideoTitle": "Допускается показ только одного видео",
|
||||
"WaitingForHost": "Ждем организатора...",
|
||||
"WaitForHostMsg": "Встреча <b>__room__ </b> еще не началась. Если вы организатор встречи, пожалуйста, представьтесь. Если нет, подождите организатора.",
|
||||
"IamHost": "Я организатор",
|
||||
"Cancel": "Отмена",
|
||||
"Submit": "ОК",
|
||||
"retry": "Повторить",
|
||||
"logoutTitle": "Завершить сеанс",
|
||||
"logoutQuestion": "Вы уверены, что хотите выйти и остановить конференцию?",
|
||||
"sessTerminated": "Сеанс закрыт",
|
||||
"logoutQuestion": "Уверены, что хотите выйти и остановить встречу?",
|
||||
"sessTerminated": "Связь прервана",
|
||||
"hungUp": "Вы повесили трубку",
|
||||
"joinAgain": "Войдите заново",
|
||||
"joinAgain": "Войти снова",
|
||||
"Share": "Поделиться",
|
||||
"Save": "Сохранить",
|
||||
"recording": "Запись",
|
||||
"recordingToken": "Введите токен для записи",
|
||||
"passwordCheck": "Вы уверены, что хотите удалить ваш пароль?",
|
||||
"passwordMsg": "Введите пароль для вашей комнаты",
|
||||
"shareLink": "Поделитесь ссылкой на звонок",
|
||||
"settings1": "Настройка Вашей конференции",
|
||||
"settings2": "Участница подключилась без звука",
|
||||
"settings3": "Нужны имена<br/><br/>Установите пароль, чтобы запереть Вашу комнату:",
|
||||
"yourPassword": "Введите новый пароль",
|
||||
"Back": "Назад",
|
||||
"serviceUnavailable": "Служба недоступна",
|
||||
"gracefulShutdown": "Сервис закрыт на переучёт. Пожалуйста попробуйте позже.",
|
||||
"gracefulShutdown": "Технические работы. Пожалуйста, попробуйте позже.",
|
||||
"Yes": "Да",
|
||||
"reservationError": "Ошибка системы резервации",
|
||||
"reservationError": "Ошибка системы резервирования",
|
||||
"reservationErrorMsg": "Код ошибки: __code__, сообщение: __msg__",
|
||||
"password": "Введите пароль",
|
||||
"userPassword": "пароль пользователя",
|
||||
"token": "токен",
|
||||
"tokenAuthFailedTitle": "Ошибка аутентификации",
|
||||
"tokenAuthFailed": "Извините, вам не разрешено присоединиться к этому звонку.",
|
||||
"displayNameRequired": "Требуется отображаемое имя",
|
||||
"enterDisplayName": "Пожалуйста, введите Ваше имя экрана",
|
||||
"extensionRequired": "Требуется расширение:",
|
||||
"firefoxExtensionPrompt": "Нужно установить расширение Firefox, чтобы совместно пользоваться экраном. Попробуйте позже, скачав его <a href='__url__'>отсюда</a>!",
|
||||
"feedbackHelp": "Ваша поддержка поможет нам улучшить опыт видео.",
|
||||
"feedbackQuestion": "Расскажите нам о вашем звонке!",
|
||||
"thankYou": "Спасибо за использование __appName__!",
|
||||
"sorryFeedback": "Мы удручены услышанным. Может расскажете поподробнее?",
|
||||
"tokenAuthFailed": "Извините, вам не разрешено присоединиться к этому сеансу связи.",
|
||||
"displayNameRequired": "Требуется имя",
|
||||
"enterDisplayName": "Пожалуйста, введите имя",
|
||||
"feedbackHelp": "Ваш отзыв поможет нам улучшать параметры видеосвязи.",
|
||||
"feedbackQuestion": "Расскажите, как вам понравилась связь!",
|
||||
"thankYou": "Спасибо, что используете __appName__!",
|
||||
"sorryFeedback": "Жаль. Может, расскажете подробнее?",
|
||||
"liveStreaming": "Трансляция",
|
||||
"streamKey": "Имя/ключ трансляции",
|
||||
"streamKey": "Ключ трансляции",
|
||||
"startLiveStreaming": "Начать трансляцию",
|
||||
"stopStreamingWarning": "Вы уверены, что хотите остановить трансляцию?",
|
||||
"stopRecordingWarning": "Вы уверены, что хотите остановить запись?",
|
||||
"startRecording": "Начать запись",
|
||||
"stopStreamingWarning": "Уверены, что хотите остановить трансляцию?",
|
||||
"stopRecordingWarning": "Уверены, что хотите остановить запись?",
|
||||
"stopLiveStreaming": "Остановить трансляцию",
|
||||
"stopRecording": "Остановить запись",
|
||||
"doNotShowWarningAgain": "Больше не показывать это предупреждение",
|
||||
"doNotShowMessageAgain": "Не показывать больше это сообщение",
|
||||
"permissionDenied": "Доступ запрещён",
|
||||
"screenSharingPermissionDeniedError": "У Вас нет прав совместно использовать Ваш экран",
|
||||
"micErrorPresent": "Произошла ошибка при подключении к Вашему микрофону",
|
||||
"cameraErrorPresent": "Произошла ошибка при подключении к Вашей камере",
|
||||
"cameraUnsupportedResolutionError": "Ваша камера не поддерживает необходимое разрешение.",
|
||||
"cameraUnknownError": "Не могу использовать камеру по неизвестной причине.",
|
||||
"cameraPermissionDeniedError": "У вас нет прав на использование камеры. Вы можете участвовать в конференции, но другие не будут Вас видеть. Используйте значок с камерой в строке адреса, чтобы устранить проблему.",
|
||||
"cameraNotFoundError": "Камера не была найдена.",
|
||||
"cameraConstraintFailedError": "",
|
||||
"micUnknownError": "Не могу пользоваться микрофоном по непонятным причинам.",
|
||||
"micPermissionDeniedError": "Вы не дали прав на использование микрофона. Вы все-равно можете присоединиться к конференции, но никто не будет Вас слышать. Используйте иконку с камерой в адресной строке браузера, чтобы исправить это.",
|
||||
"micNotFoundError": "Микрофон не был найден.",
|
||||
"micConstraintFailedError": "",
|
||||
"micNotSendingData": "Мы не можем получить доступ к вашему микрофону. Пожалуйста, выберите другое устройство из меню настроек или попробуйте перезапустить приложение.",
|
||||
"cameraNotSendingData": "Мы не можем получить доступ к вашей камере. Пожалуйста, проверьте, используется ли это устройство другим приложением, выберите другое устройство из меню настроек или же перезапустите приложение.",
|
||||
"doNotShowMessageAgain": "Больше не показывать это сообщение",
|
||||
"permissionDenied": "Доступ запрещен",
|
||||
"screenSharingFailedToInstall": "Ошибка установки расширения для показа экрана.",
|
||||
"screenSharingFailedToInstallTitle": "Расширение для показа экрана не установлено",
|
||||
"screenSharingFirefoxPermissionDeniedError": "Что-то пошло не так, когда мы пытались поделиться вашим экраном. Пожалуйста, убедитесь, что вы дали нам разрешение на это.",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "Ошибка показа экрана!",
|
||||
"screenSharingPermissionDeniedError": "Ошибка доступа к вашему расширению для показа экрана. Пожалуйста, перезапустите браузер и попробуйте снова.",
|
||||
"cameraUnsupportedResolutionError": "Ваша камера не поддерживает необходимое разрешение видео.",
|
||||
"cameraUnknownError": "Неизвестная ошибка использования камеры.",
|
||||
"cameraPermissionDeniedError": "Нет доступа к камере. Вы можете участвовать во встрече, но другие не будут вас видеть. Используйте значок камеры в адресной строке браузера, чтобы устранить проблему.",
|
||||
"cameraNotFoundError": "Камера не обнаружена.",
|
||||
"cameraConstraintFailedError": "Камера не отвечает определенным требованиям.",
|
||||
"micUnknownError": "Неизвестная ошибка использования микрофона.",
|
||||
"micPermissionDeniedError": "Нет доступа к микрофону. Вы можете участвовать во встрече, но другие не будут вас слышать. Используйте значок камеры в адресной строке браузера, чтобы устранить проблему.",
|
||||
"micNotFoundError": "Микрофон не обнаружен.",
|
||||
"micConstraintFailedError": "Ваш микрофон не отвечает определенным требованиям.",
|
||||
"micNotSendingDataTitle": "Нет доступа к микрофону",
|
||||
"micNotSendingData": "Ошибка доступа к микрофону. Пожалуйста, выберите другое устройство из меню настроек или попробуйте перезапустить приложение.",
|
||||
"cameraNotSendingDataTitle": "Нет доступа к камере",
|
||||
"cameraNotSendingData": "Ошибка доступа к камере. Пожалуйста, проверьте, не использует ли камеру какая-нибудь другая программа. Вы можете также выбрать другое устройство из меню настроек или попробовать перезапустить приложение.",
|
||||
"goToStore": "Перейти к интернет-магазину",
|
||||
"externalInstallationTitle": "Требуется расширение",
|
||||
"externalInstallationMsg": "Вам необходимо установить наше дополнение для совместного использования рабочего стола.",
|
||||
"inlineInstallationMsg": "Вам необходимо установить наше дополнение для совместного использования рабочего стола.",
|
||||
"inlineInstallExtension": "",
|
||||
"muteParticipantTitle": "Приглушить этого участника?",
|
||||
"muteParticipantBody": "Вы не сможете перестать глушить их, но они могут сделать это сами в любое время.",
|
||||
"inlineInstallExtension": "Установить",
|
||||
"muteParticipantTitle": "Выключить звук этому участнику?",
|
||||
"muteParticipantBody": "Вы не можете включить им звук, но они могут сделать это сами в любое время.",
|
||||
"muteParticipantButton": "Выключить звук",
|
||||
"remoteControlTitle": "",
|
||||
"remoteControlRequestMessage": "",
|
||||
"remoteControlShareScreenWarning": "",
|
||||
"remoteControlDeniedMessage": "__user__ отклонил ваш запрос на дистанционное управление!",
|
||||
"remoteControlAllowedMessage": "__user__ принял ваш запрос на дистанционное управление!",
|
||||
"remoteControlErrorMessage": "Произошла ошибка при попытке запросить разрешения удалённого управления от __user__!",
|
||||
"startRemoteControlErrorMessage": "",
|
||||
"remoteControlStopMessage": "Сессия дистанционного управления завершена!",
|
||||
"close": "",
|
||||
"shareYourScreen": "",
|
||||
"yourEntireScreen": "",
|
||||
"applicationWindow": ""
|
||||
"remoteControlTitle": "Удаленное управление рабочим столом",
|
||||
"remoteControlRequestMessage": "Разрешить __user__ удаленное управление вашим рабочим столом?",
|
||||
"remoteControlShareScreenWarning": "Если нажмете \"Разрешить\", то поделитесь своим экраном!",
|
||||
"remoteControlDeniedMessage": "__user__ отклонил ваш запрос на удаленное управление!",
|
||||
"remoteControlAllowedMessage": "__user__ принял ваш запрос на удаленное управление!",
|
||||
"remoteControlErrorMessage": "Произошла ошибка при попытке запросить разрешения удаленного управления от __user__.",
|
||||
"startRemoteControlErrorMessage": "Ошибка начала сессии удаленного управления!",
|
||||
"remoteControlStopMessage": "Сессия удаленного управления завершена!",
|
||||
"close": "Закрыть",
|
||||
"shareYourScreen": "Показать экран",
|
||||
"yourEntireScreen": "Весь экран",
|
||||
"applicationWindow": "Окно приложения"
|
||||
},
|
||||
"\u0005dialog": {},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Эта конференция защищена паролем. Пожалуйста, используйте это пин для входа:",
|
||||
"Эта встреча защищена паролем. Пожалуйста, используйте для входа:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Приглашение для __appName__ (__conferenceName__)",
|
||||
"subject": "Приглашение в __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Привет! я бы хотел пригласить тебя на __appName__ конференцию, которую мы как раз начали.",
|
||||
"Здравствуйте! Приглашаю вас на текущую встречу __appName__.",
|
||||
"",
|
||||
"",
|
||||
"Пожелуста, следуй по ссылке, чтобы подключиться к конференции.",
|
||||
"Для подключения, пожалуйста, используйте ссылку:",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
"Имей в виду, что __appName__ сейчас поддерживается только __supportedBrowsers__, так что полюзуйся одним из этих браузеров.",
|
||||
"Имейте в виду, что __appName__ в настоящее время поддерживается только в __supportedBrowsers__. Вам понадобится один из этих браузеров.",
|
||||
"",
|
||||
"",
|
||||
"Услышимся через секунду!"
|
||||
"Присоединяйтесь!"
|
||||
],
|
||||
"and": "и"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Ошибка",
|
||||
"CONNECTING": "Идёт подключение",
|
||||
"CONNECTING": "Подключение",
|
||||
"RECONNECTING": "Проблема с сетью. Переподключение...",
|
||||
"CONNFAIL": "Сбой подключения",
|
||||
"AUTHENTICATING": "Аутентификация",
|
||||
@@ -397,89 +375,190 @@
|
||||
"ATTACHED": "Прикреплено"
|
||||
},
|
||||
"recording": {
|
||||
"pending": "Записываем ожидаем подключение участницы...",
|
||||
"on": "Запись",
|
||||
"busy": "Мы стараемся обеспечить больше ресурсов для записи. Пожалуйста, попробуйте через несколько минут.",
|
||||
"busyTitle": "Все записывающие устройства заняты",
|
||||
"buttonTooltip": "Запись (старт/стоп)",
|
||||
"error": "Ошибка записи. Пожалуйста, попробуйте позже.",
|
||||
"failedToStart": "Ошибка начала записи",
|
||||
"off": "Запись остановлена",
|
||||
"failedToStart": "Ошибка при начале записи",
|
||||
"buttonTooltip": "Начать / Остановить запись",
|
||||
"error": "Ошибка записи. Попробуйте позже.",
|
||||
"unavailable": "Сервис записи сейчас недоступен. Попробуйте позже."
|
||||
"on": "Запись",
|
||||
"pending": "Запись приостановлена: ожидаем подключение участника...",
|
||||
"serviceName": "Служба записи",
|
||||
"unavailable": "Служба __serviceName__ сейчас недоступна. Мы работаем над исправлением этой ошибки. Пожалуйста, попробуйте позже.",
|
||||
"unavailableTitle": "Запись невозможна"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"pending": "Начинаю трансляцию...",
|
||||
"on": "Трансляция",
|
||||
"busy": "Освобождаем новые ресурсы для трансляции. Пожалуйста, попробуйте снова через несколько минут.",
|
||||
"busyTitle": "Все ресурсы для трансляции уже задействованы",
|
||||
"buttonTooltip": "Трансляция (старт/стоп)",
|
||||
"changeSignIn": "Переключить аккаунты.",
|
||||
"choose": "Выбрать трансляцию",
|
||||
"chooseCTA": "Выберите трансляцию. Вы вошли в систему как __email__. ",
|
||||
"enterStreamKey": "Введите ваш ключ трансляции YouTube.",
|
||||
"error": "Ошибка трансляции. Пожалуйста, попробуйте снова.",
|
||||
"errorAPI": "Произошла ошибка при доступе к вашим трансляциям на YouTube. Повторите попытку входа в систему.",
|
||||
"failedToStart": "Ошибка трансляции видео",
|
||||
"off": "Трансляция остановлена",
|
||||
"unavailable": "Служба трансляций сейчас недоступна. Попробуйте позже.",
|
||||
"failedToStart": "Трансляция видео не может быть начата",
|
||||
"buttonTooltip": "Начать / Остановить прямую трансляцию",
|
||||
"streamIdRequired": "Пожалуйста введите идентификатор трансляции, чтобы запустить её.",
|
||||
"streamIdHelp": "Где я могу найти это?",
|
||||
"error": "Не удалось начать трансляцию. Попробуйте снова.",
|
||||
"busy": "Все рекордеры сейчас заняты. Попробуйте позже."
|
||||
"on": "Трансляция",
|
||||
"pending": "Начинаем трансляцию...",
|
||||
"serviceName": "Служба трансляции",
|
||||
"signIn": "Войти через Google",
|
||||
"signInCTA": "Войдите или введите свой ключ трансляции YouTube.",
|
||||
"start": "Начать трансляцию",
|
||||
"streamIdHelp": "Что это?",
|
||||
"unavailableTitle": "Трансляция недоступна"
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "Мы работаем над высвобождением ресурсов. Пожалуйста, попробуйте через несколько минут.",
|
||||
"busyTitle": "Служба сейчас занята",
|
||||
"errorInvite": "Встреча еще не началась. Пожалуйста, попробуйте позже.",
|
||||
"errorInviteTitle": "Ошибка приглашения в комнату",
|
||||
"errorAlreadyInvited": "__displayName__ уже приглашен",
|
||||
"errorInviteFailedTitle": "Ошибка приглашения __displayName__",
|
||||
"errorInviteFailed": "Мы работаем над решением проблемы. Пожалуйста, попробуйте позже.",
|
||||
"pending": "__displayName__ был приглашен",
|
||||
"serviceName": "Служба комнат",
|
||||
"unavailableTitle": "Служба недоступна"
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"hours": "__count__ч",
|
||||
"minutes": "__count__м",
|
||||
"name": "Имя",
|
||||
"seconds": "",
|
||||
"speakerStats": "",
|
||||
"speakerTime": ""
|
||||
"seconds": "__count__с",
|
||||
"speakerStats": "Статистика выступлений",
|
||||
"speakerTime": "Время выступлений"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "",
|
||||
"noPermission": "",
|
||||
"previewUnavailable": "",
|
||||
"selectADevice": "",
|
||||
"testAudio": ""
|
||||
},
|
||||
"invite": {
|
||||
"addPassword": "",
|
||||
"callNumber": "",
|
||||
"enterID": "",
|
||||
"howToDialIn": "",
|
||||
"hidePassword": "",
|
||||
"inviteTo": "",
|
||||
"invitedYouTo": "",
|
||||
"locked": "",
|
||||
"showPassword": "",
|
||||
"unlocked": ""
|
||||
"deviceSettings": "Настройки устройства",
|
||||
"noPermission": "Нет доступа",
|
||||
"previewUnavailable": "Предпросмотр недоступен",
|
||||
"selectADevice": "Выбор устройства",
|
||||
"testAudio": "Тест звука"
|
||||
},
|
||||
"videoStatus": {
|
||||
"callQuality": "",
|
||||
"changeVideoTip": "",
|
||||
"hd": "",
|
||||
"highDefinition": "",
|
||||
"ld": "",
|
||||
"lowDefinition": "",
|
||||
"p2pEnabled": "",
|
||||
"p2pVideoQualityDescription": "",
|
||||
"recHighDefinitionOnly": "",
|
||||
"sd": "",
|
||||
"standardDefinition": "",
|
||||
"qualityButtonTip": ""
|
||||
"callQuality": "Качество связи",
|
||||
"hd": "HD",
|
||||
"hdTooltip": "Видео высокого качества",
|
||||
"highDefinition": "Высокое качество",
|
||||
"labelTooltipAudioOnly": "Включен режим \"только звук\"",
|
||||
"labelTooiltipNoVideo": "Нет видео",
|
||||
"labelTooltipVideo": "Текущее качество видео",
|
||||
"ld": "LD",
|
||||
"ldTooltip": "Видео низкого качества",
|
||||
"lowDefinition": "Низкое качество",
|
||||
"onlyAudioAvailable": "Только звук",
|
||||
"onlyAudioSupported": "В этом браузере разрешен только звук.",
|
||||
"p2pEnabled": "Включен режим \"точка-к-точке\"",
|
||||
"p2pVideoQualityDescription": "В режиме \"точка-к-точке\" качество входящего сигнала может быть установлено в позицию \"высокое\" или \"только звук\". Остальные варианты в этом режиме не работают.",
|
||||
"recHighDefinitionOnly": "Предпочтительно высокое качество.",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Видео стандартного качества",
|
||||
"standardDefinition": "Стандартное качество (SD)",
|
||||
"qualityButtonTip": "Изменить качество входящего видео"
|
||||
},
|
||||
"dialOut": {
|
||||
"dial": "Дозвон",
|
||||
"dialOut": "",
|
||||
"statusMessage": "",
|
||||
"enterPhone": "",
|
||||
"phoneNotAllowed": ""
|
||||
"statusMessage": "сейчас __status__"
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "Добавить",
|
||||
"noResults": "",
|
||||
"searchPlaceholder": "",
|
||||
"title": "",
|
||||
"failedToAdd": ""
|
||||
"add": "Пригласить",
|
||||
"countryNotSupported": "Эта страна пока не поддерживается.",
|
||||
"countryReminder": "Вызов не в США? Пожалуйста, убедитесь, что указали код страны!",
|
||||
"disabled": "",
|
||||
"invite": "Пригласить",
|
||||
"loading": "Поиск людей и номеров телефонов",
|
||||
"loadingNumber": "Поиск людей для приглашения",
|
||||
"loadingPeople": "Поиск людей для приглашения",
|
||||
"noResults": "Поиск не дал результата",
|
||||
"noValidNumbers": "Пожалуйста, введите номер телефона",
|
||||
"notAvailable": "Поиск не дал результата",
|
||||
"searchNumbers": "Добавить номера телефонов",
|
||||
"searchPeople": "Поиск не дал результата",
|
||||
"searchPeopleAndNumbers": "Поиск людей или добавление их телефонов",
|
||||
"telephone": "Номер: __number__",
|
||||
"title": "Пригласить людей на эту встречу",
|
||||
"failedToAdd": "Ошибка добавления участников"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "",
|
||||
"retry": "",
|
||||
"support": "",
|
||||
"supportMsg": ""
|
||||
"msg": "Небольшая заминка.",
|
||||
"retry": "Попробовать снова",
|
||||
"support": "Поддержка",
|
||||
"supportMsg": "Если это продолжится, свяжитесь с"
|
||||
},
|
||||
"deviceError": {
|
||||
"cameraPermission": "",
|
||||
"microphonePermission": ""
|
||||
"cameraError": "Ошибка доступа к камере",
|
||||
"microphoneError": "Ошибка доступа к микрофону",
|
||||
"cameraPermission": "Ошибка доступа к микрофону",
|
||||
"microphonePermission": "Нет разрешения на доступ к микрофону"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Средне",
|
||||
"bad": "Плохо",
|
||||
"good": "Хорошо",
|
||||
"detailsLabel": "Расскажите подробнее.",
|
||||
"rateExperience": "Оценка качества связи",
|
||||
"veryBad": "Очень плохо",
|
||||
"veryGood": "Очень хорошо"
|
||||
},
|
||||
"info": {
|
||||
"addPassword": "Добавить пароль",
|
||||
"cancelPassword": "Убрать пароль",
|
||||
"conferenceURL": "Ссылка:",
|
||||
"country": "Страна",
|
||||
"dialANumber": "Чтобы присоединиться к встрече, наберите один из этих номеров, а затем введите PIN-код: __conferenceID __ #",
|
||||
"dialInNumber": "Номер:",
|
||||
"dialInConferenceID": "PIN:",
|
||||
"dialInNotSupported": "К сожалению, набор номера в настоящее время не поддерживается.",
|
||||
"genericError": "Что-то пошло не так.",
|
||||
"inviteLiveStream": "Трансляция этой встречи: __url__",
|
||||
"invitePhone": "Чтобы присоединиться по телефону, наберите __number__ и введите PIN-код: __conferenceID __#",
|
||||
"invitePhoneAlternatives": "Посмотреть другие номера телефонов: __url__",
|
||||
"inviteURL": "Присоединиться к видеоконференции: __url__",
|
||||
"liveStreamURL": "Трансляция:",
|
||||
"moreNumbers": "Больше номеров",
|
||||
"noNumbers": "Нет номеров для набора.",
|
||||
"noPassword": "нет",
|
||||
"noRoom": "Для набора номера не было указано ни одной комнаты.",
|
||||
"numbers": "Номера для набора",
|
||||
"password": "Пароль:",
|
||||
"title": "Поделиться",
|
||||
"tooltip": "Получить информацию об этой встрече"
|
||||
},
|
||||
"settingsView": {
|
||||
"alertOk": "OK",
|
||||
"alertTitle": "Внимание",
|
||||
"alertURLText": "Ошибка адреса сервера",
|
||||
"conferenceSection": "Номера для набора",
|
||||
"displayName": "Отображаемое имя",
|
||||
"email": "Email",
|
||||
"header": "Настройки",
|
||||
"profileSection": "Профиль",
|
||||
"serverURL": "Адрес сервера",
|
||||
"startWithAudioMuted": "Начать с отключенным звуком",
|
||||
"startWithVideoMuted": "Начать с отключенным видео"
|
||||
},
|
||||
"calendarSync": {
|
||||
"later": "Позже",
|
||||
"next": "Предстоящие",
|
||||
"nextMeeting": "следующая встреча",
|
||||
"now": "Сейчас",
|
||||
"permissionButton": "Открыть настройки",
|
||||
"permissionMessage": "Для показа ваших встреч в приложении нужен доступ к календарю."
|
||||
},
|
||||
"recentList": {
|
||||
"today": "Сегодня",
|
||||
"yesterday": "Вчера",
|
||||
"earlier": "Ранее"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Потяните для обновления"
|
||||
},
|
||||
"deepLinking": {
|
||||
"title": "Запуск вашей встречи в __app __...",
|
||||
"description": "Ничего не случилось? Мы попытались запустить вашу встречу в настольном приложении __app__. Повторите попытку или запустите ее в веб-приложении __app__.",
|
||||
"tryAgainButton": "Повторите в настольном приложении",
|
||||
"launchWebButton": "Запустить в браузере",
|
||||
"appNotInstalled": "Чтобы присоединиться к этой встрече на телефоне, нужно мобильное приложение __app__.",
|
||||
"downloadApp": "Скачать приложение",
|
||||
"openApp": "Перейти к приложению"
|
||||
}
|
||||
}
|
||||
551
lang/main-zhTW.json
Normal file
@@ -0,0 +1,551 @@
|
||||
{
|
||||
"contactlist_plural": "__count__ 位成員",
|
||||
"passwordSetRemotely": "由另一位成員設置",
|
||||
"poweredby": "技術支援",
|
||||
"inviteUrlDefaultMsg": "您的會議正在建立起來………",
|
||||
"me": "我",
|
||||
"speaker": "",
|
||||
"raisedHand": "請求發言",
|
||||
"defaultNickname": "例如 阿美 志明",
|
||||
"defaultLink": "例如 __url__",
|
||||
"audioDevices": {
|
||||
"bluetooth": "藍牙",
|
||||
"headphones": "耳機",
|
||||
"phone": "電話",
|
||||
"speaker": "發言者"
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "僅用音訊",
|
||||
"featureToggleDisabled": "在僅用音訊模式下,開關 __feature__ 功能是停用的"
|
||||
},
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "",
|
||||
"chromeGrantPermissions": "",
|
||||
"androidGrantPermissions": "",
|
||||
"electronGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "當瀏覽器要求權限允許時,請選擇<b><i>分享設備</i></b> ",
|
||||
"operaGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>允許</i></b>",
|
||||
"iexplorerGrantPermissions": "",
|
||||
"safariGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>OK</i></b>",
|
||||
"nwjsGrantPermissions": "請允許權限使用您的攝影裝置和麥克風",
|
||||
"edgeGrantPermissions": "當瀏覽器要求權限允許時,請選擇 <b><i>是的</i></b>"
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"keyboardShortcuts": "快捷鍵",
|
||||
"raiseHand": "舉手發言或不作發言",
|
||||
"pushToTalk": "按鍵通話",
|
||||
"toggleScreensharing": "在攝影鏡頭和螢幕分享之間進行切換",
|
||||
"toggleFilmstrip": "顯示或隱藏視訊",
|
||||
"toggleShortcuts": "顯示或隱藏說明選單",
|
||||
"focusLocal": "聚焦於自己的視訊",
|
||||
"focusRemote": "聚焦於另一位通話者的視訊",
|
||||
"toggleChat": "開啟或關閉聊天",
|
||||
"mute": "靜音或解除靜音",
|
||||
"fullScreen": "進入或退出全螢幕",
|
||||
"videoMute": "啟動或停止自己的攝影裝置",
|
||||
"showSpeakerStats": "顯示發言者數據"
|
||||
},
|
||||
"welcomepage": {
|
||||
"appDescription": "快來使用吧,團隊全部成員使用視訊通話,可以邀請任何您所認識的人。 __app__ 是一套完全加密、100% 開放源碼的視訊會議解決方案。無需註冊帳號,無時無刻不分日夜均可免費使用。",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "語音",
|
||||
"video": "視訊"
|
||||
},
|
||||
"calendar": "日曆",
|
||||
"go": "開始",
|
||||
"join": "加入",
|
||||
"privacy": "隱私",
|
||||
"roomname": "輸入會議室名稱",
|
||||
"roomnameHint": "請輸入您想加入的會議室 URL 網址或名稱。您可以用個名稱來建立會議室,只要其他人輸入相同的名稱就能加入會議室喔。",
|
||||
"sendFeedback": "發送回報",
|
||||
"terms": "條款",
|
||||
"title": "更加安全、更具彈性、又完全免費的視訊會議系統"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"title": "__app__ 需要使用您的麥克風和攝影裝置。"
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"title": "由於電腦進入休眠,您的視訊通話已經中斷。",
|
||||
"text": "按下 <i>重新加入</i> 按鈕重新連接。",
|
||||
"rejoinKeyTitle": "重新加入"
|
||||
},
|
||||
"toolbar": {
|
||||
"addPeople": "新增人員到您的通話中",
|
||||
"audioonly": "啟用/停用 僅用音訊模式(節省頻寬)",
|
||||
"callQuality": "管理通話品質",
|
||||
"enterFullScreen": "觀看全螢幕",
|
||||
"exitFullScreen": "跳出全螢幕",
|
||||
"feedback": "留言回報",
|
||||
"moreActions": "更多動作",
|
||||
"mute": "靜音 / 解除靜音",
|
||||
"videomute": "啟動/停止 攝影裝置",
|
||||
"authenticate": "驗證",
|
||||
"lock": "鎖定/解鎖 會議室",
|
||||
"chat": "開啟/關閉 聊天",
|
||||
"etherpad": "開啟/關閉 分享文件檔案",
|
||||
"documentOpen": "開啟分享的文件檔案",
|
||||
"documentClose": "關閉分享的文件檔案",
|
||||
"sharedvideo": "分享 YouTube 視訊",
|
||||
"sharescreen": "螢幕分享",
|
||||
"stopSharedVideo": "停止 YouTube 視訊",
|
||||
"fullscreen": "觀看/跳出 全螢幕",
|
||||
"sip": "播打 SIP 號碼",
|
||||
"Settings": "",
|
||||
"hangup": "留言",
|
||||
"login": "登入",
|
||||
"logout": "",
|
||||
"sharedVideoMutedPopup": "您分享的視訊已經靜音,現在可以和其他成員交談了。",
|
||||
"micMutedPopup": "您的麥克風已經處於靜音,可以觀看分享視訊了。",
|
||||
"talkWhileMutedPopup": "您要發言嗎? 目前您處於靜音。",
|
||||
"unableToUnmutePopup": "當分享視訊正在使用時,您不能解除靜音。",
|
||||
"cameraDisabled": "攝影裝置無法使用",
|
||||
"micDisabled": "麥克風無法使用",
|
||||
"filmstrip": "顯示/隱藏 視訊",
|
||||
"profile": "編輯您的簡介",
|
||||
"raiseHand": "舉手/取消 請求發言",
|
||||
"shortcuts": "查看快捷鍵",
|
||||
"speakerStats": "發言者數據"
|
||||
},
|
||||
"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": "遠端端口:",
|
||||
"localport": "本地端口:",
|
||||
"localaddress": "本地地址:",
|
||||
"remoteaddress": "遠端地址:",
|
||||
"transport": "傳輸:",
|
||||
"bandwidth": "估計頻寬:",
|
||||
"na": "一旦會議啟動,即可回到此處查看連接資訊",
|
||||
"turn": " (轉)",
|
||||
"quality": {
|
||||
"good": "",
|
||||
"inactive": "未啟用",
|
||||
"lost": "漏失",
|
||||
"nonoptimal": "不甚理想",
|
||||
"poor": "不好"
|
||||
},
|
||||
"status": "連接:"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "已經中斷連接",
|
||||
"moderator": "主持人權限已經取得!",
|
||||
"connectedOneMember": "__name__ 已經連接",
|
||||
"connectedTwoMembers": "__first__ 與 __second__ 已經連接",
|
||||
"connectedThreePlusMembers": "__name__ 與 __count__ 位其他成員已經連接",
|
||||
"somebody": "某人",
|
||||
"me": "自己",
|
||||
"focus": "會議焦點",
|
||||
"focusFail": "__component__ 無法使用 - 請在 __ms__ 秒後重試",
|
||||
"grantedTo": "主持人權限已授予 __to__!",
|
||||
"grantedToUnknown": "主持人權限已經授予 $t(somebody) !",
|
||||
"muted": "您已經啟動通話,並處於靜音狀態。",
|
||||
"mutedTitle": "您目前處於靜音!",
|
||||
"raisedHand": "請求發言。",
|
||||
"suboptimalExperienceTitle": "瀏覽器警告",
|
||||
"suboptimalExperienceDescription": "呃……恐怕您對 __appName__ 的體驗不是很好,我們正在嘗試找方法改進對此瀏覽器的支援。現下敬請選用 <a href='static/recommendedBrowsers.html' target='_blank'>全力支援的瀏覽器</a> 來進行。"
|
||||
},
|
||||
"dialog": {
|
||||
"allow": "允許",
|
||||
"kickMessage": "您已經被踢出會議!",
|
||||
"popupErrorTitle": "彈出視窗遭到阻攔",
|
||||
"popupError": "您的瀏覽器在此網站上阻攔彈出視窗。請在瀏覽器的安全設置中開啟它並再試一次。",
|
||||
"passwordErrorTitle": "密碼錯誤",
|
||||
"passwordError": "此會議目前已受密碼保護。只有會議的擁有者可以設定密碼。",
|
||||
"passwordError2": "此會議目前未受密碼保護。只有會議的擁有者可以設定密碼。",
|
||||
"connectError": "喔哦!發生錯誤,無法連接至會議。",
|
||||
"connectErrorWithMsg": "喔哦!發生錯誤,無法連接至會議: __msg__",
|
||||
"incorrectPassword": "錯誤的用戶名稱或密碼",
|
||||
"connecting": "",
|
||||
"copy": "複製",
|
||||
"contactSupport": "聯絡支援",
|
||||
"error": "",
|
||||
"detectext": "嘗試偵測桌面分享擴充應用程式時發生錯誤。",
|
||||
"failedpermissions": "未能取得使用本地麥克風或攝影裝置的權限。",
|
||||
"conferenceReloadTitle": "不好意思,出錯了。",
|
||||
"conferenceReloadMsg": "我們正試著修復狀況。重新連接於 __seconds__ 秒內……",
|
||||
"conferenceDisconnectTitle": "您已經被中斷連接。",
|
||||
"conferenceDisconnectMsg": "請檢查一下網路連接。將在 __seconds__ 秒後重新連接…",
|
||||
"dismiss": "解除",
|
||||
"rejoinNow": "立即重新加入",
|
||||
"maxUsersLimitReachedTitle": "成員人數已經達到上限",
|
||||
"maxUsersLimitReached": "由於會議已達到人數上限,額滿不能加入。請聯絡會議發起人,或是稍後再次嘗試!",
|
||||
"lockTitle": "鎖定失敗",
|
||||
"lockMessage": "鎖定會議失敗。",
|
||||
"warning": "",
|
||||
"passwordNotSupportedTitle": "不支援密碼",
|
||||
"passwordNotSupported": "不支援設置會議密碼。",
|
||||
"internalErrorTitle": "內部錯誤",
|
||||
"internalError": "喔哦!出現了點問題。發生錯誤: __error__",
|
||||
"unableToSwitch": "無法切換視訊串流。",
|
||||
"SLDFailure": "喔哦!發生錯誤,無法靜音! (SLD故障)",
|
||||
"SRDFailure": "喔哦!發生錯誤,無法停止視訊! (SRD故障)",
|
||||
"oops": "喔哦!",
|
||||
"currentPassword": "目前的密碼是",
|
||||
"passwordLabel": "密碼",
|
||||
"defaultError": "存在某種錯誤",
|
||||
"passwordRequired": "需要密碼",
|
||||
"Ok": "Ok",
|
||||
"done": "完成",
|
||||
"Remove": "移除",
|
||||
"removePassword": "移除密碼",
|
||||
"shareVideoTitle": "分享視訊",
|
||||
"shareVideoLinkError": "請提供正確的 YouTube 連結。",
|
||||
"removeSharedVideoTitle": "移除分享視訊",
|
||||
"removeSharedVideoMsg": "您確定要移除自己的分享視訊嗎?",
|
||||
"alreadySharedVideoMsg": "另一位成員正準備分享視訊。會議一次僅允許一位視訊分享。",
|
||||
"alreadySharedVideoTitle": "一次只能允許一位視訊分享",
|
||||
"WaitingForHost": "等侯主辦人………",
|
||||
"WaitForHostMsg": "會議 <b>__room__ </b> 尚未啟動。如果您是主辦人,請授權開始,否則請等候主辦人。",
|
||||
"IamHost": "我是主辦人",
|
||||
"Cancel": "取消",
|
||||
"Submit": "提交",
|
||||
"retry": "重試",
|
||||
"logoutTitle": "登出",
|
||||
"logoutQuestion": "您確定要登出並停止會議嗎?",
|
||||
"sessTerminated": "通話已經終止",
|
||||
"hungUp": "我方掛斷",
|
||||
"joinAgain": "再次加入",
|
||||
"Share": "",
|
||||
"Save": "儲存",
|
||||
"recording": "",
|
||||
"recordingToken": "輸入錄製標記",
|
||||
"Back": "返回",
|
||||
"serviceUnavailable": "服務無法使用",
|
||||
"gracefulShutdown": "本伺服器閉關維護中,請稍後再試。",
|
||||
"Yes": "是的",
|
||||
"reservationError": "預約系統錯誤",
|
||||
"reservationErrorMsg": "錯誤碼: __code__, 訊息: __msg__",
|
||||
"password": "輸入密碼",
|
||||
"userPassword": "用戶密碼",
|
||||
"token": "標記",
|
||||
"tokenAuthFailedTitle": "",
|
||||
"tokenAuthFailed": "對不起,您未被允許加入此會議。",
|
||||
"displayNameRequired": "顯示名稱是必須的",
|
||||
"enterDisplayName": "請輸入您的顯示名稱",
|
||||
"feedbackHelp": "您的回報將幫助提昇視訊體驗。",
|
||||
"feedbackQuestion": "請告訴我們本次通話體驗!",
|
||||
"thankYou": "感謝您使用 __appName__!",
|
||||
"sorryFeedback": "很抱歉聽到這些,能告訴我們更多詳情嗎?",
|
||||
"liveStreaming": "",
|
||||
"streamKey": "直播串流密鑰",
|
||||
"startLiveStreaming": "立即開始直播",
|
||||
"startRecording": "啟動錄製作業",
|
||||
"stopStreamingWarning": "確定要停止直播串流嗎?",
|
||||
"stopRecordingWarning": "確定要停止錄製作業嗎?",
|
||||
"stopLiveStreaming": "停止直播串流",
|
||||
"stopRecording": "停止錄製作業",
|
||||
"doNotShowMessageAgain": "不再顯示此訊息",
|
||||
"permissionDenied": "權限受到禁止",
|
||||
"screenSharingFailedToInstall": "喔哦!螢幕分享擴充程式安裝失敗。",
|
||||
"screenSharingFailedToInstallTitle": "螢幕分享擴充安裝失敗",
|
||||
"screenSharingFirefoxPermissionDeniedError": "嘗試進行螢幕分享時遇到問題。請確認您有賦予相對的權限允許。",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "喔哦!我們無法啟動螢幕分享!",
|
||||
"screenSharingPermissionDeniedError": "喔哦!您的視訊分享擴充權限發生一點問題。請重新載入再試一次。",
|
||||
"cameraUnsupportedResolutionError": "您的攝影裝置不支援所需的視訊解析度。",
|
||||
"cameraUnknownError": "由於不明原因,無法使用攝影裝置。",
|
||||
"cameraPermissionDeniedError": "您未取得權限使用您的攝影裝置。您仍然可參加會議,但是其他人無法看到。可以利用位址欄中的攝影裝置按鈕來修復啟動。",
|
||||
"cameraNotFoundError": "未發現攝影裝置。",
|
||||
"cameraConstraintFailedError": "您的攝影裝置不符合要求。",
|
||||
"micUnknownError": "不明原因造成麥克風無法使用。",
|
||||
"micPermissionDeniedError": "您未取得權限使用麥克風。您仍然可參加會議,但是其他人無法聽到。可以利用位址欄中的攝影裝置按鈕來修復啟動。",
|
||||
"micNotFoundError": "未發現麥克風。",
|
||||
"micConstraintFailedError": "您的麥克風不符合要求。",
|
||||
"micNotSendingDataTitle": "無法取用麥克風",
|
||||
"micNotSendingData": "我們無法取用您的麥克風。請從設罝選單裡選擇其他設備或者重新裝載。",
|
||||
"cameraNotSendingDataTitle": "無法取用攝影裝置",
|
||||
"cameraNotSendingData": "我們無法取用您的攝影裝置。請檢查是否有其他程序正在使用這個設備,否則請從設置選單裡選擇其他設備或者重新裝載。",
|
||||
"goToStore": "前往應用商店",
|
||||
"externalInstallationTitle": "需要擴充應用程式",
|
||||
"externalInstallationMsg": "",
|
||||
"inlineInstallationMsg": "您需要安裝桌面分享擴充應用程式。",
|
||||
"inlineInstallExtension": "立即安裝",
|
||||
"muteParticipantTitle": "靜音這位成員?",
|
||||
"muteParticipantBody": "您無法對他們解除靜音,但是他們自己隨時可以解除靜音。",
|
||||
"muteParticipantButton": "靜音",
|
||||
"remoteControlTitle": "遠端桌面控制",
|
||||
"remoteControlRequestMessage": "您要允許 __user__ 遠端控制您的桌面嗎?",
|
||||
"remoteControlShareScreenWarning": "注意:如果按下 \"允許\" 您將分享自己的螢幕!",
|
||||
"remoteControlDeniedMessage": "__user__ 拒絕您進行遠端控制的要求!",
|
||||
"remoteControlAllowedMessage": "__user__ 接受您進行遠端控制的要求!",
|
||||
"remoteControlErrorMessage": "在嘗試向 __user__ 請求遠端控制權限時發生錯誤!",
|
||||
"startRemoteControlErrorMessage": "嘗試啟動遠端控制階段時發生錯誤!",
|
||||
"remoteControlStopMessage": "遠端控制階段結束!",
|
||||
"close": "關閉",
|
||||
"shareYourScreen": "分享自己的螢幕",
|
||||
"yourEntireScreen": "自己的全螢幕",
|
||||
"applicationWindow": "應用程式視窗"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"該會議受密碼保護,請在加入會議時使用下列密碼:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "邀請發至__appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"嗨, 我想邀請您加入剛剛建立的 __appName__ 會議。",
|
||||
"",
|
||||
"",
|
||||
"請點按下面的連結來加入會議。",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
" 請注意 __appName__ 目前僅支援下列瀏覽器:__supportedBrowsers__, 敬請選用其一。",
|
||||
"",
|
||||
"",
|
||||
"即刻加入與您交談喔!"
|
||||
],
|
||||
"and": "與"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "錯誤",
|
||||
"CONNECTING": "連接中",
|
||||
"RECONNECTING": "網絡錯誤發生。重新連接中………",
|
||||
"CONNFAIL": "連接失敗",
|
||||
"AUTHENTICATING": "驗證中",
|
||||
"AUTHFAIL": "驗證失敗",
|
||||
"CONNECTED": "已經連接",
|
||||
"DISCONNECTED": "已經中斷連接",
|
||||
"DISCONNECTING": "中斷連接中",
|
||||
"ATTACHED": "已經附加"
|
||||
},
|
||||
"recording": {
|
||||
"busy": "我們正在釋放錄製資源。請過幾分鐘後再試。",
|
||||
"busyTitle": "全部錄製設備正在忙碌",
|
||||
"buttonTooltip": "啟動/停止 錄製作業",
|
||||
"error": "錄製作業失敗。請再次重試。",
|
||||
"failedToStart": "錄製啟動失敗",
|
||||
"off": "錄製作業已經停止",
|
||||
"on": "錄製作業中",
|
||||
"pending": "錄製作業正在等待成員加入……",
|
||||
"serviceName": "錄製作業服務",
|
||||
"unavailable": "喔哦!__serviceName__ 目前無法使用。我們正在解決此問題,請稍後再試。",
|
||||
"unavailableTitle": "錄製作業無法使用"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "我們正在釋放串流資源。請過幾分鐘後再試。",
|
||||
"busyTitle": "全部串流設備正在忙碌",
|
||||
"buttonTooltip": "啟動/停止 直播串流",
|
||||
"changeSignIn": "切換帳號。",
|
||||
"choose": "選擇直播串流",
|
||||
"chooseCTA": "請選擇直播串流選項。您目前是以 __email__ 身份登入。",
|
||||
"enterStreamKey": "在此輸入您的 YouTube 直播串流密鑰。",
|
||||
"error": "直播串流失敗。請重試。",
|
||||
"errorAPI": "取用您的 YouTube 播出時發生錯誤。請重新登入。",
|
||||
"failedToStart": "直播串流啟動失敗",
|
||||
"off": "直播串流已經停止",
|
||||
"on": "直播串流中",
|
||||
"pending": "啟動直播串流………",
|
||||
"serviceName": "直播串流服務",
|
||||
"signIn": "使用 Google 帳戶登入",
|
||||
"signInCTA": "輸入 YouTube 直播串流密鑰,或登入 YouTube 帳號。",
|
||||
"start": "啟動直播串流",
|
||||
"streamIdHelp": "這是什麼?",
|
||||
"unavailableTitle": "直播串流無法使用"
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "我們正在清理釋放資源。請過幾分鐘後再試。",
|
||||
"busyTitle": "會議室服務正處於忙碌中",
|
||||
"errorInvite": "會議尚未開始,請稍後再來。",
|
||||
"errorInviteTitle": "錯誤邀請會議室",
|
||||
"errorAlreadyInvited": "__displayName__ 已受邀請",
|
||||
"errorInviteFailedTitle": "邀請 __displayName__ 失敗",
|
||||
"errorInviteFailed": "我們正在解決問題。請稍後再試。",
|
||||
"pending": "__displayName__ 已經邀請",
|
||||
"serviceName": "會議室服務",
|
||||
"unavailableTitle": "會議室服務無法使用"
|
||||
},
|
||||
"speakerStats": {
|
||||
"hours": "__count__h",
|
||||
"minutes": "__count__m",
|
||||
"name": "名稱",
|
||||
"seconds": "__count__s",
|
||||
"speakerStats": "發言者數據",
|
||||
"speakerTime": "發言者時間"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "設備設置",
|
||||
"noPermission": "未取得權限",
|
||||
"previewUnavailable": "預覽無法使用",
|
||||
"selectADevice": "選擇設備",
|
||||
"testAudio": "測試聲音"
|
||||
},
|
||||
"videoStatus": {
|
||||
"callQuality": "通話品質",
|
||||
"hd": "HD 高清",
|
||||
"hdTooltip": "觀看高清視訊 HD",
|
||||
"highDefinition": "高清品質 HD",
|
||||
"labelTooltipAudioOnly": "僅有音訊模式已經啟用",
|
||||
"labelTooiltipNoVideo": "沒有視訊",
|
||||
"labelTooltipVideo": "目前視訊品質",
|
||||
"ld": "LD 低清",
|
||||
"ldTooltip": "觀看低清視訊 LD",
|
||||
"lowDefinition": "低清品質 LD",
|
||||
"onlyAudioAvailable": "僅有音訊可以使用",
|
||||
"onlyAudioSupported": "在此瀏覽器我們僅支援音訊功能。",
|
||||
"p2pEnabled": "點對點功能已經啟用",
|
||||
"p2pVideoQualityDescription": "在點對點模式下,通話品質只能使有高清和僅用音訊兩個選項。其他選項只有在點對點模式退出後才可使用。",
|
||||
"recHighDefinitionOnly": "將會偏好使用高清模式 HD。",
|
||||
"sd": "SD 標清",
|
||||
"sdTooltip": "觀看標清視訊 SD",
|
||||
"standardDefinition": "標清品質 SD",
|
||||
"qualityButtonTip": "修改接收視訊品質"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "現在狀態為 __status__"
|
||||
},
|
||||
"addPeople": {
|
||||
"add": "",
|
||||
"countryNotSupported": "此目標區域尚未支援。",
|
||||
"countryReminder": "嘗試在美國外地通話?請確認開頭使用的國家代碼!",
|
||||
"disabled": "",
|
||||
"invite": "邀請",
|
||||
"loading": "尋找聯絡人及電話號碼",
|
||||
"loadingNumber": "驗證電話號碼",
|
||||
"loadingPeople": "正在尋搜人員進行邀請",
|
||||
"noResults": "沒有符合要求的搜尋結果",
|
||||
"noValidNumbers": "請輸入一組電話號碼",
|
||||
"notAvailable": "您不可以邀請人員。",
|
||||
"searchNumbers": "新增電話號碼",
|
||||
"searchPeople": "尋找人員",
|
||||
"searchPeopleAndNumbers": "尋找人員或新增電話號碼",
|
||||
"telephone": "電話: __number__",
|
||||
"title": "邀請人員參加會議",
|
||||
"failedToAdd": "無法增加成員"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "好像有點卡卡不順。",
|
||||
"retry": "重試",
|
||||
"support": "支援",
|
||||
"supportMsg": "如果狀況一直發生,請聯絡"
|
||||
},
|
||||
"deviceError": {
|
||||
"cameraError": "無法取用您的攝影裝置",
|
||||
"microphoneError": "無法取用您的麥克風",
|
||||
"cameraPermission": "無法獲得攝影裝置取用權限",
|
||||
"microphonePermission": "無法獲得麥克風取用權限"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "普通中等",
|
||||
"bad": "很差",
|
||||
"good": "很好",
|
||||
"detailsLabel": "告訴我們本次會議使用上更多結果。",
|
||||
"rateExperience": "請您評價這次會議的體驗成效",
|
||||
"veryBad": "極差",
|
||||
"veryGood": "極好"
|
||||
},
|
||||
"info": {
|
||||
"addPassword": "新增密碼",
|
||||
"cancelPassword": "取消密碼",
|
||||
"conferenceURL": "連結:",
|
||||
"country": "國家",
|
||||
"dialANumber": "要加入您的會議,請撥打其中之一的電話號碼並輸入 PIN:__conferenceID__#",
|
||||
"dialInNumber": "播入:",
|
||||
"dialInConferenceID": "PIN 號碼:",
|
||||
"dialInNotSupported": "抱歉,目前不支援電話播入。",
|
||||
"genericError": "糟糕!出錯了。",
|
||||
"inviteLiveStream": "要觀看這場會議的直播串流,點按此連結: __url__",
|
||||
"invitePhone": "要想使用電話加入會議,請撥打 __number__ 並輸入 PIN: __conferenceID__#",
|
||||
"invitePhoneAlternatives": "查看更多電話號碼,點按此連結: __url__",
|
||||
"inviteURL": "加入視訊會議,請點按此連結: __url__",
|
||||
"liveStreamURL": "直播串流:",
|
||||
"moreNumbers": "更多成員",
|
||||
"noNumbers": "無播入號碼。",
|
||||
"noPassword": "無",
|
||||
"noRoom": "沒有會議室是指定要播打進入。",
|
||||
"numbers": "播入號碼",
|
||||
"password": "密碼:",
|
||||
"title": "分享",
|
||||
"tooltip": "取得關於會議的連接使用資訊"
|
||||
},
|
||||
"settingsView": {
|
||||
"alertOk": "確認",
|
||||
"alertTitle": "警告",
|
||||
"alertURLText": "所輸入的伺服器 URL 是無效的",
|
||||
"conferenceSection": "會議",
|
||||
"displayName": "顯示名稱",
|
||||
"email": "電子郵件",
|
||||
"header": "設置",
|
||||
"profileSection": "簡介",
|
||||
"serverURL": "伺服器 URL",
|
||||
"startWithAudioMuted": "啟動並音訊靜音",
|
||||
"startWithVideoMuted": "啟動並視訊靜音"
|
||||
},
|
||||
"calendarSync": {
|
||||
"later": "稍後",
|
||||
"next": "即將推出",
|
||||
"nextMeeting": "下次會議",
|
||||
"now": "現在",
|
||||
"permissionButton": "開啟設定",
|
||||
"permissionMessage": "日曆允許權限是必須的,以列入您的會議於應用程式中。"
|
||||
},
|
||||
"recentList": {
|
||||
"today": "今日",
|
||||
"yesterday": "昨天",
|
||||
"earlier": "稍早"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "下滑以重新整理"
|
||||
},
|
||||
"deepLinking": {
|
||||
"title": "發起您的會議於 __app__...",
|
||||
"description": "沒有發生作用嗎?我們嘗試發起您的會議於 __app__ desktop 桌面應用程式。請再試一次,或是發起會議於 __app__ 網路應用程式。",
|
||||
"tryAgainButton": "在桌面上再試一次",
|
||||
"launchWebButton": "在網路上發起",
|
||||
"appNotInstalled": "在您的手機上需要 __app__ 行動應用程式去加入這場會議。",
|
||||
"downloadApp": "下載應用 APP",
|
||||
"openApp": "繼續前往此應用程式"
|
||||
}
|
||||
}
|
||||
@@ -57,15 +57,20 @@
|
||||
"video": "Video"
|
||||
},
|
||||
"calendar": "Calendar",
|
||||
"connectCalendarText": "Connect your calendar to view all your meetings in __app__. Plus, add __app__ meetings to your calendar and start them with one click.",
|
||||
"connectCalendarButton": "Connect your calendar",
|
||||
"enterRoomTitle": "Start a new meeting",
|
||||
"go": "GO",
|
||||
"join": "JOIN",
|
||||
"privacy": "Privacy",
|
||||
"recentList": "History",
|
||||
"recentList": "Recent",
|
||||
"recentListDelete": "Delete",
|
||||
"recentListEmpty": "Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",
|
||||
"roomname": "Enter room name",
|
||||
"roomnameHint": "Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",
|
||||
"sendFeedback": "Send feedback",
|
||||
"terms": "Terms",
|
||||
"title": "More secure, more flexible, and completely free video conferencing."
|
||||
"title": "Secure, fully featured, and completely free video conferencing"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
@@ -155,6 +160,7 @@
|
||||
"title": "Enter a nickname in the box below",
|
||||
"popover": "Choose a nickname"
|
||||
},
|
||||
"error": "Error: your message \"__originalText__\" was not sent. Reason: __error__",
|
||||
"messagebox": "Enter text..."
|
||||
},
|
||||
"settings": {
|
||||
@@ -204,7 +210,9 @@
|
||||
"connectionindicator":
|
||||
{
|
||||
"header": "Connection data",
|
||||
"connectedTo": "Connected to:",
|
||||
"bitrate": "Bitrate:",
|
||||
"bridgeCount": "Server count: ",
|
||||
"packetloss": "Packet loss:",
|
||||
"resolution": "Resolution:",
|
||||
"framerate": "Frame rate:",
|
||||
@@ -259,6 +267,7 @@
|
||||
"allow": "Allow",
|
||||
"confirm": "Confirm",
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
"kickTitle": "Kicked from meeting",
|
||||
"popupErrorTitle": "Pop-up blocked",
|
||||
"popupError": "Your browser is blocking pop-up windows from this site. Please enable pop-ups in your browser's security settings and try again.",
|
||||
"passwordErrorTitle": "Password Error",
|
||||
@@ -424,6 +433,11 @@
|
||||
],
|
||||
"and": "and"
|
||||
},
|
||||
"share":
|
||||
{
|
||||
"mainText": "Click the following link to join the meeting:\n__roomUrl__",
|
||||
"dialInfoText": "\n\n=====\n\nJust want to dial in on your phone?\n\n__defaultDialInNumber__Click this link to see the dial in phone numbers for this meeting\n__dialInfoPageUrl__"
|
||||
},
|
||||
"connection":
|
||||
{
|
||||
"ERROR": "Error",
|
||||
@@ -450,14 +464,16 @@
|
||||
"busyTitle": "All recorders are currently busy",
|
||||
"buttonTooltip": "Start / Stop recording",
|
||||
"error": "Recording failed. Please try again.",
|
||||
"expandedOff": "Recording has stopped",
|
||||
"expandedOn": "The meeting is currently being recorded.",
|
||||
"expandedPending": "Recording is being started...",
|
||||
"failedToStart": "Recording failed to start",
|
||||
"live": "LIVE",
|
||||
"off": "Recording stopped",
|
||||
"on": "Recording",
|
||||
"pending": "Preparing to record the meeting...",
|
||||
"rec": "REC",
|
||||
"authDropboxText": "Upload your recording to Dropbox.",
|
||||
"authDropboxCompletedText": "Your recording file will appear in your Dropbox shortly after the recording has finished.",
|
||||
"authDropboxText": "Upload to Dropbox",
|
||||
"serviceName": "Recording service",
|
||||
"signOut": "Sign Out",
|
||||
"signIn": "sign in",
|
||||
@@ -472,10 +488,13 @@
|
||||
"pending" : "Preparing to transcribe the meeting...",
|
||||
"off" : "Transcribing stopped",
|
||||
"error": "Transcribing failed. Please try again.",
|
||||
"expandedLabel": "Transcribing is currently on",
|
||||
"failedToStart": "Transcribing failed to start",
|
||||
"tr": "TR",
|
||||
"labelToolTip": "The meeting is being transcribed",
|
||||
"ccButtonTooltip": "Start / Stop showing subtitles"
|
||||
"ccButtonTooltip": "Start / Stop showing subtitles",
|
||||
"start": "Start showing subtitles",
|
||||
"stop": "Stop showing subtitles"
|
||||
},
|
||||
"liveStreaming":
|
||||
{
|
||||
@@ -489,12 +508,17 @@
|
||||
"error": "Live Streaming failed. Please try again.",
|
||||
"errorAPI": "An error occurred while accessing your YouTube broadcasts. Please try logging in again.",
|
||||
"errorLiveStreamNotEnabled": "Live Streaming is not enabled on __email__. Please enable live streaming or log into an account with live streaming enabled.",
|
||||
"expandedOff": "The live streaming has stopped",
|
||||
"expandedOn": "The meeting is currently being streamed to YouTube.",
|
||||
"expandedPending": "The live streaming is being started...",
|
||||
"failedToStart": "Live Streaming failed to start",
|
||||
"off": "Live Streaming stopped",
|
||||
"on": "Live Streaming",
|
||||
"pending": "Starting Live Stream...",
|
||||
"serviceName": "Live Streaming service",
|
||||
"signedInAs": "You are currently signed in as:",
|
||||
"signIn": "Sign in with Google",
|
||||
"signOut": "Sign out",
|
||||
"signInCTA": "Sign in or enter your live stream key from YouTube.",
|
||||
"start": "Start a live stream",
|
||||
"streamIdHelp": "What's this?",
|
||||
@@ -531,6 +555,7 @@
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "AUD",
|
||||
"audioOnlyExpanded": "You are in audio only mode. This mode saves bandwidth but you won't see videos of others.",
|
||||
"callQuality": "Call Quality",
|
||||
"hd": "HD",
|
||||
"hdTooltip": "Viewing high definition video",
|
||||
@@ -634,16 +659,21 @@
|
||||
"startWithVideoMuted": "Start with video muted"
|
||||
},
|
||||
"calendarSync": {
|
||||
"later": "Later",
|
||||
"next": "Upcoming",
|
||||
"addMeetingURL": "Add a meeting link",
|
||||
"confirmAddLink": "Do you want to add a Jitsi link to this event?",
|
||||
"confirmAddLinkTitle": "Calendar",
|
||||
"join": "Join",
|
||||
"joinTooltip": "Join the meeting",
|
||||
"nextMeeting": "next meeting",
|
||||
"now": "Now",
|
||||
"noEvents": "There are no upcoming events scheduled.",
|
||||
"ongoingMeeting": "ongoing meeting",
|
||||
"permissionButton": "Open settings",
|
||||
"permissionMessage": "The Calendar permission is required to see your meetings in the app."
|
||||
"permissionMessage": "The Calendar permission is required to see your meetings in the app.",
|
||||
"refresh": "Refresh calendar",
|
||||
"today": "Today"
|
||||
},
|
||||
"recentList": {
|
||||
"joinPastMeeting": "Join A Past Meeting"
|
||||
"joinPastMeeting": "Join a past meeting"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Pull to refresh"
|
||||
|
||||
@@ -112,7 +112,7 @@ function initCommands() {
|
||||
const { name } = request;
|
||||
|
||||
switch (name) {
|
||||
case 'invite': // eslint-disable-line no-case-declarations
|
||||
case 'invite': {
|
||||
const { invitees } = request;
|
||||
|
||||
if (!Array.isArray(invitees) || invitees.length === 0) {
|
||||
@@ -143,6 +143,7 @@ function initCommands() {
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'is-audio-muted':
|
||||
callback(APP.conference.isLocalAudioMuted());
|
||||
break;
|
||||
|
||||
64
modules/API/external/external_api.js
vendored
@@ -114,10 +114,10 @@ function parseArguments(args) {
|
||||
|
||||
switch (typeof firstArg) {
|
||||
case 'string': // old arguments format
|
||||
case undefined: // eslint-disable-line no-case-declarations
|
||||
// not sure which format but we are trying to parse the old
|
||||
// format because if the new format is used everything will be undefined
|
||||
// anyway.
|
||||
case undefined: {
|
||||
// Not sure which format but we are trying to parse the old
|
||||
// format because if the new format is used everything will be undefined
|
||||
// anyway.
|
||||
const [
|
||||
roomName,
|
||||
width,
|
||||
@@ -141,6 +141,7 @@ function parseArguments(args) {
|
||||
jwt,
|
||||
onload
|
||||
};
|
||||
}
|
||||
case 'object': // new arguments format
|
||||
return args[0];
|
||||
default:
|
||||
@@ -461,55 +462,57 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* the event and value - the listener.
|
||||
* Currently we support the following
|
||||
* events:
|
||||
* incomingMessage - receives event notifications about incoming
|
||||
* {@code incomingMessage} - receives event notifications about incoming
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* 'from': from,//JID of the user that sent the message
|
||||
* 'nick': nick,//the nickname of the user that sent the message
|
||||
* 'message': txt//the text of the message
|
||||
* }}
|
||||
* outgoingMessage - receives event notifications about outgoing
|
||||
* {@code outgoingMessage} - receives event notifications about outgoing
|
||||
* messages. The listener will receive object with the following structure:
|
||||
* {{
|
||||
* 'message': txt//the text of the message
|
||||
* }}
|
||||
* displayNameChanged - receives event notifications about display name
|
||||
* change. The listener will receive object with the following structure:
|
||||
* {@code displayNameChanged} - receives event notifications about display
|
||||
* name change. The listener will receive object with the following
|
||||
* structure:
|
||||
* {{
|
||||
* jid: jid,//the JID of the participant that changed his display name
|
||||
* displayname: displayName //the new display name
|
||||
* }}
|
||||
* participantJoined - receives event notifications about new participant.
|
||||
* {@code participantJoined} - receives event notifications about new
|
||||
* participant.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* participantLeft - receives event notifications about the participant that
|
||||
* left the room.
|
||||
* {@code participantLeft} - receives event notifications about the
|
||||
* participant that left the room.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* jid: jid //the jid of the participant
|
||||
* }}
|
||||
* video-conference-joined - receives event notifications about the local
|
||||
* user has successfully joined the video conference.
|
||||
* {@code video-conference-joined} - receives event notifications about the
|
||||
* local user has successfully joined the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* video-conference-left - receives event notifications about the local user
|
||||
* has left the video conference.
|
||||
* {@code video-conference-left} - receives event notifications about the
|
||||
* local user has left the video conference.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* roomName: room //the room name of the conference
|
||||
* }}
|
||||
* screenSharingStatusChanged - receives event notifications about
|
||||
* {@code screenSharingStatusChanged} - receives event notifications about
|
||||
* turning on/off the local user screen sharing.
|
||||
* The listener will receive object with the following structure:
|
||||
* {{
|
||||
* on: on //whether screen sharing is on
|
||||
* }}
|
||||
* readyToClose - all hangup operations are completed and Jitsi Meet is
|
||||
* ready to be disposed.
|
||||
* {@code readyToClose} - all hangup operations are completed and Jitsi Meet
|
||||
* is ready to be disposed.
|
||||
* @returns {void}
|
||||
*
|
||||
* @deprecated
|
||||
@@ -536,11 +539,12 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Executes command. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* toggleAudio - mutes / unmutes audio with no arguments.
|
||||
* toggleVideo - mutes / unmutes video with no arguments.
|
||||
* toggleFilmStrip - hides / shows the filmstrip with no arguments.
|
||||
* {@code displayName} - Sets the display name of the local participant to
|
||||
* the value passed in the arguments array.
|
||||
* {@code toggleAudio} - Mutes / unmutes audio with no arguments.
|
||||
* {@code toggleVideo} - Mutes / unmutes video with no arguments.
|
||||
* {@code toggleFilmStrip} - Hides / shows the filmstrip with no arguments.
|
||||
*
|
||||
* If the command doesn't require any arguments the parameter should be set
|
||||
* to empty array or it may be omitted.
|
||||
*
|
||||
@@ -561,13 +565,13 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Executes commands. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* toggleAudio - mutes / unmutes audio. no arguments
|
||||
* toggleVideo - mutes / unmutes video. no arguments
|
||||
* toggleFilmStrip - hides / shows the filmstrip. no arguments
|
||||
* toggleChat - hides / shows chat. no arguments.
|
||||
* toggleShareScreen - starts / stops screen sharing. no arguments.
|
||||
* {@code displayName} - Sets the display name of the local participant to
|
||||
* the value passed in the arguments array.
|
||||
* {@code toggleAudio} - Mutes / unmutes audio. No arguments.
|
||||
* {@code toggleVideo} - Mutes / unmutes video. No arguments.
|
||||
* {@code toggleFilmStrip} - Hides / shows the filmstrip. No arguments.
|
||||
* {@code toggleChat} - Hides / shows chat. No arguments.
|
||||
* {@code toggleShareScreen} - Starts / stops screen sharing. No arguments.
|
||||
*
|
||||
* @param {Object} commandList - The object with commands to be executed.
|
||||
* The keys of the object are the commands that will be executed and the
|
||||
|
||||
@@ -4,9 +4,6 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
const UI = {};
|
||||
|
||||
import Chat from './side_pannels/chat/Chat';
|
||||
import SidePanels from './side_pannels/SidePanels';
|
||||
import SideContainerToggler from './side_pannels/SideContainerToggler';
|
||||
import messageHandler from './util/MessageHandler';
|
||||
import UIUtil from './util/UIUtil';
|
||||
import UIEvents from '../../service/UI/UIEvents';
|
||||
@@ -22,8 +19,10 @@ import {
|
||||
showParticipantJoinedNotification
|
||||
} from '../../react/features/base/participants';
|
||||
import { destroyLocalTracks } from '../../react/features/base/tracks';
|
||||
import { toggleChat } from '../../react/features/chat';
|
||||
import { openDisplayNamePrompt } from '../../react/features/display-name';
|
||||
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
|
||||
import { setFilmstripVisible } from '../../react/features/filmstrip';
|
||||
import {
|
||||
setNotificationsEnabled,
|
||||
showWarningNotification
|
||||
@@ -88,12 +87,9 @@ const UIListeners = new Map([
|
||||
], [
|
||||
UIEvents.SHARED_VIDEO_CLICKED,
|
||||
() => sharedVideoManager && sharedVideoManager.toggleSharedVideo()
|
||||
], [
|
||||
UIEvents.TOGGLE_CHAT,
|
||||
() => UI.toggleChat()
|
||||
], [
|
||||
UIEvents.TOGGLE_FILMSTRIP,
|
||||
() => UI.handleToggleFilmstrip()
|
||||
() => UI.toggleFilmstrip()
|
||||
], [
|
||||
UIEvents.FOLLOW_ME_ENABLED,
|
||||
enabled => followMeHandler && followMeHandler.enableFollowMe(enabled)
|
||||
@@ -157,7 +153,7 @@ UI.notifyKicked = function() {
|
||||
messageHandler.showError({
|
||||
hideErrorSupportLink: true,
|
||||
descriptionKey: 'dialog.kickMessage',
|
||||
titleKey: 'dialog.sessTerminated'
|
||||
titleKey: 'dialog.kickTitle'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -174,17 +170,6 @@ UI.notifyConferenceDestroyed = function(reason) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show chat error.
|
||||
* @param err the Error
|
||||
* @param msg
|
||||
*/
|
||||
UI.showChatError = function(err, msg) {
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
Chat.chatAddError(err, msg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change nickname for the user.
|
||||
* @param {string} id user id
|
||||
@@ -192,10 +177,6 @@ UI.showChatError = function(err, msg) {
|
||||
*/
|
||||
UI.changeDisplayName = function(id, displayName) {
|
||||
VideoLayout.onDisplayNameChanged(id, displayName);
|
||||
|
||||
if (APP.conference.isLocalId(id) || id === 'localVideoContainer') {
|
||||
Chat.setChatConversationMode(Boolean(displayName));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -255,11 +236,6 @@ UI.initConference = function() {
|
||||
followMeHandler = new FollowMe(APP.conference, UI);
|
||||
};
|
||||
|
||||
/** *
|
||||
* Handler for toggling filmstrip
|
||||
*/
|
||||
UI.handleToggleFilmstrip = () => UI.toggleFilmstrip();
|
||||
|
||||
/**
|
||||
* Returns the shared document manager object.
|
||||
* @return {EtherpadManager} the shared document manager object
|
||||
@@ -280,7 +256,6 @@ UI.start = function() {
|
||||
// Set the defaults for prompt dialogs.
|
||||
$.prompt.setDefaults({ persistent: false });
|
||||
|
||||
SideContainerToggler.init(eventEmitter);
|
||||
Filmstrip.init(eventEmitter);
|
||||
|
||||
VideoLayout.init(eventEmitter);
|
||||
@@ -298,26 +273,16 @@ UI.start = function() {
|
||||
|
||||
if (interfaceConfig.filmStripOnly) {
|
||||
$('body').addClass('filmstrip-only');
|
||||
Filmstrip.setFilmstripOnly();
|
||||
APP.store.dispatch(setNotificationsEnabled(false));
|
||||
} else {
|
||||
// Initialize recording mode UI.
|
||||
if (config.iAmRecorder) {
|
||||
VideoLayout.enableDeviceAvailabilityIcons(
|
||||
APP.conference.getMyUserId(), false);
|
||||
|
||||
// in case of iAmSipGateway keep local video visible
|
||||
if (!config.iAmSipGateway) {
|
||||
VideoLayout.setLocalVideoVisible(false);
|
||||
}
|
||||
|
||||
APP.store.dispatch(setToolboxEnabled(false));
|
||||
APP.store.dispatch(setNotificationsEnabled(false));
|
||||
UI.messageHandler.enablePopups(false);
|
||||
} else if (config.iAmRecorder) {
|
||||
// in case of iAmSipGateway keep local video visible
|
||||
if (!config.iAmSipGateway) {
|
||||
VideoLayout.setLocalVideoVisible(false);
|
||||
}
|
||||
|
||||
// Initialize side panels
|
||||
SidePanels.init(eventEmitter);
|
||||
APP.store.dispatch(setToolboxEnabled(false));
|
||||
APP.store.dispatch(setNotificationsEnabled(false));
|
||||
UI.messageHandler.enablePopups(false);
|
||||
}
|
||||
|
||||
document.title = interfaceConfig.APP_NAME;
|
||||
@@ -343,7 +308,6 @@ UI.bindEvents = () => {
|
||||
*
|
||||
*/
|
||||
function onResize() {
|
||||
SideContainerToggler.resize();
|
||||
VideoLayout.resizeVideoArea();
|
||||
}
|
||||
|
||||
@@ -503,18 +467,13 @@ UI.updateUserStatus = (user, status) => {
|
||||
{ status: UIUtil.escapeHtml(status) });
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles smileys in the chat.
|
||||
*/
|
||||
UI.toggleSmileys = () => Chat.toggleSmileys();
|
||||
|
||||
/**
|
||||
* Toggles filmstrip.
|
||||
*/
|
||||
UI.toggleFilmstrip = function() {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
Filmstrip.toggleFilmstrip(...arguments);
|
||||
VideoLayout.resizeVideoArea(true, false);
|
||||
const { visible } = APP.store.getState()['features/filmstrip'];
|
||||
|
||||
APP.store.dispatch(setFilmstripVisible(!visible));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -524,22 +483,9 @@ UI.toggleFilmstrip = function() {
|
||||
UI.isFilmstripVisible = () => Filmstrip.isFilmstripVisible();
|
||||
|
||||
/**
|
||||
* @returns {true} if the chat panel is currently visible, and false otherwise.
|
||||
* Toggles the visibility of the chat panel.
|
||||
*/
|
||||
UI.isChatVisible = () => Chat.isVisible();
|
||||
|
||||
/**
|
||||
* Toggles chat panel.
|
||||
*/
|
||||
UI.toggleChat = () => UI.toggleSidePanel('chat_container');
|
||||
|
||||
/**
|
||||
* Toggles the given side panel.
|
||||
*
|
||||
* @param {String} sidePanelId the identifier of the side panel to toggle
|
||||
*/
|
||||
UI.toggleSidePanel = sidePanelId => SideContainerToggler.toggle(sidePanelId);
|
||||
|
||||
UI.toggleChat = () => APP.store.dispatch(toggleChat());
|
||||
|
||||
/**
|
||||
* Handle new user display name.
|
||||
@@ -749,17 +695,6 @@ UI.hideStats = function() {
|
||||
VideoLayout.hideStats();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add chat message.
|
||||
* @param {string} from user id
|
||||
* @param {string} displayName user nickname
|
||||
* @param {string} message message text
|
||||
* @param {number} stamp timestamp when message was created
|
||||
*/
|
||||
// eslint-disable-next-line max-params
|
||||
UI.addMessage = function(from, displayName, message, stamp) {
|
||||
Chat.updateChatConversation(from, displayName, message, stamp);
|
||||
};
|
||||
|
||||
UI.notifyTokenAuthFailed = function() {
|
||||
messageHandler.showError({
|
||||
|
||||
@@ -87,6 +87,7 @@ class Etherpad extends LargeContainer {
|
||||
this.container.appendChild(iframe);
|
||||
|
||||
iframe.onload = function() {
|
||||
// eslint-disable-next-line no-self-assign
|
||||
document.domain = document.domain;
|
||||
bubbleIframeMouseMove(iframe);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* global $, APP */
|
||||
import { shouldDisplayTileView } from '../../../react/features/video-layout';
|
||||
/* global $ */
|
||||
|
||||
import SmallVideo from '../videolayout/SmallVideo';
|
||||
|
||||
@@ -66,9 +65,7 @@ SharedVideoThumb.prototype.createContainer = function(spanId) {
|
||||
* The thumb click handler.
|
||||
*/
|
||||
SharedVideoThumb.prototype.videoClick = function() {
|
||||
if (!shouldDisplayTileView(APP.store.getState())) {
|
||||
this._togglePin();
|
||||
}
|
||||
this._togglePin();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/* global $, APP */
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import { setVisiblePanel } from '../../../react/features/side-panel';
|
||||
|
||||
/**
|
||||
* Handles open and close of the extended toolbar side panel
|
||||
* (chat, settings, etc.).
|
||||
*
|
||||
* @type {{init, toggle, isVisible, hide, show, resize}}
|
||||
*/
|
||||
const SideContainerToggler = {
|
||||
/**
|
||||
* Initialises this toggler by registering the listeners.
|
||||
*
|
||||
* @param eventEmitter
|
||||
*/
|
||||
init(eventEmitter) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
|
||||
// We may not have a side toolbar container, for example, in
|
||||
// filmstrip-only mode.
|
||||
const sideToolbarContainer
|
||||
= document.getElementById('sideToolbarContainer');
|
||||
|
||||
if (!sideToolbarContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adds a listener for the animationend event that would take care of
|
||||
// hiding all internal containers when the extendedToolbarPanel is
|
||||
// closed.
|
||||
sideToolbarContainer.addEventListener(
|
||||
'animationend',
|
||||
e => {
|
||||
if (e.animationName === 'slideOutExt') {
|
||||
$('#sideToolbarContainer').children()
|
||||
.each(function() {
|
||||
/* eslint-disable no-invalid-this */
|
||||
if ($(this).hasClass('show')) {
|
||||
SideContainerToggler.hideInnerContainer($(this));
|
||||
}
|
||||
/* eslint-enable no-invalid-this */
|
||||
});
|
||||
}
|
||||
},
|
||||
false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the container with the given element id.
|
||||
*
|
||||
* @param {String} elementId the identifier of the container element to
|
||||
* toggle
|
||||
*/
|
||||
toggle(elementId) {
|
||||
const elementSelector = $(`#${elementId}`);
|
||||
const isSelectorVisible = elementSelector.hasClass('show');
|
||||
|
||||
if (isSelectorVisible) {
|
||||
this.hide();
|
||||
APP.store.dispatch(setVisiblePanel(null));
|
||||
} else {
|
||||
if (this.isVisible()) {
|
||||
$('#sideToolbarContainer').children()
|
||||
.each(function() {
|
||||
/* eslint-disable no-invalid-this */
|
||||
if ($(this).id !== elementId && $(this).hasClass('show')) {
|
||||
SideContainerToggler.hideInnerContainer($(this));
|
||||
}
|
||||
/* eslint-enable no-invalid-this */
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.isVisible()) {
|
||||
this.show();
|
||||
}
|
||||
|
||||
this.showInnerContainer(elementSelector);
|
||||
APP.store.dispatch(setVisiblePanel(elementId));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns {true} if the side toolbar panel is currently visible,
|
||||
* otherwise returns {false}.
|
||||
*/
|
||||
isVisible() {
|
||||
return $('#sideToolbarContainer').hasClass('slideInExt');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns {true} if the side toolbar panel is currently hovered and
|
||||
* {false} otherwise.
|
||||
*/
|
||||
isHovered() {
|
||||
return $('#sideToolbarContainer:hover').length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the side toolbar panel with a slide out animation.
|
||||
*/
|
||||
hide() {
|
||||
$('#sideToolbarContainer')
|
||||
.removeClass('slideInExt')
|
||||
.addClass('slideOutExt');
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the side toolbar panel with a slide in animation.
|
||||
*/
|
||||
show() {
|
||||
if (!this.isVisible()) {
|
||||
$('#sideToolbarContainer')
|
||||
.removeClass('slideOutExt')
|
||||
.addClass('slideInExt');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the inner container given by the selector.
|
||||
*
|
||||
* @param {Object} containerSelector the jquery selector for the
|
||||
* element to hide
|
||||
*/
|
||||
hideInnerContainer(containerSelector) {
|
||||
containerSelector.removeClass('show').addClass('hide');
|
||||
|
||||
this.eventEmitter.emit(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
|
||||
containerSelector.attr('id'), false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the inner container given by the selector.
|
||||
*
|
||||
* @param {Object} containerSelector the jquery selector for the
|
||||
* element to show
|
||||
*/
|
||||
showInnerContainer(containerSelector) {
|
||||
|
||||
// Before showing the container, make sure there is no other visible.
|
||||
// If we quickly show a container, while another one is animating
|
||||
// and animation never ends, so we do not really hide the first one and
|
||||
// we end up with to shown panels
|
||||
$('#sideToolbarContainer').children()
|
||||
.each(function() {
|
||||
/* eslint-disable no-invalid-this */
|
||||
if ($(this).hasClass('show')) {
|
||||
SideContainerToggler.hideInnerContainer($(this));
|
||||
}
|
||||
/* eslint-enable no-invalid-this */
|
||||
});
|
||||
|
||||
containerSelector.removeClass('hide').addClass('show');
|
||||
|
||||
this.eventEmitter.emit(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
|
||||
containerSelector.attr('id'), true);
|
||||
},
|
||||
|
||||
/**
|
||||
* TO FIX: do we need to resize the chat?
|
||||
*/
|
||||
resize() {
|
||||
// let [width, height] = UIUtil.getSidePanelSize();
|
||||
// Chat.resizeChat(width, height);
|
||||
}
|
||||
};
|
||||
|
||||
export default SideContainerToggler;
|
||||
@@ -1,13 +0,0 @@
|
||||
import Chat from './chat/Chat';
|
||||
import { isButtonEnabled } from '../../../react/features/toolbox';
|
||||
|
||||
const SidePanels = {
|
||||
init(eventEmitter) {
|
||||
// Initialize chat
|
||||
if (isButtonEnabled('chat')) {
|
||||
Chat.init(eventEmitter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default SidePanels;
|
||||
@@ -1,404 +0,0 @@
|
||||
/* global APP, $ */
|
||||
|
||||
import { processReplacements } from './Replacement';
|
||||
import VideoLayout from '../../videolayout/VideoLayout';
|
||||
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import { smileys } from './smileys';
|
||||
|
||||
import { addMessage, markAllRead } from '../../../../react/features/chat';
|
||||
import {
|
||||
dockToolbox,
|
||||
getToolboxHeight
|
||||
} from '../../../../react/features/toolbox';
|
||||
|
||||
let unreadMessages = 0;
|
||||
const sidePanelsContainerId = 'sideToolbarContainer';
|
||||
const htmlStr = `
|
||||
<div id="chat_container" class="sideToolbarContainer__inner">
|
||||
<div id="nickname">
|
||||
<span data-i18n="chat.nickname.title"></span>
|
||||
<form>
|
||||
<input type='text'
|
||||
class="input-control" id="nickinput" autofocus
|
||||
data-i18n="[placeholder]chat.nickname.popover">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="chatconversation"></div>
|
||||
<textarea id="usermsg" autofocus
|
||||
data-i18n="[placeholder]chat.messagebox"></textarea>
|
||||
<div id="smileysarea">
|
||||
<div id="smileys">
|
||||
<img src="images/smile.svg"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function initHTML() {
|
||||
$(`#${sidePanelsContainerId}`)
|
||||
.append(htmlStr);
|
||||
|
||||
// make sure we translate the panel, as adding it can be after i18n
|
||||
// library had initialized and translated already present html
|
||||
APP.translation.translateElement($(`#${sidePanelsContainerId}`));
|
||||
}
|
||||
|
||||
/**
|
||||
* The container id, which is and the element id.
|
||||
*/
|
||||
const CHAT_CONTAINER_ID = 'chat_container';
|
||||
|
||||
/**
|
||||
* Updates visual notification, indicating that a message has arrived.
|
||||
*/
|
||||
function updateVisualNotification() {
|
||||
// XXX The rewrite of the toolbar in React delayed the availability of the
|
||||
// element unreadMessages. In order to work around the delay, I introduced
|
||||
// and utilized unreadMsgSelector in addition to unreadMsgElement.
|
||||
const unreadMsgSelector = $('#unreadMessages');
|
||||
const unreadMsgElement
|
||||
= unreadMsgSelector.length > 0 ? unreadMsgSelector[0] : undefined;
|
||||
|
||||
if (unreadMessages && unreadMsgElement) {
|
||||
unreadMsgElement.innerHTML = unreadMessages.toString();
|
||||
|
||||
APP.store.dispatch(dockToolbox(true));
|
||||
|
||||
const chatButtonElement
|
||||
= document.getElementById('toolbar_button_chat');
|
||||
const leftIndent
|
||||
= (UIUtil.getTextWidth(chatButtonElement)
|
||||
- UIUtil.getTextWidth(unreadMsgElement)) / 2;
|
||||
const topIndent
|
||||
= ((UIUtil.getTextHeight(chatButtonElement)
|
||||
- UIUtil.getTextHeight(unreadMsgElement)) / 2) - 5;
|
||||
|
||||
unreadMsgElement.setAttribute(
|
||||
'style',
|
||||
`top:${topIndent}; left:${leftIndent};`);
|
||||
} else {
|
||||
unreadMsgSelector.html('');
|
||||
}
|
||||
|
||||
if (unreadMsgElement) {
|
||||
unreadMsgSelector.parent()[unreadMessages > 0 ? 'show' : 'hide']();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current time in the format it is shown to the user
|
||||
* @returns {string}
|
||||
*/
|
||||
function getCurrentTime(stamp) {
|
||||
const now = stamp ? new Date(stamp) : new Date();
|
||||
let hour = now.getHours();
|
||||
let minute = now.getMinutes();
|
||||
let second = now.getSeconds();
|
||||
|
||||
if (hour.toString().length === 1) {
|
||||
hour = `0${hour}`;
|
||||
}
|
||||
if (minute.toString().length === 1) {
|
||||
minute = `0${minute}`;
|
||||
}
|
||||
if (second.toString().length === 1) {
|
||||
second = `0${second}`;
|
||||
}
|
||||
|
||||
return `${hour}:${minute}:${second}`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function toggleSmileys() {
|
||||
const smileys = $('#smileysContainer'); // eslint-disable-line no-shadow
|
||||
|
||||
smileys.slideToggle();
|
||||
|
||||
$('#usermsg').focus();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function addClickFunction(smiley, number) {
|
||||
smiley.onclick = function addSmileyToMessage() {
|
||||
const usermsg = $('#usermsg');
|
||||
let message = usermsg.val();
|
||||
|
||||
message += smileys[`smiley${number}`];
|
||||
usermsg.val(message);
|
||||
usermsg.get(0).setSelectionRange(message.length, message.length);
|
||||
toggleSmileys();
|
||||
usermsg.focus();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the smileys container to the chat
|
||||
*/
|
||||
function addSmileys() {
|
||||
const smileysContainer = document.createElement('div');
|
||||
|
||||
smileysContainer.id = 'smileysContainer';
|
||||
for (let i = 1; i <= 21; i++) {
|
||||
const smileyContainer = document.createElement('div');
|
||||
|
||||
smileyContainer.id = `smiley${i}`;
|
||||
smileyContainer.className = 'smileyContainer';
|
||||
const smiley = document.createElement('img');
|
||||
|
||||
smiley.src = `images/smileys/smiley${i}.svg`;
|
||||
smiley.className = 'smiley';
|
||||
addClickFunction(smiley, i);
|
||||
smileyContainer.appendChild(smiley);
|
||||
smileysContainer.appendChild(smileyContainer);
|
||||
}
|
||||
|
||||
$('#chat_container').append(smileysContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the chat conversation.
|
||||
*/
|
||||
function resizeChatConversation() {
|
||||
// FIXME: this function can all be done with CSS. If Chat is ever rewritten,
|
||||
// do not copy over this logic.
|
||||
const msgareaHeight = $('#usermsg').outerHeight();
|
||||
const chatspace = $(`#${CHAT_CONTAINER_ID}`);
|
||||
const width = chatspace.width();
|
||||
const chat = $('#chatconversation');
|
||||
const smileys = $('#smileysarea'); // eslint-disable-line no-shadow
|
||||
|
||||
smileys.height(msgareaHeight);
|
||||
$('#smileys').css('bottom', (msgareaHeight - 26) / 2);
|
||||
$('#smileysContainer').css('bottom', msgareaHeight);
|
||||
chat.width(width - 10);
|
||||
|
||||
const maybeAMagicNumberForPaddingAndMargin = 100;
|
||||
const offset = maybeAMagicNumberForPaddingAndMargin
|
||||
+ msgareaHeight + getToolboxHeight();
|
||||
|
||||
chat.height(window.innerHeight - offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus input after 400 ms
|
||||
* Found input by id
|
||||
*
|
||||
* @param id {string} input id
|
||||
*/
|
||||
function deferredFocus(id) {
|
||||
setTimeout(() => $(`#${id}`).focus(), 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chat related user interface.
|
||||
*/
|
||||
const Chat = {
|
||||
/**
|
||||
* Initializes chat related interface.
|
||||
*/
|
||||
init(eventEmitter) {
|
||||
initHTML();
|
||||
if (APP.conference.getLocalDisplayName()) {
|
||||
Chat.setChatConversationMode(true);
|
||||
}
|
||||
|
||||
$('#smileys').click(() => {
|
||||
Chat.toggleSmileys();
|
||||
});
|
||||
|
||||
$('#nickinput').keydown(function(event) {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
const val = this.value; // eslint-disable-line no-invalid-this
|
||||
|
||||
this.value = '';// eslint-disable-line no-invalid-this
|
||||
eventEmitter.emit(UIEvents.NICKNAME_CHANGED, val);
|
||||
deferredFocus('usermsg');
|
||||
}
|
||||
});
|
||||
|
||||
const usermsg = $('#usermsg');
|
||||
|
||||
usermsg.keydown(function(event) {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
const value = this.value; // eslint-disable-line no-invalid-this
|
||||
|
||||
usermsg.val('').trigger('autosize.resize');
|
||||
this.focus();// eslint-disable-line no-invalid-this
|
||||
|
||||
const message = UIUtil.escapeHtml(value);
|
||||
|
||||
eventEmitter.emit(UIEvents.MESSAGE_CREATED, message);
|
||||
}
|
||||
});
|
||||
|
||||
const onTextAreaResize = function() {
|
||||
resizeChatConversation();
|
||||
Chat.scrollChatToBottom();
|
||||
};
|
||||
|
||||
usermsg.autosize({ callback: onTextAreaResize });
|
||||
|
||||
eventEmitter.on(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
|
||||
(containerId, isVisible) => {
|
||||
if (containerId !== CHAT_CONTAINER_ID || !isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
unreadMessages = 0;
|
||||
APP.store.dispatch(markAllRead());
|
||||
updateVisualNotification();
|
||||
|
||||
// Undock the toolbar when the chat is shown and if we're in a
|
||||
// video mode.
|
||||
if (VideoLayout.isLargeVideoVisible()) {
|
||||
APP.store.dispatch(dockToolbox(false));
|
||||
}
|
||||
|
||||
// if we are in conversation mode focus on the text input
|
||||
// if we are not, focus on the display name input
|
||||
deferredFocus(
|
||||
APP.conference.getLocalDisplayName()
|
||||
? 'usermsg'
|
||||
: 'nickinput');
|
||||
});
|
||||
|
||||
addSmileys();
|
||||
updateVisualNotification();
|
||||
},
|
||||
|
||||
/**
|
||||
* Appends the given message to the chat conversation.
|
||||
*/
|
||||
// eslint-disable-next-line max-params
|
||||
updateChatConversation(id, displayName, message, stamp) {
|
||||
const isFromLocalParticipant = APP.conference.isLocalId(id);
|
||||
let divClassName = '';
|
||||
|
||||
if (isFromLocalParticipant) {
|
||||
divClassName = 'localuser';
|
||||
} else {
|
||||
divClassName = 'remoteuser';
|
||||
|
||||
if (!Chat.isVisible()) {
|
||||
unreadMessages++;
|
||||
updateVisualNotification();
|
||||
}
|
||||
}
|
||||
|
||||
// replace links and smileys
|
||||
// Strophe already escapes special symbols on sending,
|
||||
// so we escape here only tags to avoid double &
|
||||
const escMessage = message.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n/g, '<br/>');
|
||||
const escDisplayName = UIUtil.escapeHtml(displayName);
|
||||
const timestamp = getCurrentTime(stamp);
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
message = processReplacements(escMessage);
|
||||
|
||||
const messageContainer
|
||||
= `${'<div class="chatmessage">'
|
||||
+ '<img src="images/chatArrow.svg" class="chatArrow">'
|
||||
+ '<div class="username '}${divClassName}">${escDisplayName
|
||||
}</div><div class="timestamp">${timestamp
|
||||
}</div><div class="usermessage">${message}</div>`
|
||||
+ '</div>';
|
||||
|
||||
$('#chatconversation').append(messageContainer);
|
||||
$('#chatconversation').animate(
|
||||
{ scrollTop: $('#chatconversation')[0].scrollHeight }, 1000);
|
||||
|
||||
const markAsRead = Chat.isVisible() || isFromLocalParticipant;
|
||||
|
||||
APP.store.dispatch(addMessage(
|
||||
escDisplayName, message, timestamp, markAsRead));
|
||||
},
|
||||
|
||||
/**
|
||||
* Appends error message to the conversation
|
||||
* @param errorMessage the received error message.
|
||||
* @param originalText the original message.
|
||||
*/
|
||||
chatAddError(errorMessage, originalText) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
errorMessage = UIUtil.escapeHtml(errorMessage);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
originalText = UIUtil.escapeHtml(originalText);
|
||||
|
||||
$('#chatconversation').append(
|
||||
`${'<div class="errorMessage"><b>Error: </b>Your message'}${
|
||||
originalText ? ` "${originalText}"` : ''
|
||||
} was not sent.${
|
||||
errorMessage ? ` Reason: ${errorMessage}` : ''}</div>`);
|
||||
$('#chatconversation').animate(
|
||||
{ scrollTop: $('#chatconversation')[0].scrollHeight }, 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the chat conversation mode.
|
||||
* Conversation mode is the normal chat mode, non conversation mode is
|
||||
* where we ask user to input its display name.
|
||||
* @param {boolean} isConversationMode if chat should be in
|
||||
* conversation mode or not.
|
||||
*/
|
||||
setChatConversationMode(isConversationMode) {
|
||||
$(`#${CHAT_CONTAINER_ID}`)
|
||||
.toggleClass('is-conversation-mode', isConversationMode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resizes the chat area.
|
||||
*/
|
||||
resizeChat(width, height) {
|
||||
$(`#${CHAT_CONTAINER_ID}`).width(width)
|
||||
.height(height);
|
||||
|
||||
resizeChatConversation();
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates if the chat is currently visible.
|
||||
*/
|
||||
isVisible() {
|
||||
return UIUtil.isVisible(
|
||||
document.getElementById(CHAT_CONTAINER_ID));
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows and hides the window with the smileys
|
||||
*/
|
||||
toggleSmileys,
|
||||
|
||||
/**
|
||||
* Scrolls chat to the bottom.
|
||||
*/
|
||||
scrollChatToBottom() {
|
||||
setTimeout(
|
||||
() => {
|
||||
const chatconversation = $('#chatconversation');
|
||||
|
||||
// XXX Prevent TypeError: undefined is not an object when the
|
||||
// Web browser does not support WebRTC (yet).
|
||||
chatconversation.length > 0
|
||||
&& chatconversation.scrollTop(
|
||||
chatconversation[0].scrollHeight);
|
||||
},
|
||||
5);
|
||||
}
|
||||
};
|
||||
|
||||
export default Chat;
|
||||
@@ -1,58 +0,0 @@
|
||||
import { regexes } from './smileys';
|
||||
|
||||
/**
|
||||
* Processes links and smileys in "body"
|
||||
*/
|
||||
export function processReplacements(body) {
|
||||
// make links clickable + add smileys
|
||||
return smilify(linkify(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and replaces all links in the links in "body"
|
||||
* with their <a href=""></a>
|
||||
*/
|
||||
export function linkify(inputText) {
|
||||
let replacedText;
|
||||
|
||||
/* eslint-disable no-useless-escape, max-len */
|
||||
|
||||
// URLs starting with http://, https://, or ftp://
|
||||
const replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||
|
||||
replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>');
|
||||
|
||||
// URLs starting with "www." (without // before it, or it'd re-link the ones done above).
|
||||
const replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
|
||||
|
||||
replacedText = replacedText.replace(replacePattern2, '$1<a href="https://$2" target="_blank" rel="noopener noreferrer">$2</a>');
|
||||
|
||||
// Change email addresses to mailto: links.
|
||||
const replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
|
||||
|
||||
replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');
|
||||
|
||||
/* eslint-enable no-useless-escape */
|
||||
|
||||
return replacedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces common smiley strings with images
|
||||
*/
|
||||
function smilify(body) {
|
||||
if (!body) {
|
||||
return body;
|
||||
}
|
||||
|
||||
let formattedBody = body;
|
||||
|
||||
for (const smiley in regexes) {
|
||||
if (regexes.hasOwnProperty(smiley)) {
|
||||
formattedBody = formattedBody.replace(regexes[smiley],
|
||||
`<img class="smiley" src="images/smileys/${smiley}.svg">`);
|
||||
}
|
||||
}
|
||||
|
||||
return formattedBody;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
import { setFilmstripVisible } from '../../../react/features/filmstrip';
|
||||
import {
|
||||
LAYOUTS,
|
||||
getCurrentLayout,
|
||||
@@ -9,188 +8,16 @@ import {
|
||||
shouldDisplayTileView
|
||||
} from '../../../react/features/video-layout';
|
||||
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
import {
|
||||
createShortcutEvent,
|
||||
createToolbarEvent,
|
||||
sendAnalytics
|
||||
} from '../../../react/features/analytics';
|
||||
|
||||
const Filmstrip = {
|
||||
/**
|
||||
*
|
||||
* @param eventEmitter the {EventEmitter} through which {Filmstrip} is to
|
||||
* emit/fire {UIEvents} (such as {UIEvents.TOGGLED_FILMSTRIP}).
|
||||
* Caches jquery lookups of the filmstrip for future use.
|
||||
*/
|
||||
init(eventEmitter) {
|
||||
this.iconMenuDownClassName = 'icon-menu-down';
|
||||
this.iconMenuUpClassName = 'icon-menu-up';
|
||||
init() {
|
||||
this.filmstripContainerClassName = 'filmstrip';
|
||||
this.filmstrip = $('#remoteVideos');
|
||||
this.filmstripRemoteVideos = $('#filmstripRemoteVideosContainer');
|
||||
this.eventEmitter = eventEmitter;
|
||||
|
||||
// Show the toggle button and add event listeners only when out of
|
||||
// filmstrip only mode.
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
this._initFilmstripToolbar();
|
||||
this.registerListeners();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the filmstrip toolbar.
|
||||
*/
|
||||
_initFilmstripToolbar() {
|
||||
const toolbarContainerHTML = this._generateToolbarHTML();
|
||||
const className = this.filmstripContainerClassName;
|
||||
const container = document.querySelector(`.${className}`);
|
||||
|
||||
UIUtil.prependChild(container, toolbarContainerHTML);
|
||||
|
||||
const iconSelector = '#toggleFilmstripButton i';
|
||||
|
||||
this.toggleFilmstripIcon = document.querySelector(iconSelector);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates HTML layout for filmstrip toggle button and wrapping container.
|
||||
* @returns {HTMLElement}
|
||||
* @private
|
||||
*/
|
||||
_generateToolbarHTML() {
|
||||
const container = document.createElement('div');
|
||||
const isVisible = this.isFilmstripVisible();
|
||||
|
||||
container.className = 'filmstrip__toolbar';
|
||||
container.innerHTML = `
|
||||
<button id="toggleFilmstripButton">
|
||||
<i class="icon-menu-${isVisible ? 'down' : 'up'}">
|
||||
</i>
|
||||
</button>
|
||||
`;
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach 'click' listener to "hide filmstrip" button
|
||||
*/
|
||||
registerListeners() {
|
||||
// Important:
|
||||
// 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',
|
||||
() => {
|
||||
// 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();
|
||||
},
|
||||
|
||||
/**
|
||||
* Registering toggle filmstrip shortcut
|
||||
* @private
|
||||
*/
|
||||
_registerToggleFilmstripShortcut() {
|
||||
const shortcut = 'F';
|
||||
const shortcutAttr = 'filmstripPopover';
|
||||
const description = 'keyboardShortcuts.toggleFilmstrip';
|
||||
|
||||
// Important:
|
||||
// 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 = () => {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.filmstrip',
|
||||
{
|
||||
enable: this.isFilmstripVisible()
|
||||
}));
|
||||
this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||
};
|
||||
|
||||
APP.keyboardshortcut.registerShortcut(
|
||||
shortcut,
|
||||
shortcutAttr,
|
||||
handler,
|
||||
description
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes classes of icon for showing down state
|
||||
*/
|
||||
showMenuDownIcon() {
|
||||
const icon = this.toggleFilmstripIcon;
|
||||
|
||||
if (icon) {
|
||||
icon.classList.add(this.iconMenuDownClassName);
|
||||
icon.classList.remove(this.iconMenuUpClassName);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes classes of icon for showing up state
|
||||
*/
|
||||
showMenuUpIcon() {
|
||||
const icon = this.toggleFilmstripIcon;
|
||||
|
||||
if (icon) {
|
||||
icon.classList.add(this.iconMenuUpClassName);
|
||||
icon.classList.remove(this.iconMenuDownClassName);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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) {
|
||||
const wasFilmstripVisible = this.isFilmstripVisible();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
this.filmstrip.toggleClass('hidden');
|
||||
|
||||
if (wasFilmstripVisible) {
|
||||
this.showMenuUpIcon();
|
||||
} else {
|
||||
this.showMenuDownIcon();
|
||||
}
|
||||
|
||||
if (this.eventEmitter) {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.TOGGLED_FILMSTRIP,
|
||||
!wasFilmstripVisible);
|
||||
}
|
||||
APP.store.dispatch(setFilmstripVisible(!wasFilmstripVisible));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -198,14 +25,7 @@ const Filmstrip = {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isFilmstripVisible() {
|
||||
return !this.filmstrip.hasClass('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Adjusts styles for filmstrip-only mode.
|
||||
*/
|
||||
setFilmstripOnly() {
|
||||
this.filmstrip.addClass('filmstrip__videos-filmstripOnly');
|
||||
return APP.store.getState()['features/filmstrip'].visible;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -275,9 +275,7 @@ LocalVideo.prototype._onContainerClick = function(event) {
|
||||
= $source.parents('.displayNameContainer').length > 0;
|
||||
const clickedOnPopover = $source.parents('.popover').length > 0
|
||||
|| classList.contains('popover');
|
||||
const ignoreClick = clickedOnDisplayName
|
||||
|| clickedOnPopover
|
||||
|| shouldDisplayTileView(APP.store.getState());
|
||||
const ignoreClick = clickedOnDisplayName || clickedOnPopover;
|
||||
|
||||
if (event.stopPropagation && !ignoreClick) {
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -627,8 +627,7 @@ RemoteVideo.prototype._onContainerClick = function(event) {
|
||||
const { classList } = event.target;
|
||||
|
||||
const ignoreClick = $source.parents('.popover').length > 0
|
||||
|| classList.contains('popover')
|
||||
|| shouldDisplayTileView(APP.store.getState());
|
||||
|| classList.contains('popover');
|
||||
|
||||
if (!ignoreClick) {
|
||||
this._togglePin();
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
import {
|
||||
LAYOUTS,
|
||||
getCurrentLayout,
|
||||
setTileView,
|
||||
shouldDisplayTileView
|
||||
} from '../../../react/features/video-layout';
|
||||
/* eslint-enable no-unused-vars */
|
||||
@@ -161,53 +162,6 @@ SmallVideo.prototype.isVisible = function() {
|
||||
return this.$container.is(':visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables / disables the device availability icons for this small video.
|
||||
* @param {enable} set to {true} to enable and {false} to disable
|
||||
*/
|
||||
SmallVideo.prototype.enableDeviceAvailabilityIcons = function(enable) {
|
||||
if (typeof enable === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.deviceAvailabilityIconsEnabled = enable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the device "non" availability icons.
|
||||
* @param devices the devices, which will be checked for availability
|
||||
*/
|
||||
SmallVideo.prototype.setDeviceAvailabilityIcons = function(devices) {
|
||||
if (!this.deviceAvailabilityIconsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const noMic = this.$container.find('.noMic');
|
||||
const noVideo = this.$container.find('.noVideo');
|
||||
|
||||
noMic.remove();
|
||||
noVideo.remove();
|
||||
if (!devices.audio) {
|
||||
this.container.appendChild(
|
||||
document.createElement('div')).setAttribute('class', 'noMic');
|
||||
}
|
||||
|
||||
if (!devices.video) {
|
||||
this.container.appendChild(
|
||||
document.createElement('div')).setAttribute('class', 'noVideo');
|
||||
}
|
||||
|
||||
if (!devices.audio && !devices.video) {
|
||||
noMic.css('background-position', '75%');
|
||||
noVideo.css('background-position', '25%');
|
||||
noVideo.css('background-color', 'transparent');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the type of the video displayed by this instance.
|
||||
* Note that this is a string without clearly defined or checked values, and
|
||||
|
||||
@@ -216,8 +216,6 @@ export class VideoContainer extends LargeContainer {
|
||||
this.emitter = emitter;
|
||||
this.resizeContainer = resizeContainer;
|
||||
|
||||
this.isVisible = false;
|
||||
|
||||
/**
|
||||
* Whether the background should fit the height of the container
|
||||
* (portrait) or fit the width of the container (landscape).
|
||||
@@ -603,17 +601,11 @@ export class VideoContainer extends LargeContainer {
|
||||
* TODO: refactor this since Temasys is no longer supported.
|
||||
*/
|
||||
show() {
|
||||
// its already visible
|
||||
if (this.isVisible) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.$wrapperParent.css('visibility', 'visible').fadeTo(
|
||||
FADE_DURATION_MS,
|
||||
1,
|
||||
() => {
|
||||
this.isVisible = true;
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
@@ -628,15 +620,9 @@ export class VideoContainer extends LargeContainer {
|
||||
// hide its avatar
|
||||
this.showAvatar(false);
|
||||
|
||||
// its already hidden
|
||||
if (!this.isVisible) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.$wrapperParent.fadeTo(FADE_DURATION_MS, 0, () => {
|
||||
this.$wrapperParent.css('visibility', 'hidden');
|
||||
this.isVisible = false;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -230,26 +230,6 @@ const VideoLayout = {
|
||||
video.setDeviceAvailabilityIcons(devices);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables/disables device availability icons for the given participant id.
|
||||
* The default value is {true}.
|
||||
* @param id the identifier of the participant
|
||||
* @param enable {true} to enable device availability icons
|
||||
*/
|
||||
enableDeviceAvailabilityIcons(id, enable) {
|
||||
let video;
|
||||
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
video = localVideoThumbnail;
|
||||
} else {
|
||||
video = remoteVideos[id];
|
||||
}
|
||||
|
||||
if (video) {
|
||||
video.enableDeviceAvailabilityIcons(enable);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows/hides local video.
|
||||
* @param {boolean} true to make the local video visible, false - otherwise
|
||||
|
||||
@@ -55,10 +55,6 @@ const KeyboardShortcut = {
|
||||
APP.UI.clickOnVideo(num);
|
||||
}
|
||||
|
||||
// esc while the smileys are visible hides them
|
||||
} else if (key === 'ESCAPE'
|
||||
&& $('#smileysContainer').is(':visible')) {
|
||||
APP.UI.toggleSmileys();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
63
modules/util/TaskQueue.js
Normal file
@@ -0,0 +1,63 @@
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Manages a queue of functions where the current function in progress will
|
||||
* automatically execute the next queued function.
|
||||
*/
|
||||
export class TaskQueue {
|
||||
/**
|
||||
* Creates a new instance of {@link TaskQueue} and sets initial instance
|
||||
* variable values.
|
||||
*/
|
||||
constructor() {
|
||||
this._queue = [];
|
||||
this._currentTask = null;
|
||||
|
||||
this._onTaskComplete = this._onTaskComplete.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new function to the queue. It will be immediately invoked if no
|
||||
* other functions are queued.
|
||||
*
|
||||
* @param {Function} taskFunction - The function to be queued for execution.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
enqueue(taskFunction) {
|
||||
this._queue.push(taskFunction);
|
||||
this._executeNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* If no queued task is currently executing, invokes the first task in the
|
||||
* queue if any.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_executeNext() {
|
||||
if (this._currentTask) {
|
||||
logger.warn('Task queued while a task is in progress.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._currentTask = this._queue.shift() || null;
|
||||
|
||||
if (this._currentTask) {
|
||||
this._currentTask(this._onTaskComplete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares to invoke the next function in the queue.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTaskComplete() {
|
||||
this._currentTask = null;
|
||||
this._executeNext();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { TaskQueue } from './TaskQueue';
|
||||
|
||||
/**
|
||||
* Create deferred object.
|
||||
*
|
||||
@@ -13,3 +15,12 @@ export function createDeferred() {
|
||||
|
||||
return deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of {@link TaskQueue}.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function createTaskQueue() {
|
||||
return new TaskQueue();
|
||||
}
|
||||
|
||||
7141
package-lock.json
generated
95
package.json
@@ -15,25 +15,25 @@
|
||||
"author": "",
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"@atlaskit/avatar": "8.0.5",
|
||||
"@atlaskit/button": "5.4.2",
|
||||
"@atlaskit/checkbox": "2.0.2",
|
||||
"@atlaskit/dropdown-menu": "3.10.2",
|
||||
"@atlaskit/droplist": "4.11.1",
|
||||
"@atlaskit/field-text": "4.0.1",
|
||||
"@atlaskit/field-text-area": "1.2.0",
|
||||
"@atlaskit/avatar": "14.0.10",
|
||||
"@atlaskit/button": "9.0.8",
|
||||
"@atlaskit/checkbox": "4.0.6",
|
||||
"@atlaskit/dropdown-menu": "6.1.12",
|
||||
"@atlaskit/field-text": "7.0.10",
|
||||
"@atlaskit/field-text-area": "4.0.9",
|
||||
"@atlaskit/flag": "6.1.0",
|
||||
"@atlaskit/icon": "10.0.0",
|
||||
"@atlaskit/icon": "13.8.1",
|
||||
"@atlaskit/inline-dialog": "5.3.0",
|
||||
"@atlaskit/inline-message": "4.0.0",
|
||||
"@atlaskit/layer-manager": "2.8.0",
|
||||
"@atlaskit/lozenge": "3.4.2",
|
||||
"@atlaskit/modal-dialog": "3.4.0",
|
||||
"@atlaskit/multi-select": "7.1.3",
|
||||
"@atlaskit/spinner": "4.0.0",
|
||||
"@atlaskit/tabs": "4.0.1",
|
||||
"@atlaskit/theme": "2.4.0",
|
||||
"@atlaskit/tooltip": "9.1.1",
|
||||
"@atlaskit/inline-message": "7.0.4",
|
||||
"@atlaskit/layer-manager": "5.0.12",
|
||||
"@atlaskit/lozenge": "6.2.0",
|
||||
"@atlaskit/modal-dialog": "6.0.12",
|
||||
"@atlaskit/multi-select": "11.0.6",
|
||||
"@atlaskit/spinner": "9.0.8",
|
||||
"@atlaskit/tabs": "8.0.8",
|
||||
"@atlaskit/theme": "6.0.2",
|
||||
"@atlaskit/toggle": "5.0.8",
|
||||
"@atlaskit/tooltip": "12.0.14",
|
||||
"@microsoft/microsoft-graph-client": "1.1.0",
|
||||
"@webcomponents/url": "0.7.1",
|
||||
"autosize": "1.18.13",
|
||||
@@ -47,61 +47,70 @@
|
||||
"jquery-contextmenu": "2.4.5",
|
||||
"jquery-i18next": "1.2.0",
|
||||
"js-md5": "0.6.1",
|
||||
"js-utils": "github:jitsi/js-utils#446497893023aa8dec403e0e4e35a22cae6bc87d",
|
||||
"jsc-android": "224109.1.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#095a4485f2e6749f5b4fe91da7088aed359ee728",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#34f728456eb5e8e9817bdaf999e4702bac2ee6ce",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.4",
|
||||
"moment": "2.19.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"postis": "2.2.0",
|
||||
"prop-types": "15.6.0",
|
||||
"react": "16.3.1",
|
||||
"react-dom": "16.3.1",
|
||||
"react-i18next": "4.8.0",
|
||||
"react-native": "0.55.4",
|
||||
"react": "16.5.0",
|
||||
"react-dom": "16.5.0",
|
||||
"react-i18next": "7.13.0",
|
||||
"react-native": "0.57.1",
|
||||
"react-native-background-timer": "2.0.0",
|
||||
"react-native-calendar-events": "github:jitsi/react-native-calendar-events#03babdb99e7fea3539796804cecdef8a907f2a3f",
|
||||
"react-native-callstats": "3.52.0",
|
||||
"react-native-fast-image": "4.0.14",
|
||||
"react-native-calendar-events": "github:wmcmahan/react-native-calendar-events#056807286da610d884fb6b4c8ca187a767b261f7",
|
||||
"react-native-callstats": "3.53.4",
|
||||
"react-native-fast-image": "github:jitsi/react-native-fast-image#1f8c93a5584869848d75cc9b946beb9688efe285",
|
||||
"react-native-google-signin": "1.0.0-rc6",
|
||||
"react-native-immersive": "1.1.0",
|
||||
"react-native-keep-awake": "2.0.6",
|
||||
"react-native-linear-gradient": "2.4.0",
|
||||
"react-native-locale-detector": "github:jitsi/react-native-locale-detector#845281e9fd4af756f6d0f64afe5cce08c63e5ee9",
|
||||
"react-native-permissions": "1.1.1",
|
||||
"react-native-prompt": "1.0.0",
|
||||
"react-native-sound": "0.10.9",
|
||||
"react-native-vector-icons": "4.4.2",
|
||||
"react-native-webrtc": "github:jitsi/react-native-webrtc#bed49210a51cf53081954028589d720381e7cf40",
|
||||
"react-native-swipeout": "2.3.6",
|
||||
"react-native-vector-icons": "6.0.2",
|
||||
"react-native-webrtc": "github:jitsi/react-native-webrtc#6322a9b5a38ce590cfaea4041072ea87c8dbf558",
|
||||
"react-redux": "5.0.7",
|
||||
"react-transition-group": "2.4.0",
|
||||
"redux": "4.0.0",
|
||||
"redux-thunk": "2.2.0",
|
||||
"styled-components": "1.4.6",
|
||||
"styled-components": "3.4.9",
|
||||
"uuid": "3.1.0",
|
||||
"xmldom": "0.1.27"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "6.26.0",
|
||||
"babel-eslint": "8.0.3",
|
||||
"babel-loader": "7.1.2",
|
||||
"babel-preset-env": "1.6.1",
|
||||
"babel-preset-react": "6.24.1",
|
||||
"babel-preset-stage-1": "6.24.1",
|
||||
"@babel/core": "7.1.2",
|
||||
"@babel/preset-env": "7.1.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "7.1.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.0.0",
|
||||
"@babel/runtime": "7.1.2",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-loader": "8.0.4",
|
||||
"clean-css": "3.4.25",
|
||||
"css-loader": "0.28.7",
|
||||
"eslint": "4.12.1",
|
||||
"eslint": "5.6.1",
|
||||
"eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#7474f6668515eb5852f1273dc5a50b940a550d3f",
|
||||
"eslint-plugin-flowtype": "2.39.1",
|
||||
"eslint-plugin-import": "2.8.0",
|
||||
"eslint-plugin-jsdoc": "3.2.0",
|
||||
"eslint-plugin-react": "7.5.1",
|
||||
"eslint-plugin-react-native": "3.2.0",
|
||||
"eslint-plugin-flowtype": "2.50.3",
|
||||
"eslint-plugin-import": "2.14.0",
|
||||
"eslint-plugin-jsdoc": "3.8.0",
|
||||
"eslint-plugin-react": "7.11.1",
|
||||
"eslint-plugin-react-native": "3.3.0",
|
||||
"expose-loader": "0.7.4",
|
||||
"file-loader": "1.1.5",
|
||||
"flow-bin": "0.67.1",
|
||||
"flow-bin": "0.78.0",
|
||||
"imports-loader": "0.7.1",
|
||||
"node-sass": "4.8.3",
|
||||
"metro-react-native-babel-preset": "0.47.0",
|
||||
"node-sass": "4.10.0",
|
||||
"precommit-hook": "3.0.0",
|
||||
"string-replace-loader": "1.3.0",
|
||||
"style-loader": "0.19.0",
|
||||
|
||||
@@ -4,5 +4,8 @@ module.exports = {
|
||||
'eslint-config-jitsi/jsdoc',
|
||||
'eslint-config-jitsi/react',
|
||||
'.eslintrc-react-native.js'
|
||||
]
|
||||
],
|
||||
'rules': {
|
||||
'react/no-deprecated': 0
|
||||
}
|
||||
};
|
||||
|
||||