mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-05 06:12:28 +00:00
Compare commits
84 Commits
3035
...
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 |
18
.flowconfig
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,9 +45,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:22.2.0'
|
||||
compile 'com.google.android.gms:play-services-auth:15.0.0'
|
||||
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')
|
||||
|
||||
@@ -50,6 +56,58 @@ dependencies {
|
||||
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
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
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
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.
|
||||
|
||||
@@ -7,7 +7,7 @@ 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
|
||||
@@ -153,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
|
||||
@@ -179,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
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -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
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
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,135 +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-google-signin")) {
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
|
||||
api 'com.facebook.react:react-native:+'
|
||||
|
||||
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'
|
||||
}
|
||||
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 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')
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
// Build process helpers
|
||||
//
|
||||
|
||||
void runBefore(String dependentTaskName, Task task) {
|
||||
Task dependentTask = tasks.findByPath(dependentTaskName);
|
||||
if (dependentTask != null) {
|
||||
dependentTask.dependsOn task
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
android.buildTypes.all { buildType ->
|
||||
def buildNameCapitalized = "${buildType.name.capitalize()}"
|
||||
def bundlePath = "${buildDir}/intermediates/bundles/${buildType.name}"
|
||||
currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
|
||||
currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)
|
||||
|
||||
// Bundle fonts in react-native-vector-icons.
|
||||
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) {
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -97,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 {
|
||||
@@ -424,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:
|
||||
@@ -1819,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));
|
||||
@@ -2033,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());
|
||||
|
||||
14
config.js
14
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',
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
206
css/_chat.scss
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
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
@extend %navigate-section-list-text;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16px;
|
||||
display: block;
|
||||
}
|
||||
.navigate-section-list {
|
||||
position: relative;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -49,41 +48,118 @@ body.welcome-page {
|
||||
}
|
||||
|
||||
#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;
|
||||
width: 650px;
|
||||
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;
|
||||
right: 10px;
|
||||
top: 32px;
|
||||
right: 32px;
|
||||
z-index: $zindex2;
|
||||
|
||||
* {
|
||||
cursor: pointer;
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
28
doc/mobile-dropbox.md
Normal file
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
|
||||
@@ -1,22 +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 auto-signed build too, just
|
||||
retrieve the signing hash.
|
||||
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 and iOS client IDs are auto generated during the Firebase project
|
||||
creation. Find them in the Google Developer console:
|
||||
https://console.developers.google.com/
|
||||
- Make sure your config reflects these IDs so then the Redux state of the
|
||||
feature ```features/base/config``` contains variables
|
||||
```googleApiApplicationClientID``` and ```googleApiIOSClientID``` with the
|
||||
respective values.
|
||||
- Add your iOS client ID as an application URL schema into
|
||||
```ios/app/src/Info.plist``` (replacing placeholder).
|
||||
- Enable YouTube API access on the developer console (see above) for live
|
||||
- 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
|
||||
```
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 612 B |
Binary file not shown.
|
Before Width: | Height: | Size: 889 B |
BIN
images/noMic.png
BIN
images/noMic.png
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -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',
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
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)
|
||||
- GoogleSignIn (4.2.0):
|
||||
- glog (0.3.5)
|
||||
- GoogleSignIn (4.3.0):
|
||||
- "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)"
|
||||
- "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)"
|
||||
- GTMOAuth2 (~> 1.0)
|
||||
@@ -27,8 +27,9 @@ PODS:
|
||||
- GTMSessionFetcher/Core (1.2.0)
|
||||
- GTMSessionFetcher/Full (1.2.0):
|
||||
- GTMSessionFetcher/Core (= 1.2.0)
|
||||
- React (0.55.4):
|
||||
- React/Core (= 0.55.4)
|
||||
- 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.2):
|
||||
@@ -42,48 +43,48 @@ PODS:
|
||||
- React
|
||||
- react-native-locale-detector (1.0.0):
|
||||
- React
|
||||
- react-native-webrtc (1.63.0):
|
||||
- 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
|
||||
- RNGoogleSignin (1.0.0-rc3):
|
||||
- RNGoogleSignin (1.0.0-rc6):
|
||||
- GoogleSignIn
|
||||
- React
|
||||
- RNSound (0.10.9):
|
||||
@@ -91,18 +92,19 @@ PODS:
|
||||
- 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`)
|
||||
@@ -132,6 +134,7 @@ SPEC REPOS:
|
||||
- GoogleToolboxForMac
|
||||
- GTMOAuth2
|
||||
- GTMSessionFetcher
|
||||
- ObjectiveDropboxOfficial
|
||||
- SDWebImage
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -166,15 +169,16 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
DoubleConversion: e22e0762848812a87afd67ffda3998d9ef29170c
|
||||
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
|
||||
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
|
||||
Folly: 211775e49d8da0ca658aebc8eab89d642935755c
|
||||
glog: 1de0bb937dccdc981596d3b5825ebfb765017ded
|
||||
GoogleSignIn: 591e46382014e591269f862ba6e7bc0fbd793532
|
||||
Folly: c89ac2d5c6ab169cd7397ef27485c44f35f742c7
|
||||
glog: e8acf0ebbf99759d3ff18c86c292a5898282dcde
|
||||
GoogleSignIn: 11183592dc63e105475c7305a325045ff95e02b7
|
||||
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
|
||||
GTMOAuth2: c77fe325e4acd453837e72d91e3b5f13116857b2
|
||||
GTMSessionFetcher: 0c4baf0a73acd0041bf9f71ea018deedab5ea84e
|
||||
React: aa2040dbb6f317b95314968021bd2888816e03d5
|
||||
ObjectiveDropboxOfficial: aa792e0556ceb7b72955fa29a2709072f6e35fd9
|
||||
React: 1fe0eb13d90b625d94c3b117c274dcfd2e760e11
|
||||
react-native-background-timer: 63dcbf37dbcf294b5c6c071afcdc661fa06a7594
|
||||
react-native-calendar-events: fe6fbc8ed337a7423c98f2c9012b25f20444de09
|
||||
react-native-fast-image: cba3d9bf9c2cf8ddb643d887a686c53a5dd90a2c
|
||||
@@ -183,10 +187,10 @@ SPEC CHECKSUMS:
|
||||
react-native-webrtc: 31b6d3f1e3e2ce373aa43fd682b04367250f807d
|
||||
RNGoogleSignin: 44debd8c359a662c0e2d585952e88b985bf78008
|
||||
RNSound: b360b3862d3118ed1c74bb9825696b5957686ac4
|
||||
RNVectorIcons: c0dbfbf6068fefa240c37b0f71bd03b45dddac44
|
||||
RNVectorIcons: 8c52e1e8da1153613fdef44748e865c25556cb9c
|
||||
SDWebImage: 624d6e296c69b244bcede364c72ae0430ac14681
|
||||
yoga: a23273df0088bf7f2bb7e5d7b00044ea57a2a54a
|
||||
yoga: b1ce48b6cf950b98deae82838f5173ea7cf89e85
|
||||
|
||||
PODFILE CHECKSUM: da74c08f6eb674668c49d8d799f8d9e2476a9fc5
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
@@ -378,10 +408,14 @@
|
||||
"${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",
|
||||
@@ -394,10 +428,14 @@
|
||||
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",
|
||||
@@ -433,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 */,
|
||||
@@ -456,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;
|
||||
@@ -463,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;
|
||||
@@ -515,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;
|
||||
@@ -522,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;
|
||||
@@ -547,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
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
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,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é"
|
||||
}
|
||||
}
|
||||
@@ -59,16 +59,18 @@
|
||||
"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": "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": " ",
|
||||
@@ -158,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": {
|
||||
@@ -461,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",
|
||||
@@ -483,6 +488,7 @@
|
||||
"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",
|
||||
@@ -502,6 +508,9 @@
|
||||
"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",
|
||||
@@ -546,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",
|
||||
|
||||
@@ -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
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,6 +19,7 @@ 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';
|
||||
@@ -89,9 +87,6 @@ const UIListeners = new Map([
|
||||
], [
|
||||
UIEvents.SHARED_VIDEO_CLICKED,
|
||||
() => sharedVideoManager && sharedVideoManager.toggleSharedVideo()
|
||||
], [
|
||||
UIEvents.TOGGLE_CHAT,
|
||||
() => UI.toggleChat()
|
||||
], [
|
||||
UIEvents.TOGGLE_FILMSTRIP,
|
||||
() => UI.toggleFilmstrip()
|
||||
@@ -175,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
|
||||
@@ -193,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));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -276,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);
|
||||
@@ -295,24 +274,15 @@ UI.start = function() {
|
||||
if (interfaceConfig.filmStripOnly) {
|
||||
$('body').addClass('filmstrip-only');
|
||||
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;
|
||||
@@ -338,7 +308,6 @@ UI.bindEvents = () => {
|
||||
*
|
||||
*/
|
||||
function onResize() {
|
||||
SideContainerToggler.resize();
|
||||
VideoLayout.resizeVideoArea();
|
||||
}
|
||||
|
||||
@@ -498,11 +467,6 @@ UI.updateUserStatus = (user, status) => {
|
||||
{ status: UIUtil.escapeHtml(status) });
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles smileys in the chat.
|
||||
*/
|
||||
UI.toggleSmileys = () => Chat.toggleSmileys();
|
||||
|
||||
/**
|
||||
* Toggles filmstrip.
|
||||
*/
|
||||
@@ -519,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.
|
||||
@@ -744,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,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;
|
||||
}
|
||||
@@ -162,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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
5696
package-lock.json
generated
5696
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
58
package.json
58
package.json
@@ -32,6 +32,7 @@
|
||||
"@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",
|
||||
@@ -46,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#615934a78afb3b501976263f5f916efed7af0080",
|
||||
"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:wmcmahan/react-native-calendar-events#cb2731db6684a49b4343e09de7f9c2fcc68bcd9b",
|
||||
"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-rc3",
|
||||
"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-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#be3de15bb988cfabbb62cd4b3b06f4c920ee5ba0",
|
||||
"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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -413,7 +413,7 @@ export function createRemoteVideoMenuButtonEvent(buttonName, attributes) {
|
||||
|
||||
/**
|
||||
* Creates an event indicating that an action related to screen sharing
|
||||
* occurred (e.g. it was started or stopped).
|
||||
* occurred (e.g. It was started or stopped).
|
||||
*
|
||||
* @param {string} action - The action which occurred.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
@@ -466,7 +466,7 @@ export function createSharedVideoEvent(action, attributes = {}) {
|
||||
* Creates an event associated with a shortcut being pressed, released or
|
||||
* triggered. By convention, where appropriate an attribute named 'enable'
|
||||
* should be used to indicate the action which resulted by the shortcut being
|
||||
* pressed (e.g. whether screen sharing was enabled or disabled).
|
||||
* pressed (e.g. Whether screen sharing was enabled or disabled).
|
||||
*
|
||||
* @param {string} shortcut - The identifier of the shortcut which produced
|
||||
* an action.
|
||||
@@ -512,7 +512,7 @@ export function createStartAudioOnlyEvent(audioOnly) {
|
||||
*
|
||||
* @param {string} source - The source of the configuration, 'local' or
|
||||
* 'remote' depending on whether it comes from the static configuration (i.e.
|
||||
* config.js) or comes dynamically from Jicofo.
|
||||
* {@code config.js}) or comes dynamically from Jicofo.
|
||||
* @param {boolean} audioMute - Whether the configuration requests that audio
|
||||
* is muted.
|
||||
* @param {boolean} videoMute - Whether the configuration requests that video
|
||||
@@ -573,7 +573,7 @@ export function createSyncTrackStateEvent(mediaType, muted) {
|
||||
* Creates an event associated with a toolbar button being clicked/pressed. By
|
||||
* convention, where appropriate an attribute named 'enable' should be used to
|
||||
* indicate the action which resulted by the shortcut being pressed (e.g.
|
||||
* whether screen sharing was enabled or disabled).
|
||||
* Whether screen sharing was enabled or disabled).
|
||||
*
|
||||
* @param {string} buttonName - The identifier of the toolbar button which was
|
||||
* clicked/pressed.
|
||||
@@ -595,9 +595,9 @@ export function createToolbarEvent(buttonName, attributes = {}) {
|
||||
* Creates an event which indicates that a local track was muted.
|
||||
*
|
||||
* @param {string} mediaType - The track's media type ('audio' or 'video').
|
||||
* @param {string} reason - The reason the track was muted (e.g. it was
|
||||
* @param {string} reason - The reason the track was muted (e.g. It was
|
||||
* triggered by the "initial mute" option, or a previously muted track was
|
||||
* replaced (e.g. when a new device was used)).
|
||||
* replaced (e.g. When a new device was used)).
|
||||
* @param {boolean} muted - Whether the track was muted or unmuted.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
|
||||
@@ -128,10 +128,10 @@ export class AbstractApp extends BaseApp<Props, *> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates this {@code AbstractApp} to (i.e. opens) a specific URL.
|
||||
* Navigates this {@code AbstractApp} to (i.e. Opens) a specific URL.
|
||||
*
|
||||
* @param {Object|string} url - The URL to navigate this {@code AbstractApp}
|
||||
* to (i.e. the URL to open).
|
||||
* to (i.e. The URL to open).
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
/**
|
||||
@@ -13,27 +14,24 @@ const AUDIO_LEVEL_DOTS = 5;
|
||||
*/
|
||||
const CENTER_DOT_INDEX = Math.floor(AUDIO_LEVEL_DOTS / 2);
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AudioLevelIndicator}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current audio level to display. The value should be a number between
|
||||
* 0 and 1.
|
||||
*/
|
||||
audioLevel: number
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a ReactElement responsible for drawing audio levels.
|
||||
*
|
||||
* @extends {Component}
|
||||
*/
|
||||
class AudioLevelIndicator extends Component {
|
||||
/**
|
||||
* {@code AudioLevelIndicator}'s property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The current audio level to display. The value should be a number
|
||||
* between 0 and 1.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
audioLevel: PropTypes.number
|
||||
};
|
||||
|
||||
class AudioLevelIndicator extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,7 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
* password + guest access configuration. Refer to {@link LoginDialog} for more
|
||||
* info.
|
||||
*
|
||||
* @param {string} id - The XMPP user's ID (e.g. user@domain.com).
|
||||
* @param {string} id - The XMPP user's ID (e.g. {@code user@domain.com}).
|
||||
* @param {string} password - The XMPP user's password.
|
||||
* @param {JitsiConference} conference - The conference for which the local
|
||||
* participant's role will be upgraded.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, TextInput, View } from 'react-native';
|
||||
import { connect as reduxConnect } from 'react-redux';
|
||||
@@ -11,6 +12,65 @@ import { JitsiConnectionErrors } from '../../base/lib-jitsi-meet';
|
||||
import { authenticateAndUpgradeRole, cancelLogin } from '../actions';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@link Component} props of {@link LoginDialog}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* {@link JitsiConference} that needs authentication - will hold a valid
|
||||
* value in XMPP login + guest access mode.
|
||||
*/
|
||||
_conference: Object,
|
||||
|
||||
/**
|
||||
* The server hosts specified in the global config.
|
||||
*/
|
||||
_configHosts: Object,
|
||||
|
||||
/**
|
||||
* Indicates if the dialog should display "connecting" status message.
|
||||
*/
|
||||
_connecting: boolean,
|
||||
|
||||
/**
|
||||
* The error which occurred during login/authentication.
|
||||
*/
|
||||
_error: Object,
|
||||
|
||||
/**
|
||||
* The progress in the floating range between 0 and 1 of the authenticating
|
||||
* and upgrading the role of the local participant/user.
|
||||
*/
|
||||
_progress: number,
|
||||
|
||||
/**
|
||||
* Redux store dispatch method.
|
||||
*/
|
||||
dispatch: Dispatch<*>,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@link Component} state of {@link LoginDialog}.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* The user entered password for the conference.
|
||||
*/
|
||||
password: string,
|
||||
|
||||
/**
|
||||
* The user entered local participant name.
|
||||
*/
|
||||
username: string
|
||||
};
|
||||
|
||||
/**
|
||||
* Dialog asks user for username and password.
|
||||
*
|
||||
@@ -38,58 +98,14 @@ import styles from './styles';
|
||||
* See {@link https://github.com/jitsi/jicofo#secure-domain} for a description
|
||||
* of the configuration parameters.
|
||||
*/
|
||||
class LoginDialog extends Component {
|
||||
/**
|
||||
* LoginDialog component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* {@link JitsiConference} that needs authentication - will hold a valid
|
||||
* value in XMPP login + guest access mode.
|
||||
*/
|
||||
_conference: PropTypes.object,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
_configHosts: PropTypes.object,
|
||||
|
||||
/**
|
||||
* Indicates if the dialog should display "connecting" status message.
|
||||
*/
|
||||
_connecting: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The error which occurred during login/authentication.
|
||||
*/
|
||||
_error: PropTypes.object,
|
||||
|
||||
/**
|
||||
* The progress in the floating range between 0 and 1 of the
|
||||
* authenticating and upgrading the role of the local participant/user.
|
||||
*/
|
||||
_progress: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Redux store dispatch method.
|
||||
*/
|
||||
dispatch: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: PropTypes.func
|
||||
};
|
||||
|
||||
class LoginDialog extends Component<Props, State> {
|
||||
/**
|
||||
* Initializes a new LoginDialog instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -119,7 +135,7 @@ class LoginDialog extends Component {
|
||||
} = this.props;
|
||||
|
||||
let messageKey;
|
||||
let messageOptions;
|
||||
const messageOptions = {};
|
||||
|
||||
if (progress && progress < 1) {
|
||||
messageKey = 'connection.FETCH_SESSION_ID';
|
||||
@@ -142,7 +158,6 @@ class LoginDialog extends Component {
|
||||
}
|
||||
} else if (name) {
|
||||
messageKey = 'dialog.connectErrorWithMsg';
|
||||
messageOptions || (messageOptions = {});
|
||||
messageOptions.msg = `${name} ${error.message}`;
|
||||
}
|
||||
}
|
||||
@@ -170,7 +185,7 @@ class LoginDialog extends Component {
|
||||
<Text style = { styles.dialogText }>
|
||||
{
|
||||
messageKey
|
||||
? t(messageKey, messageOptions || {})
|
||||
? t(messageKey, messageOptions)
|
||||
: connecting
|
||||
? t('connection.CONNECTING')
|
||||
: ''
|
||||
@@ -181,6 +196,8 @@ class LoginDialog extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_onUsernameChange: (string) => void;
|
||||
|
||||
/**
|
||||
* Called when user edits the username.
|
||||
*
|
||||
@@ -194,6 +211,8 @@ class LoginDialog extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
_onPasswordChange: (string) => void;
|
||||
|
||||
/**
|
||||
* Called when user edits the password.
|
||||
*
|
||||
@@ -207,6 +226,8 @@ class LoginDialog extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
_onCancel: () => void;
|
||||
|
||||
/**
|
||||
* Notifies this LoginDialog that it has been dismissed by cancel.
|
||||
*
|
||||
@@ -217,6 +238,8 @@ class LoginDialog extends Component {
|
||||
this.props.dispatch(cancelLogin());
|
||||
}
|
||||
|
||||
_onLogin: () => void;
|
||||
|
||||
/**
|
||||
* Notifies this LoginDialog that the login button (OK) has been pressed by
|
||||
* the user.
|
||||
|
||||
@@ -198,8 +198,8 @@ export function getNearestReceiverVideoQualityLevel(availableHeight: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an error thrown by the backend (i.e. lib-jitsi-meet) while
|
||||
* manipulating a conference participant (e.g. pin or select participant).
|
||||
* Handle an error thrown by the backend (i.e. {@code lib-jitsi-meet}) while
|
||||
* manipulating a conference participant (e.g. Pin or select participant).
|
||||
*
|
||||
* @param {Error} err - The Error which was thrown by the backend while
|
||||
* manipulating a conference participant and which is to be handled.
|
||||
|
||||
@@ -28,8 +28,13 @@ import { VIDEO_QUALITY_LEVELS } from './constants';
|
||||
import { isRoomValid } from './functions';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
conference: undefined,
|
||||
joining: undefined,
|
||||
leaving: undefined,
|
||||
locked: undefined,
|
||||
maxReceiverVideoQuality: VIDEO_QUALITY_LEVELS.HIGH,
|
||||
password: undefined,
|
||||
passwordRequired: undefined,
|
||||
preferredReceiverVideoQuality: VIDEO_QUALITY_LEVELS.HIGH
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export type ConnectionFailedError = {
|
||||
/**
|
||||
* Opens new connection.
|
||||
*
|
||||
* @param {string} [id] - The XMPP user's ID (e.g. user@server.com).
|
||||
* @param {string} [id] - The XMPP user's ID (e.g. {@code user@server.com}).
|
||||
* @param {string} [password] - The XMPP user's password.
|
||||
* @returns {Function}
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { toState } from '../redux';
|
||||
|
||||
/**
|
||||
* Retrieves a simplified version of the conference/location URL stripped of URL
|
||||
* params (i.e. query/search and hash) which should be used for sending invites.
|
||||
* params (i.e. Query/search and hash) which should be used for sending invites.
|
||||
*
|
||||
* @param {Function|Object} stateOrGetState - The redux state or redux's
|
||||
* {@code getState} function.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import PropTypes from 'prop-types';
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link DialogContainer}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The component to render.
|
||||
*/
|
||||
_component: Function,
|
||||
|
||||
/**
|
||||
* The props to pass to the component that will be rendered.
|
||||
*/
|
||||
_componentProps: Object,
|
||||
|
||||
/**
|
||||
* True if the UI is in a compact state where we don't show dialogs.
|
||||
*/
|
||||
_reducedUI: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a DialogContainer responsible for showing all dialogs. We will
|
||||
* need a separate container so we can handle multiple dialogs by showing them
|
||||
* simultaneously or queuing them.
|
||||
*/
|
||||
export class DialogContainer extends Component {
|
||||
/**
|
||||
* DialogContainer component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The component to render.
|
||||
*/
|
||||
_component: PropTypes.func,
|
||||
|
||||
/**
|
||||
* The props to pass to the component that will be rendered.
|
||||
*/
|
||||
_componentProps: PropTypes.object,
|
||||
|
||||
/**
|
||||
* True if the UI is in a compact state where we don't show dialogs.
|
||||
*/
|
||||
_reducedUI: PropTypes.bool
|
||||
};
|
||||
|
||||
export class DialogContainer extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import { Container, Text } from '../../react';
|
||||
|
||||
import { dialog as styles } from './styles';
|
||||
|
||||
@@ -31,9 +32,9 @@ export default class DialogContent extends Component<Props> {
|
||||
: children;
|
||||
|
||||
return (
|
||||
<View style = { styles.dialogContainer }>
|
||||
<Container style = { styles.dialogContainer }>
|
||||
{ childrenComponent }
|
||||
</View>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
5
react/features/base/dialog/components/styles.web.js
Normal file
5
react/features/base/dialog/components/styles.web.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Placeholder styles for web to be able to use cross platform components
|
||||
* unmodified such as {@code DialogContent}.
|
||||
*/
|
||||
export const dialog = {};
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
// FIXME The import of react-native-vector-icons makes the file native-specific
|
||||
// but the file's name and/or location (within the directory structure) don't
|
||||
// reflect that, it suggests the file is platform-independent.
|
||||
@@ -10,3 +12,8 @@ import icoMoonConfig from './jitsi.json';
|
||||
* Creates the Jitsi icon set from the ico moon project config file.
|
||||
*/
|
||||
export const Icon = createIconSetFromIcoMoon(icoMoonConfig);
|
||||
|
||||
// Dynamically load font on iOS
|
||||
if (Platform.OS === 'ios') {
|
||||
Icon.loadFont('jitsi.ttf');
|
||||
}
|
||||
|
||||
@@ -5,13 +5,15 @@ import { translate as reactI18nextTranslate } from 'react-i18next';
|
||||
* Wraps a specific React Component in order to enable translations in it.
|
||||
*
|
||||
* @param {Component} component - The React Component to wrap.
|
||||
* @param {Object} options - Additional options to pass into react-i18next's
|
||||
* initialization.
|
||||
* @returns {Component} The React Component which wraps {@link component} and
|
||||
* enables translations in it.
|
||||
*/
|
||||
export function translate(component) {
|
||||
export function translate(component, options = { wait: true }) {
|
||||
// Use the default list of namespaces.
|
||||
return (
|
||||
reactI18nextTranslate([ 'main', 'languages' ], { wait: true })(
|
||||
reactI18nextTranslate([ 'main', 'languages' ], options)(
|
||||
component));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ declare var interfaceConfig: Object;
|
||||
* @public
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const LANGUAGES = Object.keys(LANGUAGES_RESOURCES);
|
||||
export const LANGUAGES: Array<string> = Object.keys(LANGUAGES_RESOURCES);
|
||||
|
||||
/**
|
||||
* The default language.
|
||||
|
||||
@@ -12,6 +12,7 @@ export type Props = {
|
||||
/**
|
||||
* Abstract class for the {@code CircularLabel} component.
|
||||
*/
|
||||
export default class AbstractCircularLabel<P: Props> extends Component<P> {
|
||||
export default class AbstractCircularLabel<P: Props, S: *>
|
||||
extends Component<P, S> {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Animated, Text } from 'react-native';
|
||||
|
||||
import { combineStyles, type StyleType } from '../../styles';
|
||||
|
||||
@@ -9,36 +9,142 @@ import AbstractCircularLabel, {
|
||||
} from './AbstractCircularLabel';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* Const for status string 'in progress'.
|
||||
*/
|
||||
const STATUS_IN_PROGRESS = 'in_progress';
|
||||
|
||||
/**
|
||||
* Const for status string 'off'.
|
||||
*/
|
||||
const STATUS_OFF = 'off';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* Status of the label. This prop adds some additional styles based on its
|
||||
* value. E.g. if status = off, it will render the label symbolising that
|
||||
* the thing it displays (e.g. recording) is off.
|
||||
*/
|
||||
status: ('in_progress' | 'off' | 'on'),
|
||||
|
||||
/**
|
||||
* Style of the label.
|
||||
*/
|
||||
style?: ?StyleType
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* An animation object handling the opacity changes of the in progress
|
||||
* label.
|
||||
*/
|
||||
pulseAnimation: Object
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a circular indicator to be used for status icons, such as recording
|
||||
* on, audio-only conference, video quality and similar.
|
||||
*/
|
||||
export default class CircularLabel extends AbstractCircularLabel<Props> {
|
||||
export default class CircularLabel extends AbstractCircularLabel<Props, State> {
|
||||
/**
|
||||
* A reference to the started animation of this label.
|
||||
*/
|
||||
animationReference: Object;
|
||||
|
||||
/**
|
||||
* Instantiates a new instance of {@code CircularLabel}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
pulseAnimation: new Animated.Value(0)
|
||||
};
|
||||
|
||||
this._maybeToggleAnimation({}, props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code Component#componentWillReceiveProps}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
this._maybeToggleAnimation(this.props, newProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React {@link Component}'s render.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { label, style } = this.props;
|
||||
const { status, label, style } = this.props;
|
||||
|
||||
let extraStyle = null;
|
||||
|
||||
switch (status) {
|
||||
case STATUS_IN_PROGRESS:
|
||||
extraStyle = {
|
||||
opacity: this.state.pulseAnimation
|
||||
};
|
||||
break;
|
||||
case STATUS_OFF:
|
||||
extraStyle = styles.labelOff;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style = {
|
||||
combineStyles(styles.indicatorContainer, style)
|
||||
}>
|
||||
<Animated.View
|
||||
style = { [
|
||||
combineStyles(styles.indicatorContainer, style),
|
||||
extraStyle
|
||||
] }>
|
||||
<Text style = { styles.indicatorText }>
|
||||
{ label }
|
||||
</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the animation has to be started or stopped and acts
|
||||
* accordingly.
|
||||
*
|
||||
* @param {Props} oldProps - The previous values of the Props.
|
||||
* @param {Props} newProps - The new values of the Props.
|
||||
* @returns {void}
|
||||
*/
|
||||
_maybeToggleAnimation(oldProps, newProps) {
|
||||
const { status: oldStatus } = oldProps;
|
||||
const { status: newStatus } = newProps;
|
||||
const { pulseAnimation } = this.state;
|
||||
|
||||
if (newStatus === STATUS_IN_PROGRESS
|
||||
&& oldStatus !== STATUS_IN_PROGRESS) {
|
||||
// Animation must be started
|
||||
this.animationReference = Animated.loop(Animated.sequence([
|
||||
Animated.timing(pulseAnimation, {
|
||||
delay: 500,
|
||||
toValue: 1,
|
||||
useNativeDriver: true
|
||||
}),
|
||||
Animated.timing(pulseAnimation, {
|
||||
toValue: 0.3,
|
||||
useNativeDriver: true
|
||||
})
|
||||
]));
|
||||
|
||||
this.animationReference.start();
|
||||
} else if (this.animationReference
|
||||
&& newStatus !== STATUS_IN_PROGRESS
|
||||
&& oldStatus === STATUS_IN_PROGRESS) {
|
||||
// Animation must be stopped
|
||||
this.animationReference.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ type Props = AbstractProps & {
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class CircularLabel extends AbstractCircularLabel<Props> {
|
||||
export default class CircularLabel extends AbstractCircularLabel<Props, {}> {
|
||||
/**
|
||||
* Default values for {@code CircularLabel} component's properties.
|
||||
*
|
||||
|
||||
135
react/features/base/label/components/ExpandedLabel.native.js
Normal file
135
react/features/base/label/components/ExpandedLabel.native.js
Normal file
@@ -0,0 +1,135 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Animated, Text, View } from 'react-native';
|
||||
|
||||
import styles, { DEFAULT_COLOR, LABEL_MARGIN, LABEL_SIZE } from './styles';
|
||||
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* The position of the parent element (from right to left) to display the
|
||||
* arrow.
|
||||
*/
|
||||
parentPosition: number
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* The opacity animation Object.
|
||||
*/
|
||||
opacityAnimation: Object,
|
||||
|
||||
/**
|
||||
* A boolean to descide to show or not show the arrow. This is required as
|
||||
* we can't easily animate this transformed Component so we render it once
|
||||
* the animation is done.
|
||||
*/
|
||||
showArrow: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Offset to the arrow to be rendered in the right position.
|
||||
*/
|
||||
const ARROW_OFFSET = 0;
|
||||
|
||||
/**
|
||||
* A react {@code Component} that implements an expanded label as tooltip-like
|
||||
* component to explain the meaning of the {@code Label}.
|
||||
*/
|
||||
export default class ExpandedLabel<P: Props> extends Component<P, State> {
|
||||
/**
|
||||
* Instantiates a new {@code ExpandedLabel} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: P) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
opacityAnimation: new Animated.Value(0),
|
||||
showArrow: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React {@code Component}'s componentDidMount.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
Animated.decay(this.state.opacityAnimation, {
|
||||
toValue: 1,
|
||||
velocity: 1,
|
||||
useNativeDriver: true
|
||||
}).start(({ finished }) => {
|
||||
finished && this.setState({
|
||||
showArrow: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React {@code Component}'s render.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const arrowPosition
|
||||
= this.props.parentPosition - LABEL_MARGIN - (LABEL_SIZE / 2);
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style = { [
|
||||
styles.expandedLabelWrapper,
|
||||
{
|
||||
opacity: this.state.opacityAnimation
|
||||
}
|
||||
] } >
|
||||
<View
|
||||
style = { [
|
||||
styles.expandedLabelArrow,
|
||||
{
|
||||
backgroundColor: this._getColor() || DEFAULT_COLOR,
|
||||
marginRight: arrowPosition + ARROW_OFFSET
|
||||
}
|
||||
] } />
|
||||
<View
|
||||
style = { [
|
||||
styles.expandedLabelContainer,
|
||||
{
|
||||
backgroundColor: this._getColor() || DEFAULT_COLOR
|
||||
}
|
||||
] }>
|
||||
<Text style = { styles.expandedLabelText }>
|
||||
{ this._getLabel() }
|
||||
</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label that needs to be rendered in the box. To be implemented
|
||||
* by its overriding classes.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
_getLabel: () => string
|
||||
|
||||
_getColor: () => string
|
||||
|
||||
/**
|
||||
* Defines the color of the expanded label. This function returns a default
|
||||
* value if implementing classes don't override it, but the goal is to have
|
||||
* expanded labels matching to circular labels in color.
|
||||
* If implementing classes return a falsy value, it also uses the default
|
||||
* color.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
_getColor() {
|
||||
return DEFAULT_COLOR;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export { default as CircularLabel } from './CircularLabel';
|
||||
export { default as ExpandedLabel } from './ExpandedLabel';
|
||||
|
||||
@@ -1,30 +1,75 @@
|
||||
// @flow
|
||||
|
||||
import { ColorPalette, createStyleSheet } from '../../styles';
|
||||
import { BoxModel, ColorPalette, createStyleSheet } from '../../styles';
|
||||
|
||||
/**
|
||||
* The default color of the {@code Label} and {@code ExpandedLabel}.
|
||||
*/
|
||||
export const DEFAULT_COLOR = '#808080';
|
||||
|
||||
/**
|
||||
* Margin of the {@Label} - to be reused when rendering the
|
||||
* {@code ExpandedLabel}.
|
||||
*/
|
||||
export const LABEL_MARGIN = 5;
|
||||
|
||||
/**
|
||||
* Size of the {@Label} - to be reused when rendering the
|
||||
* {@code ExpandedLabel}.
|
||||
*/
|
||||
export const LABEL_SIZE = 36;
|
||||
|
||||
/**
|
||||
* The styles of the native base/label feature.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
|
||||
expandedLabelArrow: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
height: 15,
|
||||
transform: [ { rotate: '45deg' }, { translateX: 10 } ],
|
||||
width: 15
|
||||
},
|
||||
|
||||
expandedLabelContainer: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
borderColor: ColorPalette.blue,
|
||||
borderRadius: 6,
|
||||
marginHorizontal: BoxModel.margin,
|
||||
padding: BoxModel.padding
|
||||
},
|
||||
|
||||
expandedLabelText: {
|
||||
color: ColorPalette.white
|
||||
},
|
||||
|
||||
expandedLabelWrapper: {
|
||||
alignItems: 'flex-end',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
/**
|
||||
* The outermost view.
|
||||
*/
|
||||
indicatorContainer: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#808080',
|
||||
borderRadius: 18,
|
||||
backgroundColor: DEFAULT_COLOR,
|
||||
borderRadius: LABEL_SIZE / 2,
|
||||
borderWidth: 0,
|
||||
flex: 0,
|
||||
height: 36,
|
||||
height: LABEL_SIZE,
|
||||
justifyContent: 'center',
|
||||
margin: 5,
|
||||
margin: LABEL_MARGIN,
|
||||
opacity: 0.6,
|
||||
width: 36
|
||||
width: LABEL_SIZE
|
||||
},
|
||||
|
||||
indicatorText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 12
|
||||
},
|
||||
|
||||
labelOff: {
|
||||
opacity: 0.3
|
||||
}
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ const SOCK_STREAM = 1; /* stream socket */
|
||||
* The RTCPeerConnection provided by react-native-webrtc fires onaddstream
|
||||
* before it remembers remotedescription (and thus makes it available to API
|
||||
* clients). Because that appears to be a problem for lib-jitsi-meet which has
|
||||
* been successfully running on Chrome, Firefox, etc. for a very long
|
||||
* been successfully running on Chrome, Firefox and others for a very long
|
||||
* time, attempt to meet its expectations (by extending RTCPPeerConnection).
|
||||
*
|
||||
* @class
|
||||
|
||||
@@ -39,9 +39,10 @@ function _getCommonPrototype(a, b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an absolute minimum of the common logic of Document.querySelector
|
||||
* and Element.querySelector. Implements the most simple of selectors necessary
|
||||
* to satisfy the call sites at the time of this writing i.e. select by tagName.
|
||||
* Implements an absolute minimum of the common logic of
|
||||
* {@code Document.querySelector} and {@code Element.querySelector}. Implements
|
||||
* the most simple of selectors necessary to satisfy the call sites at the time
|
||||
* of this writing (i.e. Select by tagName).
|
||||
*
|
||||
* @param {Node} node - The Node which is the root of the tree to query.
|
||||
* @param {string} selectors - The group of CSS selectors to match on.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { trackVideoStarted } from '../../tracks';
|
||||
@@ -6,51 +7,71 @@ import { trackVideoStarted } from '../../tracks';
|
||||
import { shouldRenderVideoTrack } from '../functions';
|
||||
import { Video } from './_';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AbstractVideoTrack}.
|
||||
*/
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Dispatch<*>,
|
||||
|
||||
/**
|
||||
* Callback to invoke when the {@link Video} of {@code AbstractVideoTrack}
|
||||
* is clicked/pressed.
|
||||
*/
|
||||
onPress?: Function,
|
||||
|
||||
/**
|
||||
* The Redux representation of the participant's video track.
|
||||
*/
|
||||
videoTrack?: Object,
|
||||
|
||||
/**
|
||||
* Whether or not video should be rendered after knowing video playback has
|
||||
* started.
|
||||
*/
|
||||
waitForVideoStarted?: boolean,
|
||||
|
||||
/**
|
||||
* The z-order of the Video of AbstractVideoTrack in the stacking space of
|
||||
* all Videos. For more details, refer to the zOrder property of the Video
|
||||
* class for React Native.
|
||||
*/
|
||||
zOrder?: number,
|
||||
|
||||
/**
|
||||
* Indicates whether zooming (pinch to zoom and/or drag) is enabled.
|
||||
*/
|
||||
zoomEnabled?: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link AbstractVideoTrack}.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* The Redux representation of the participant's video track.
|
||||
*/
|
||||
videoTrack: Object | null
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} that renders video element for a
|
||||
* specific video track.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
export default class AbstractVideoTrack extends Component {
|
||||
/**
|
||||
* AbstractVideoTrack component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback to invoke when the {@link Video} of
|
||||
* {@code AbstractVideoTrack} is clicked/pressed.
|
||||
*/
|
||||
onPress: PropTypes.func,
|
||||
|
||||
videoTrack: PropTypes.object,
|
||||
|
||||
waitForVideoStarted: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The z-order of the Video of AbstractVideoTrack in the stacking space
|
||||
* of all Videos. For more details, refer to the zOrder property of the
|
||||
* Video class for React Native.
|
||||
*/
|
||||
zOrder: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Indicates whether zooming (pinch to zoom and/or drag) is enabled.
|
||||
*/
|
||||
zoomEnabled: PropTypes.bool
|
||||
};
|
||||
|
||||
export default class AbstractVideoTrack<P: Props> extends Component<P, State> {
|
||||
/**
|
||||
* Initializes a new AbstractVideoTrack instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: P) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -69,7 +90,7 @@ export default class AbstractVideoTrack extends Component {
|
||||
* receive.
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps: P) {
|
||||
const oldValue = this.state.videoTrack;
|
||||
const newValue = _falsy2null(nextProps.videoTrack);
|
||||
|
||||
@@ -88,7 +109,7 @@ export default class AbstractVideoTrack extends Component {
|
||||
const { videoTrack } = this.state;
|
||||
let render;
|
||||
|
||||
if (this.props.waitForVideoStarted) {
|
||||
if (this.props.waitForVideoStarted && videoTrack) {
|
||||
// That's the complex case: we have to wait for onPlaying before we
|
||||
// render videoTrack. The complexity comes from the fact that
|
||||
// onPlaying will come after we render videoTrack.
|
||||
@@ -110,14 +131,15 @@ export default class AbstractVideoTrack extends Component {
|
||||
render = shouldRenderVideoTrack(videoTrack, false);
|
||||
}
|
||||
|
||||
const stream
|
||||
= render ? videoTrack.jitsiTrack.getOriginalStream() : null;
|
||||
const stream = render && videoTrack
|
||||
? videoTrack.jitsiTrack.getOriginalStream() : null;
|
||||
|
||||
// Actual zoom is currently only enabled if the stream is a desktop
|
||||
// stream.
|
||||
const zoomEnabled
|
||||
= this.props.zoomEnabled
|
||||
&& stream
|
||||
&& videoTrack
|
||||
&& videoTrack.videoType === 'desktop';
|
||||
|
||||
return (
|
||||
@@ -131,6 +153,8 @@ export default class AbstractVideoTrack extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_onVideoPlaying: () => void;
|
||||
|
||||
/**
|
||||
* Handler for case when video starts to play.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { RTCView } from 'react-native-webrtc';
|
||||
|
||||
@@ -9,60 +8,58 @@ import { Pressable } from '../../../react';
|
||||
import styles from './styles';
|
||||
import VideoTransform from './VideoTransform';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Video}.
|
||||
*/
|
||||
type Props = {
|
||||
mirror: boolean,
|
||||
|
||||
onPlaying: Function,
|
||||
|
||||
/**
|
||||
* Callback to invoke when the {@code Video} is clicked/pressed.
|
||||
*/
|
||||
onPress: Function,
|
||||
|
||||
stream: Object,
|
||||
|
||||
/**
|
||||
* Similarly to the CSS property z-index, specifies the z-order of this
|
||||
* Video in the stacking space of all Videos. When Videos overlap,
|
||||
* zOrder determines which one covers the other. A Video with a larger
|
||||
* zOrder generally covers a Video with a lower one.
|
||||
*
|
||||
* Non-overlapping Videos may safely share a z-order (because one does
|
||||
* not have to cover the other).
|
||||
*
|
||||
* The support for zOrder is platform-dependent and/or
|
||||
* implementation-specific. Thus, specifying a value for zOrder is to be
|
||||
* thought of as giving a hint rather than as imposing a requirement.
|
||||
* For example, video renderers such as Video are commonly implemented
|
||||
* using OpenGL and OpenGL views may have different numbers of layers in
|
||||
* their stacking space. Android has three: a layer bellow the window
|
||||
* (aka default), a layer bellow the window again but above the previous
|
||||
* layer (aka media overlay), and above the window. Consequently, it is
|
||||
* advisable to limit the number of utilized layers in the stacking
|
||||
* space to the minimum sufficient for the desired display. For example,
|
||||
* a video call application usually needs a maximum of two zOrder
|
||||
* values: 0 for the remote video(s) which appear in the background, and
|
||||
* 1 for the local video(s) which appear above the remote video(s).
|
||||
*/
|
||||
zOrder: number,
|
||||
|
||||
/**
|
||||
* Indicates whether zooming (pinch to zoom and/or drag) is enabled.
|
||||
*/
|
||||
zoomEnabled: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* The React Native {@link Component} which is similar to Web's
|
||||
* {@code HTMLVideoElement} and wraps around react-native-webrtc's
|
||||
* {@link RTCView}.
|
||||
*/
|
||||
export default class Video extends Component<*> {
|
||||
/**
|
||||
* {@code Video} component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
mirror: PropTypes.bool,
|
||||
|
||||
onPlaying: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback to invoke when the {@code Video} is clicked/pressed.
|
||||
*/
|
||||
onPress: PropTypes.func,
|
||||
|
||||
stream: PropTypes.object,
|
||||
|
||||
/**
|
||||
* Similarly to the CSS property z-index, specifies the z-order of this
|
||||
* Video in the stacking space of all Videos. When Videos overlap,
|
||||
* zOrder determines which one covers the other. A Video with a larger
|
||||
* zOrder generally covers a Video with a lower one.
|
||||
*
|
||||
* Non-overlapping Videos may safely share a z-order (because one does
|
||||
* not have to cover the other).
|
||||
*
|
||||
* The support for zOrder is platform-dependent and/or
|
||||
* implementation-specific. Thus, specifying a value for zOrder is to be
|
||||
* thought of as giving a hint rather than as imposing a requirement.
|
||||
* For example, video renderers such as Video are commonly implemented
|
||||
* using OpenGL and OpenGL views may have different numbers of layers in
|
||||
* their stacking space. Android has three: a layer bellow the window
|
||||
* (aka default), a layer bellow the window again but above the previous
|
||||
* layer (aka media overlay), and above the window. Consequently, it is
|
||||
* advisable to limit the number of utilized layers in the stacking
|
||||
* space to the minimum sufficient for the desired display. For example,
|
||||
* a video call application usually needs a maximum of two zOrder
|
||||
* values: 0 for the remote video(s) which appear in the background, and
|
||||
* 1 for the local video(s) which appear above the remote video(s).
|
||||
*/
|
||||
zOrder: PropTypes.number,
|
||||
|
||||
/**
|
||||
* Indicates whether zooming (pinch to zoom and/or drag) is enabled.
|
||||
*/
|
||||
zoomEnabled: PropTypes.bool
|
||||
};
|
||||
|
||||
export default class Video extends Component<Props> {
|
||||
/**
|
||||
* React Component method that executes once component is mounted.
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user