Compare commits

..

15 Commits

Author SHA1 Message Date
George Politis
34280b9733 Merge branch 'master' into feat/more-debug-information 2020-10-02 14:24:54 +03:00
George Politis
e082f2a7d6 chore: Updates lib-jitsi-meet@679497938fd06de719f104a4640fd4139b8f5e01 2020-09-30 13:34:44 +02:00
George Politis
f53ea35790 wip: Removes bogus comment. 2020-09-30 13:31:07 +02:00
George Politis
cf6073f721 wip: Addresses @hristoterezov's comments. 2020-09-30 13:31:07 +02:00
George Politis
871504e70e wip: Removes obsolete FIXME 2020-09-30 13:31:07 +02:00
George Politis
9ecfe1add4 wip: Deduplicates code 2020-09-30 13:31:07 +02:00
George Politis
3b346e94c7 wip: Fixes linting errors 2020-09-30 13:31:07 +02:00
George Politis
54a370685d wip: More reactification (save logs action dispatch, remove global APP object usage). 2020-09-30 13:31:07 +02:00
George Politis
49046fead9 wip: Fixes lint errors. 2020-09-30 13:31:07 +02:00
George Politis
03d4f824e7 wip: Fixes lint errors. 2020-09-30 13:31:07 +02:00
George Politis
a950b308ad wip: Fixes lint errors. 2020-09-30 13:31:07 +02:00
George Politis
7ea64e4dc6 wip: UI redesign. 2020-09-30 13:31:07 +02:00
George Politis
c0b3e5cb36 wip: Cleans up code. 2020-09-30 13:31:07 +02:00
George Politis
b88dd4b7de wip: Adds debug information and a save logs link in the GSM popover. 2020-09-30 13:31:07 +02:00
George Politis
595e0d475f wip: Removes duplicate declaration. 2020-09-30 13:31:07 +02:00
307 changed files with 6245 additions and 7527 deletions

View File

@@ -1,9 +1,7 @@
---
name: "Feature request"
about: Suggest an idea for this project
title: ''
labels: 'feature-request'
assignees: ''
---
<!--

6
.gitignore vendored
View File

@@ -85,9 +85,3 @@ ios/app/dropbox.key
ios/app/GoogleService-Info.plist
.vscode
# TWA
twa/*.apk
twa/*.aab
twa/assetlinks.json

11
ConferenceEvents.js Normal file
View File

@@ -0,0 +1,11 @@
/**
* Notifies interested parties that hangup procedure will start.
*/
export const BEFORE_HANGUP = 'conference.before_hangup';
/**
* Notifies interested parties that desktop sharing enable/disable state is
* changed.
*/
export const DESKTOP_SHARING_ENABLED_CHANGED
= 'conference.desktop_sharing_enabled_changed';

View File

@@ -27,7 +27,6 @@ import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
import androidx.annotation.Nullable;
import org.jitsi.meet.sdk.JitsiMeet;
@@ -79,12 +78,6 @@ public class MainActivity extends JitsiMeetActivity {
// JitsiMeetActivity overrides
//
@Override
protected void onCreate(Bundle savedInstanceState) {
JitsiMeet.showSplashScreen(this);
super.onCreate(savedInstanceState);
}
@Override
protected boolean extraInitialize() {
Log.d(this.getClass().getSimpleName(), "LIBRE_BUILD="+BuildConfig.LIBRE_BUILD);

View File

@@ -1,70 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="262.91376dp"
android:height="262.91376dp"
android:viewportWidth="262.91376"
android:viewportHeight="262.91376">
<group>
<clip-path
android:pathData="m0,0 l262.914,-0L262.914,262.914 0,262.914 0,0Z"/>
<path
android:pathData="m142.646,105.099c0.117,0.026 0.255,0.036 0.406,0.036 3.186,-0 10.297,-4.615 11.617,-6.721l0.1,-0.17 0.153,-0.135c0.451,-0.441 1.746,-2.773 2.374,-4.17 -6.751,-2.023 -7.49,-5.677 -8.153,-8.919 -0.069,-0.376 -0.138,-0.717 -0.204,-1.019 -0.074,-0.397 -0.153,-0.8 -0.226,-1.112C138.668,86.221 135.593,88.094 133.921,89.483 133.056,90.201 132.542,92.251 135.042,97.926 136.323,100.816 140.727,104.733 142.646,105.099"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m115.413,146.042c5.934,-0 18.464,-3.543 26.748,-5.887 1.21,-0.336 2.33,-0.66 3.351,-0.944 0.166,-0.046 0.321,-0.091 0.472,-0.124 -0.463,-0.461 -1.239,-1.159 -2.497,-2.216 -5.521,-3.741 -10.736,-5.484 -16.403,-5.484 -1.237,-0 -2.522,0.071 -3.923,0.231 -4.801,0.55 -8.8,1.69 -10.722,2.237 -0.967,0.284 -1.263,0.366 -1.567,0.366 -0.58,-0 -1.079,-0.341 -1.273,-0.878 -0.194,-0.534 -0.027,-1.121 0.425,-1.507l0.024,-0.011c3.316,-2.784 9.489,-7.951 21.198,-10.256 2.027,-0.401 4.202,-0.605 6.454,-0.605 5.242,-0 10.67,1.086 16.125,3.219 7.436,2.899 12.521,6.625 16.602,9.62 2.199,1.609 4.105,3.007 5.755,3.771 0.421,0.2 0.637,0.255 0.746,0.265 0.074,-0.095 0.23,-0.365 0.474,-1.069 0.066,-0.185 0.529,-2.161 -2.806,-13.374 -1.931,-6.51 -4.264,-13.156 -5.479,-16.104 -2.356,-5.711 -1.778,-9.76 -1.051,-12.125 -1.999,0.735 -4.033,1.87 -6.174,3.446L161.758,98.711C160.694,99.506 159.599,100.404 158.426,101.454 151.517,107.64 146.344,110.864 143.035,111.04l-0.093,0.004 -0.093,-0.009c-2.912,-0.245 -7.324,-4.489 -9.133,-6.634 -0.373,-0.251 -0.8,-0.366 -1.366,-0.366 -0.564,-0 -1.202,0.116 -1.82,0.235C130.086,104.354 129.623,104.441 129.167,104.489 127.708,104.632 125.668,105.106 123.694,105.561 122.746,105.777 121.762,106.005 120.864,106.189 120.851,106.19 120.463,106.272 119.774,106.454 114.903,107.891 111.228,109.55 109.432,111.111 109.414,111.127 109.352,111.174 109.266,111.242 108.048,112.105 105.124,114.567 104.248,118.762L104.237,118.795C102.398,126.516 105.187,136.087 108.892,141.554 110.636,144.125 112.513,145.727 114.048,145.959 114.437,146.015 114.891,146.042 115.413,146.042"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m90.093,173.175c-1.252,-1.472 -1.783,-3.324 -1.574,-5.521 0.884,-10.642 -0.329,-13.215 -0.891,-13.829 -0.131,-0.144 -0.207,-0.144 -0.265,-0.144 -0.022,-0 -0.041,0.003 -0.064,0.003 -1.044,0.248 -8.066,5.002 -9.615,19.171 -0.749,6.845 0.561,15.63 1.679,20.974 0.897,-3.155 2.314,-6.624 5.057,-10.204 2.556,-3.326 5.345,-5.955 8.801,-8.253C92.143,174.93 90.991,174.235 90.093,173.175"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m94.906,156.389c-0.03,2.229 -0.326,4.36 -0.61,6.445 -0.151,1.119 -0.314,2.286 -0.434,3.46 -0.161,2.341 0.346,3.166 0.571,3.406 0.127,0.136 0.326,0.287 0.76,0.287 0.339,-0 0.741,-0.091 1.161,-0.268 4.202,-1.756 8.195,-4.815 10.115,-6.515C103.522,161.892 98.995,159.058 94.906,156.389"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m154.002,81.595c-0.031,0.074 -0.065,0.148 -0.101,0.216 -0.821,2.403 0.306,5.664 2.419,6.898 0.561,0.327 1.106,0.526 1.624,0.596 0.072,0.006 0.148,0.009 0.219,0.009 1.645,-0 2.971,-1.199 3.961,-3.561C162.752,83.959 162.836,81.827 162.37,79.904 162.003,78.409 161.057,76.627 160.453,75.738 159.332,76.509 157.111,78.207 155.585,79.553 154.518,80.582 154.136,81.229 154.002,81.595"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="M148.97,77.699C153.957,73.194 156.988,65.754 158.253,61.334 153.915,65.513 148.633,67.758 145.25,69.198 144.084,69.695 143.08,70.124 142.477,70.476 142.224,70.623 141.965,70.77 141.708,70.919 139.654,72.109 136.55,73.905 136.1,75.011l-0.012,0.036 -0.012,0.034c-1.406,2.956 -2.199,7.401 -2.457,9.95 3.266,-1.99 6.625,-3.322 9.416,-4.42C145.628,79.585 147.863,78.703 148.97,77.699"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m164.464,51.921c-0.84,5.539 -2.205,10.799 -4.751,16.347 2.781,-3.144 4.396,-6.568 4.941,-10.401C164.886,56.275 165.097,54.756 164.464,51.921"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="M148.749,142.639C148.718,142.598 148.684,142.56 148.658,142.519 148.523,142.539 148.307,142.584 147.972,142.683l-0.14,0.04c-1.726,0.644 -4.899,1.708 -8.556,2.946 -4.396,1.479 -9.365,3.154 -13.526,4.649 -5.297,1.975 -7.021,2.755 -7.557,3.024 -0.098,0.266 -0.203,0.599 -0.327,0.965 -1.254,3.816 -4.125,12.541 -18.276,18.653 2.928,2.956 9.289,8.27 21.809,8.27 1.082,-0 2.21,-0.036 3.341,-0.12 9.451,-0.666 18.342,-4.855 25.026,-11.78 6.087,-6.291 9.538,-14.136 9.585,-21.7C157.876,147.509 155.367,147.135 153.043,146.033 153.014,146.02 150.361,144.745 148.749,142.639"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m189.478,117.853c-0.523,9.749 -2.122,18.424 -4.744,25.8 -2.128,5.988 -4.94,11.134 -8.356,15.316 -5.676,6.931 -11.555,9.256 -12.804,9.304 -0.866,-0 -1.313,-0.309 -3.046,-1.528 -0.17,-0.114 -0.37,-0.252 -0.581,-0.4 -3.313,5.953 -8.505,11.097 -15.065,14.959 -7.079,4.144 -15.297,6.423 -23.157,6.423 -9.078,-0 -17.13,-2.924 -23.341,-8.456 -7.467,4.799 -12.31,9.074 -16.267,27.005l-1.363,6.17 -2.971,-5.564c-0.424,-0.786 -1.929,-3.731 -3.332,-8.887 -1.934,-7.104 -2.86,-15.181 -2.758,-24.01 0.117,-10.049 3.154,-16.526 5.68,-20.186 2.98,-4.314 6.837,-6.994 10.076,-6.994 0.216,-0 0.428,0.006 0.616,0.035 5.159,0.575 8.435,2.75 14.396,6.686l1.899,1.252c2.059,1.344 4.481,2.7 5.259,2.989 0.54,-0.284 1.749,-2.3 2.155,-5.271l0.069,-0.451c0.005,-0.045 0.009,-0.091 0.014,-0.131 -0.036,-0.02 -0.065,-0.029 -0.094,-0.041 -4.008,-1.375 -9.539,-7.7 -12.364,-17.134 -2.684,-9.382 -2.129,-17.185 1.644,-23.193 6.12,-9.736 19.198,-11.974 23.466,-12.702 1.331,-0.266 2.716,-0.511 4.041,-0.717 0.255,-0.061 0.469,-0.121 0.642,-0.168 -0.031,-0.126 -0.071,-0.265 -0.114,-0.43 -0.108,-0.417 -0.23,-0.891 -0.354,-1.447 -1.345,-6.035 -0.664,-11.069 0.181,-15.193 0.928,-4.546 1.489,-7.287 3.747,-9.936 3.029,-4.165 8.319,-5.936 11.479,-6.991 0.746,-0.249 1.511,-0.509 1.894,-0.689 8.988,-4.31 11.82,-8.739 12.615,-11.694 0.656,-2.451 1.699,-8.884 1.251,-13.335 -0.085,-0.805 0.129,-1.521 0.621,-2.065 0.45,-0.505 1.101,-0.794 1.778,-0.794 1.515,-0 2.82,-0 7.511,14.598 2.481,7.698 0.645,14.903 -5.45,21.424l-0.226,0.231c0.024,0.044 0.049,0.09 0.08,0.144 2.57,4.236 3.963,9.54 3.553,13.51 -0.099,0.906 -0.265,1.775 -0.419,2.549 -0.003,0.01 -0.003,0.016 -0.004,0.029 0.516,-0.032 1.119,-0.055 1.775,-0.055 3.052,-0 7.435,0.474 10.989,2.735 2.135,1.352 4.845,3.439 6.835,7.615C189.223,102.942 190.076,109.575 189.478,117.853m4.77,-23.191c-2.916,-6.1 -6.989,-9.177 -9.793,-10.96 -2.355,-1.494 -5.064,-2.584 -8.077,-3.24l-0.676,-0.146 -0.111,-0.689c-0.339,-2.119 -0.918,-4.275 -1.715,-6.406l-0.185,-0.49 0.292,-0.434c5.095,-7.594 6.323,-16.17 3.54,-24.802 -2.191,-6.824 -3.895,-11.211 -5.341,-13.799 -2.954,-5.305 -7.006,-6.417 -9.891,-6.417 -2.964,-0 -5.8,1.261 -7.789,3.457 -2.043,2.254 -2.993,5.207 -2.678,8.31 0.316,3.134 -0.494,8.516 -1.014,10.439 -0.04,0.117 -0.975,2.929 -8.201,6.428 -0.162,0.056 -0.512,0.179 -1.053,0.359 -3.729,1.246 -10.666,3.571 -15.258,9.64 -3.465,4.205 -4.332,8.441 -5.338,13.346 -0.586,2.865 -1.236,6.744 -1.079,11.344l0.026,0.841 -0.824,0.188c-11.646,2.585 -20.025,7.835 -24.909,15.605 -5.054,8.04 -5.919,18.055 -2.543,29.853 0.063,0.204 0.126,0.407 0.189,0.615l0.527,1.608 -1.665,-0.286c-0.561,-0.101 -1.135,-0.18 -1.729,-0.241 -0.493,-0.06 -1.001,-0.082 -1.509,-0.082 -5.633,-0 -11.663,3.585 -16.128,9.592 -3.451,4.641 -7.588,12.849 -7.735,25.601 -0.114,9.573 0.906,18.401 3.038,26.228 1.581,5.795 3.326,9.329 4.004,10.577l13.306,24.94 6.096,-27.619c2.454,-11.09 4.864,-15.262 7.725,-18.111l0.561,-0.563 0.679,0.411c6.605,3.977 14.466,6.084 22.73,6.084 9.286,-0 18.965,-2.682 27.259,-7.551 5.38,-3.16 9.974,-7.036 13.649,-11.531l0.45,-0.369 0.85,-0.02c2.156,-0.068 5.16,-1.164 8.222,-3.004 2.6,-1.555 6.543,-4.428 10.501,-9.262 3.997,-4.884 7.274,-10.854 9.716,-17.734 2.876,-8.073 4.625,-17.489 5.204,-28.004 0.689,-9.668 -0.434,-17.641 -3.327,-23.704"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m180.026,98.414c-1.67,-2.596 -3.771,-4.206 -5.475,-4.206 -0.313,-0 -0.613,0.051 -0.895,0.161 -0.911,0.361 -2.356,4.532 -1.714,7.566 0.434,2.066 2.938,9.04 4.151,12.394 0.456,1.281 0.68,1.91 0.754,2.142 0.064,0.183 0.145,0.448 0.256,0.774 0.97,2.971 3.467,10.586 4.206,16.761 1.549,-6.579 2.424,-14.512 2.085,-23.997C183.235,105.662 182.04,101.538 180.026,98.414"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="M168.088,142.604C169.896,142.111 171.33,141.705 172.398,141.395 170.213,139.874 167.689,137.979 164.247,135.304c-8.418,-6.546 -17.449,-9.87 -26.839,-9.87 -5.135,-0 -9.611,0.991 -13.156,2.186 0.882,-0.05 1.779,-0.079 2.7,-0.079 1.1,-0 2.247,0.04 3.411,0.119 3.652,0.246 13.061,1.901 21.565,12.047 1.714,2.039 3.559,3.73 8.794,3.73 1.873,-0 4.051,-0.207 6.662,-0.645C167.544,142.751 167.793,142.678 168.088,142.604"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m164.3,147.583c-0.122,1.563 -0.376,4.509 -0.782,6.76 -0.495,2.719 -1.31,5.02 -1.791,6.226 0.85,0.786 1.694,1.553 2.247,2.043 2.214,-1.447 9.47,-6.96 14.483,-19.474C176.847,144.229 174.59,145.178 171.671,146.018 168.701,146.861 165.82,147.357 164.3,147.583"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
</group>
</vector>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/ic_jitsi_logosvg"/>
</RelativeLayout>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#17A0DB</color>
<color name="colorPrimaryDark">#1081B2</color>
</resources>

View File

@@ -2,6 +2,6 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
<item name="android:navigationBarColor">#1081B2</item>
</style>
</resources>

View File

@@ -1,5 +1,4 @@
import groovy.json.JsonSlurper
import org.gradle.util.VersionNumber
// Top-level build file where you can add configuration options common to all
// sub-projects/modules.
@@ -10,7 +9,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.2'
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
}

View File

@@ -18,10 +18,6 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# This one fixes a weird WebRTC runtime problem on some devices.
# https://github.com/jitsi/jitsi-meet/issues/7911#issuecomment-714323255
android.enableDexingArtifactTransform.desugaring=false
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -89,9 +89,7 @@ fi
# Now build and publish the Jitsi Meet SDK and its dependencies
echo "Building and publishing the Jitsi Meet SDK"
pushd ${THIS_DIR}/../
./gradlew clean
./gradlew assembleRelease
./gradlew publish
./gradlew clean assembleRelease publish
popd
if [[ $DO_GIT_TAG == 1 ]]; then

View File

@@ -71,7 +71,6 @@ dependencies {
implementation project(':react-native-svg')
implementation project(':react-native-webrtc')
implementation project(':react-native-webview')
implementation project(':react-native-splash-screen')
testImplementation 'junit:junit:4.12'
}

View File

@@ -12,7 +12,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-feature
android:glEsVersion="0x00020000"
@@ -34,7 +34,8 @@
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize"></activity>
android:windowSoftInputMode="adjustResize">
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service
@@ -45,9 +46,7 @@
</intent-filter>
</service>
<service
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
android:foregroundServiceType="mediaProjection" />
<service android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService" />
</application>
</manifest>

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2017-present 8x8, Inc.
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 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.
@@ -15,16 +16,12 @@
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import com.facebook.react.ReactInstanceManager;
import org.devio.rn.splashscreen.SplashScreen;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
public class JitsiMeet {
/**
@@ -84,17 +81,4 @@ public class JitsiMeet {
String value = preferences.getString("isCrashReportingDisabled", "");
return Boolean.parseBoolean(value);
}
/**
* Helper method to show the SplashScreen.
*
* @param activity - The activity on which to show the SplashScreen {@link Activity}.
*/
public static void showSplashScreen(Activity activity) {
try {
SplashScreen.show(activity);
} catch (Exception e) {
JitsiMeetLogger.e(e, "Failed to show splash screen");
}
}
}

View File

@@ -43,7 +43,6 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
private static final String TAG = NAME;
private static boolean isSupported;
private boolean isDisabled;
public PictureInPictureModule(ReactApplicationContext reactContext) {
super(reactContext);
@@ -84,10 +83,6 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
*/
@TargetApi(Build.VERSION_CODES.O)
public void enterPictureInPicture() {
if (isDisabled) {
return;
}
if (!isSupported) {
throw new IllegalStateException("Picture-in-Picture not supported");
}
@@ -131,11 +126,6 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
}
}
@ReactMethod
public void setPictureInPictureDisabled(Boolean disabled) {
this.isDisabled = disabled;
}
public boolean isPictureInPictureSupported() {
return isSupported;
}

View File

@@ -23,18 +23,19 @@ import androidx.annotation.Nullable;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.DevInternalSettings;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.soloader.SoLoader;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import org.devio.rn.splashscreen.SplashScreenModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.audio.AudioDeviceModule;
@@ -68,7 +69,6 @@ class ReactInstanceManagerHolder {
new JavaScriptSandboxModule(reactContext),
new LocaleDetector(reactContext),
new LogBridgeModule(reactContext),
new SplashScreenModule(reactContext),
new PictureInPictureModule(reactContext),
new ProximityModule(reactContext),
new WiFiStatsModule(reactContext),

View File

@@ -21,8 +21,6 @@ include ':react-native-linear-gradient'
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
include ':react-native-sound'
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
include ':react-native-splash-screen'
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-webrtc'

4
app.js
View File

@@ -6,8 +6,6 @@ import 'jQuery-Impromptu';
import 'olm';
import 'focus-visible';
// We need to setup the jitsi-local-storage as early as possible so that we can start using it.
// NOTE: If jitsi-local-storage is used before the initial setup is performed this will break the use case when we use
// the local storage from the parent page when the localStorage is disabled. Also the setup is relying that
@@ -17,6 +15,7 @@ import conference from './conference';
import API from './modules/API';
import UI from './modules/UI/UI';
import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
import remoteControl from './modules/remotecontrol/RemoteControl';
import translation from './modules/translation/translation';
// Initialize Olm as early as possible.
@@ -48,6 +47,7 @@ window.APP = {
},
keyboardshortcut,
remoteControl,
translation,
UI
};

View File

@@ -3,6 +3,7 @@
import EventEmitter from 'events';
import Logger from 'jitsi-meet-logger';
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
import { openConnection } from './connection';
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
import AuthHandler from './modules/UI/authentication/AuthHandler';
@@ -23,6 +24,7 @@ import {
reloadWithStoredParams
} from './react/features/app/actions';
import {
AVATAR_ID_COMMAND,
AVATAR_URL_COMMAND,
EMAIL_COMMAND,
authStatusChanged,
@@ -40,7 +42,8 @@ import {
lockStateChanged,
onStartMutedPolicyChanged,
p2pStatusChanged,
sendLocalParticipant
sendLocalParticipant,
setDesktopSharingEnabled
} from './react/features/base/conference';
import {
checkAndNotifyForNewDevice,
@@ -52,7 +55,6 @@ import {
updateDeviceList
} from './react/features/base/devices';
import {
browser,
isFatalJitsiConnectionError,
JitsiConferenceErrors,
JitsiConferenceEvents,
@@ -64,8 +66,6 @@ import {
JitsiTrackEvents
} from './react/features/base/lib-jitsi-meet';
import {
getStartWithAudioMuted,
getStartWithVideoMuted,
isVideoMutedByUser,
MEDIA_TYPE,
setAudioAvailable,
@@ -85,8 +85,7 @@ import {
participantMutedUs,
participantPresenceChanged,
participantRoleChanged,
participantUpdated,
updateRemoteParticipantFeatures
participantUpdated
} from './react/features/base/participants';
import {
getUserSelectedCameraDeviceId,
@@ -98,15 +97,18 @@ import {
destroyLocalTracks,
getLocalJitsiAudioTrack,
getLocalJitsiVideoTrack,
isLocalCameraTrackMuted,
isLocalVideoTrackMuted,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
trackAdded,
trackRemoved
} from './react/features/base/tracks';
import {
getBackendSafePath,
getJitsiMeetGlobalNS
} from './react/features/base/util';
import { downloadJSON } from './react/features/base/util/downloadJSON';
import { getConferenceOptions } from './react/features/conference/functions';
import { showDesktopPicker } from './react/features/desktop-picker';
import { appendSuffix } from './react/features/display-name';
import {
@@ -122,13 +124,15 @@ import {
isPrejoinPageVisible,
makePrecallTest
} from './react/features/prejoin';
import { disableReceiver, stopReceiver } from './react/features/remote-control';
import { createRnnoiseProcessorPromise } from './react/features/rnnoise';
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
import { setSharedVideoStatus } from './react/features/shared-video';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createPresenterEffect } from './react/features/stream-effects/presenter';
import { endpointMessageReceived } from './react/features/subtitles';
import UIEvents from './service/UI/UIEvents';
import * as RemoteControlEvents
from './service/remotecontrol/RemoteControlEvents';
const logger = Logger.getLogger(__filename);
@@ -169,6 +173,7 @@ window.JitsiMeetScreenObtainer = {
* Known custom conference commands.
*/
const commands = {
AVATAR_ID: AVATAR_ID_COMMAND,
AVATAR_URL: AVATAR_URL_COMMAND,
CUSTOM_ROLE: 'custom-role',
EMAIL: EMAIL_COMMAND,
@@ -440,9 +445,18 @@ export default {
* the tracks won't exist).
*/
_localTracksInitialized: false,
isSharingScreen: false,
/**
* Indicates if the desktop sharing functionality has been enabled.
* It takes into consideration the status returned by
* {@link JitsiMeetJS.isDesktopSharingEnabled()}. The latter can be false
* either if the desktop sharing is not supported by the current browser
* or if it was disabled through lib-jitsi-meet specific options (check
* config.js for listed options).
*/
isDesktopSharingEnabled: false,
/**
* The local audio track (if any).
* FIXME tracks from redux store should be the single source of truth
@@ -493,9 +507,9 @@ export default {
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
browserName =>
browser =>
APP.store.dispatch(
mediaPermissionPromptVisibilityChanged(true, browserName))
mediaPermissionPromptVisibilityChanged(true, browser))
);
let tryCreateLocalTracks;
@@ -669,7 +683,16 @@ export default {
con.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED, _connectionFailedHandler);
APP.connection = connection = con;
// Desktop sharing related stuff:
this.isDesktopSharingEnabled
= JitsiMeetJS.isDesktopSharingEnabled();
eventEmitter.emit(JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED, this.isDesktopSharingEnabled);
APP.store.dispatch(
setDesktopSharingEnabled(this.isDesktopSharingEnabled));
this._createRoom(tracks);
APP.remoteControl.init();
// if user didn't give access to mic or camera or doesn't have
// them at all, we mark corresponding toolbar buttons as muted,
@@ -714,10 +737,10 @@ export default {
const initialOptions = {
startAudioOnly: config.startAudioOnly,
startScreenSharing: config.startScreenSharing,
startWithAudioMuted: getStartWithAudioMuted(APP.store.getState())
startWithAudioMuted: config.startWithAudioMuted
|| config.startSilent
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
startWithVideoMuted: getStartWithVideoMuted(APP.store.getState())
startWithVideoMuted: config.startWithVideoMuted
|| isUserInteractionRequiredForUnmute(APP.store.getState())
};
@@ -792,7 +815,7 @@ export default {
isLocalVideoMuted() {
// If the tracks are not ready, read from base/media state
return this._localTracksInitialized
? isLocalCameraTrackMuted(
? isLocalVideoTrackMuted(
APP.store.getState()['features/base/tracks'])
: isVideoMutedByUser(APP.store);
},
@@ -1115,6 +1138,20 @@ export default {
return room ? room.getParticipantById(id) : null;
},
/**
* Get participant connection status for the participant.
*
* @param {string} id participant's identifier(MUC nickname)
*
* @returns {ParticipantConnectionStatus|null} the status of the participant
* or null if no such participant is found or participant is the local user.
*/
getParticipantConnectionStatus(id) {
const participant = this.getParticipantById(id);
return participant ? participant.getConnectionStatus() : null;
},
/**
* Gets the display name foe the <tt>JitsiParticipant</tt> identified by
* the given <tt>id</tt>.
@@ -1282,7 +1319,53 @@ export default {
},
_getConferenceOptions() {
return getConferenceOptions(APP.store.getState());
const options = config;
const { email, name: nick } = getLocalParticipant(APP.store.getState());
const state = APP.store.getState();
const { locationURL } = state['features/base/connection'];
const { tenant } = state['features/base/jwt'];
if (tenant) {
options.siteID = tenant;
}
if (options.enableDisplayNameInStats && nick) {
options.statisticsDisplayName = nick;
}
if (options.enableEmailInStats && email) {
options.statisticsId = email;
}
options.applicationName = interfaceConfig.APP_NAME;
options.getWiFiStatsMethod = this._getWiFiStatsMethod;
options.confID = `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`;
options.createVADProcessor = createRnnoiseProcessorPromise;
// Disable CallStats, if requessted.
if (options.disableThirdPartyRequests) {
delete options.callStatsID;
delete options.callStatsSecret;
delete options.getWiFiStatsMethod;
}
return options;
},
/**
* Returns the result of getWiFiStats from the global NS or does nothing
* (returns empty result).
* Fixes a concurrency problem where we need to pass a function when creating
* JitsiConference, but that method is added to the context later.
*
* @returns {Promise}
* @private
*/
_getWiFiStatsMethod() {
const gloabalNS = getJitsiMeetGlobalNS();
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
},
/**
@@ -1426,8 +1509,11 @@ export default {
async _turnScreenSharingOff(didHaveVideo) {
this._untoggleScreenSharing = null;
this.videoSwitchInProgress = true;
const { receiver } = APP.remoteControl;
APP.store.dispatch(stopReceiver());
if (receiver) {
receiver.stop();
}
this._stopProxyConnection();
if (config.enableScreenshotCapture) {
@@ -1510,8 +1596,9 @@ export default {
if (this.videoSwitchInProgress) {
return Promise.reject('Switch in progress.');
}
if (!JitsiMeetJS.isDesktopSharingEnabled()) {
return Promise.reject('Cannot toggle screen sharing: not supported.');
if (!this.isDesktopSharingEnabled) {
return Promise.reject(
'Cannot toggle screen sharing: not supported.');
}
if (this.isAudioOnly()) {
@@ -1601,10 +1688,8 @@ export default {
*/
async _createPresenterStreamEffect(height = null, cameraDeviceId = null) {
if (!this.localPresenterVideo) {
const camera = cameraDeviceId ?? getUserSelectedCameraDeviceId(APP.store.getState());
try {
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId: camera }, height);
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId }, height);
} catch (err) {
logger.error('Failed to create a camera track for presenter', err);
@@ -1645,38 +1730,38 @@ export default {
// Create a new presenter track and apply the presenter effect.
if (!this.localPresenterVideo && !mute) {
const { height, width } = this.localVideo.track.getSettings() ?? this.localVideo.track.getConstraints();
const isPortrait = height >= width;
let { aspectRatio, height } = this.localVideo.track.getSettings();
const { width } = this.localVideo.track.getSettings();
let desktopResizeConstraints = {};
let resizeDesktopStream = false;
const DESKTOP_STREAM_CAP = 720;
// Config.js setting for resizing high resolution desktop tracks to 720p when presenter is turned on.
const resizeEnabled = config.videoQuality && config.videoQuality.resizeDesktopForPresenter;
const highResolutionTrack
= (isPortrait && width > DESKTOP_STREAM_CAP) || (!isPortrait && height > DESKTOP_STREAM_CAP);
// Determine the constraints if the desktop track needs to be resized.
// Resizing is needed when the resolution cannot be determined or when
// the window is bigger than 720p.
if (height && width) {
aspectRatio = aspectRatio ?? (width / height).toPrecision(4);
const advancedConstraints = [ { aspectRatio } ];
const isPortrait = height >= width;
// Resizing the desktop track for presenter is causing blurriness of the desktop share on chrome.
// Disable resizing by default, enable it only when config.js setting is enabled.
// Firefox doesn't return width and height for desktop tracks. Therefore, track needs to be resized
// for creating the canvas for presenter.
const resizeDesktopStream = browser.isFirefox() || (highResolutionTrack && resizeEnabled);
if (resizeDesktopStream) {
let desktopResizeConstraints = {};
if (height && width) {
const advancedConstraints = [ { aspectRatio: (width / height).toPrecision(4) } ];
const constraint = isPortrait ? { width: DESKTOP_STREAM_CAP } : { height: DESKTOP_STREAM_CAP };
advancedConstraints.push(constraint);
desktopResizeConstraints.advanced = advancedConstraints;
} else {
desktopResizeConstraints = {
width: 1280,
height: 720
};
// Determine which dimension needs resizing and resize only that side
// keeping the aspect ratio same as before.
if (isPortrait && width > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ width: DESKTOP_STREAM_CAP });
} else if (!isPortrait && height > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ height: DESKTOP_STREAM_CAP });
}
// Apply the contraints on the desktop track.
desktopResizeConstraints.advanced = advancedConstraints;
} else {
resizeDesktopStream = true;
desktopResizeConstraints = {
width: 1280,
height: 720
};
}
if (resizeDesktopStream) {
try {
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
} catch (err) {
@@ -1684,22 +1769,20 @@ export default {
return;
}
height = this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP;
}
const trackHeight = resizeDesktopStream
? this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP
: height;
const defaultCamera = getUserSelectedCameraDeviceId(APP.store.getState());
let effect;
try {
effect = await this._createPresenterStreamEffect(trackHeight);
effect = await this._createPresenterStreamEffect(height,
defaultCamera);
} catch (err) {
logger.error('Failed to unmute Presenter Video', err);
logger.error('Failed to unmute Presenter Video');
maybeShowErrorDialog(err);
return;
}
// Replace the desktop track on the peerconnection.
try {
await this.localVideo.setEffect(effect);
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
@@ -1850,9 +1933,8 @@ export default {
(authEnabled, authLogin) =>
APP.store.dispatch(authStatusChanged(authEnabled, authLogin)));
room.on(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED, user => {
APP.store.dispatch(updateRemoteParticipantFeatures(user));
});
room.on(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED,
user => APP.UI.onUserFeaturesChanged(user));
room.on(JitsiConferenceEvents.USER_JOINED, (id, user) => {
// The logic shared between RN and web.
commonUserJoinedHandling(APP.store, room, user);
@@ -1861,7 +1943,6 @@ export default {
return;
}
APP.store.dispatch(updateRemoteParticipantFeatures(user));
logger.log(`USER ${id} connnected:`, user);
APP.UI.addUser(user);
});
@@ -2032,6 +2113,30 @@ export default {
JitsiConferenceEvents.LOCK_STATE_CHANGED,
(...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
APP.remoteControl.on(RemoteControlEvents.ACTIVE_CHANGED, isActive => {
room.setLocalParticipantProperty(
'remoteControlSessionStatus',
isActive
);
APP.UI.setLocalRemoteControlActiveChanged();
});
/* eslint-disable max-params */
room.on(
JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
(participant, name, oldValue, newValue) => {
switch (name) {
case 'remoteControlSessionStatus':
APP.UI.setRemoteControlActiveStatus(
participant.getId(),
newValue);
break;
default:
// ignore
}
});
room.on(JitsiConferenceEvents.KICKED, participant => {
APP.UI.hideStats();
APP.store.dispatch(kickedOut(room, participant));
@@ -2081,6 +2186,16 @@ export default {
}));
});
room.addCommandListener(this.commands.defaults.AVATAR_ID,
(data, from) => {
APP.store.dispatch(
participantUpdated({
conference: room,
id: from,
avatarID: data.value
}));
});
APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
this.changeLocalDisplayName.bind(this));
@@ -2376,6 +2491,25 @@ export default {
APP.UI.changeDisplayName('localVideoContainer', displayName);
},
/**
* Adds any room listener.
* @param {string} eventName one of the JitsiConferenceEvents
* @param {Function} listener the function to be called when the event
* occurs
*/
addConferenceListener(eventName, listener) {
room.on(eventName, listener);
},
/**
* Removes any room listener.
* @param {string} eventName one of the JitsiConferenceEvents
* @param {Function} listener the listener to be removed.
*/
removeConferenceListener(eventName, listener) {
room.off(eventName, listener);
},
/**
* Updates the list of current devices.
* @param {boolean} setDeviceListChangeHandler - Whether to add the deviceList change handlers.
@@ -2530,20 +2664,6 @@ export default {
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689
const hasDefaultMicChanged = newDevices.audioinput === 'default';
// This is the case when the local video is muted and a preferred device is connected.
if (requestedInput.video && this.isLocalVideoMuted()) {
// We want to avoid creating a new video track in order to prevent turning on the camera.
requestedInput.video = false;
APP.store.dispatch(updateSettings({ // Update the current selected camera for the device selection dialog.
cameraDeviceId: newDevices.videoinput
}));
delete newDevices.videoinput;
// Removing the current video track in order to force the unmute to select the preferred device.
this.useVideoStream(null);
}
promises.push(
mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
createLocalTracksF,
@@ -2658,7 +2778,7 @@ export default {
* requested
*/
hangup(requestFeedback = false) {
APP.store.dispatch(disableReceiver());
eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
this._stopProxyConnection();
@@ -2675,6 +2795,7 @@ export default {
}
APP.UI.removeAllListeners();
APP.remoteControl.removeAllListeners();
let requestFeedbackPromise;
@@ -2857,6 +2978,29 @@ export default {
}
},
/**
* Returns the desktop sharing source id or undefined if the desktop sharing
* is not active at the moment.
*
* @returns {string|undefined} - The source id. If the track is not desktop
* track or the source id is not available, undefined will be returned.
*/
getDesktopSharingSourceId() {
return this.localVideo.sourceId;
},
/**
* Returns the desktop sharing source type or undefined if the desktop
* sharing is not active at the moment.
*
* @returns {'screen'|'window'|undefined} - The source type. If the track is
* not desktop track or the source type is not available, undefined will be
* returned.
*/
getDesktopSharingSourceType() {
return this.localVideo.sourceType;
},
/**
* Callback invoked by the external api create or update a direct connection
* from the local client to an external client.
@@ -2941,7 +3085,7 @@ export default {
* @param {boolean} muted - New muted status.
*/
setVideoMuteStatus(muted) {
APP.UI.setVideoMuted(this.getMyUserId());
APP.UI.setVideoMuted(this.getMyUserId(), muted);
APP.API.notifyVideoMutedStatusChanged(muted);
},

View File

@@ -275,13 +275,9 @@ var config = {
// // at least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to
// // the high quality.
// minHeightForQualityLvl: {
// 360: 'standard',
// 360: 'standard,
// 720: 'high'
// },
//
// // Provides a way to resize the desktop track to 720p (if it is greater than 720p) before creating a canvas
// // for the presenter mode (camera picture-in-picture mode with screenshare).
// resizeDesktopForPresenter: false
// }
// },
// // Options for the recording limit notification.
@@ -327,6 +323,10 @@ var config = {
// is set in Jicofo and set to 2).
// minParticipants: 2,
// Use the TURN servers discovered via XEP-0215 for the jitsi-videobridge
// connection
// useStunTurn: true,
// Use TURN/UDP servers for the jitsi-videobridge connection (by default
// we filter out TURN/UDP because it is usually not needed since the
// bridge itself is reachable via UDP)
@@ -363,12 +363,17 @@ var config = {
// Default language for the user interface.
// defaultLanguage: 'en',
// Disables profile and the edit of all fields from the profile settings (display name and email)
// disableProfile: false,
// If true all users without a token will be considered guests and all users
// with token will be considered non-guests. Only guests will be allowed to
// edit their profile.
enableUserRolesBasedOnToken: false,
// Whether or not some features are checked based on token.
// enableFeaturesBasedOnToken: false,
// Enable lock room for all moderators, even when userRolesBasedOnToken is enabled and participants are guests.
// lockRoomGuestEnabled: false,
// When enabled the password used for locking a room is restricted to up to the number of digits specified
// roomPasswordNumberOfDigits: 10,
// default: roomPasswordNumberOfDigits: false,
@@ -437,6 +442,9 @@ var config = {
// connection.
enabled: true,
// Use XEP-0215 to fetch STUN and TURN servers.
// useStunTurn: true,
// The STUN servers that will be used in the peer to peer connections
stunServers: [
@@ -678,6 +686,7 @@ var config = {
forceJVB121Ratio
hiddenDomain
ignoreStartMuted
nick
startBitrate
*/

View File

@@ -28,14 +28,9 @@ body {
overflow: hidden;
color: $defaultColor;
background: $defaultBackground;
}
/**
* This will hide the focus indicator if an element receives focus via the mouse,
* but it will still show up on keyboard focus, thus preserving accessibility.
*/
.js-focus-visible :focus:not(.focus-visible) {
outline: none;
&.filmstrip-only {
background: transparent;
}
}
/**
@@ -67,6 +62,16 @@ body {
cursor: pointer;
}
/**
* AtlasKitThemeProvider sets a background color on an app-wrapping div, thereby
* preventing transparency in filmstrip-only mode. The selector chosen to
* override this behavior is specific to where the AtlasKitThemeProvider might
* be placed within the app hierarchy.
*/
.filmstrip-only #react > .ckAJgx {
background: transparent;
}
p {
margin: 0;
}
@@ -201,74 +206,3 @@ form {
background: rgba(0, 0, 0, .5);
border-radius: 4px;
}
.desktop-browser {
@media only screen and (max-width: $smallScreen) {
.watermark {
width: 20%;
height: 20%;
}
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.toolbox-button {
.toolbox-icon {
width: 28px;
height: 28px;
svg {
width: 18px;
height: 18px;
}
}
&:nth-child(2) {
.toolbox-icon {
width: 30px;
height: 30px;
}
}
}
}
}
}
}
@media only screen and (max-width: $verySmallScreen) {
#videoResolutionLabel {
display: none;
}
.vertical-filmstrip .filmstrip {
display: none;
}
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.settings-button-small-icon {
display: none;
}
.toolbox-button {
.toolbox-icon {
width: 18px;
height: 18px;
svg {
width: 12px;
height: 12px;
}
}
&:nth-child(2) {
.toolbox-icon {
width: 20px;
height: 20px;
}
}
}
}
}
}
.chrome-extension-banner {
display: none;
}
}
}

View File

@@ -1,30 +1,30 @@
.con-status {
position: absolute;
top: 24px;
top: 40px;
width: 100%;
z-index: $toolbarZ + 3;
&-container {
background: rgba(28, 32, 37, .5);
border-radius: 3px;
color: #fff;
font-size: 13px;
line-height: 13px;
line-height: 20px;
margin: 0 auto;
width: 320px;
width: 304px;
}
&-header {
background: rgba(28, 32, 37, .5);
align-items: center;
display: flex;
justify-content: space-between;
padding: 8px;
}
&-circle {
border-radius: 50%;
display: inline-block;
padding: 4px;
margin: 8px;
}
&--good {
@@ -40,16 +40,6 @@
}
&-arrow {
height: 36px;
width: 36px;
border-radius: 3px;
margin-left: 8px;
margin-right: 2px;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.16s ease-out;
&--up {
transform: rotate(180deg);
}
@@ -57,10 +47,6 @@
&>svg {
cursor: pointer;
}
&:hover {
background-color: rgba(1,1,1, 0.1);
}
}
&-text {
@@ -68,17 +54,7 @@
}
&-details {
background: rgba(28, 32, 37, .5);
border-top: 1px solid #5E6D7A;
padding: 16px;
transition: opacity 0.16s ease-out;
&-visible {
opacity: 1;
}
&-hidden {
opacity: 0;
}
}
}

View File

@@ -27,4 +27,84 @@
font-size: 50px;
}
&-filmstrip-only {
background-color: $inlayFilmstripOnlyBg;
color: $inlayFilmstripOnlyColor;
margin-left: 20px;
margin-right: 20px;
margin-top: 20px;
bottom: 30px;
position: absolute;
display: flex;
max-height: 120px;
height: 80%;
right: 0px;
border-radius: 4px;
overflow: hidden;
&__content {
padding: 20px;
display: flex;
justify-content: center;
position: relative;
> .button-control {
align-self: center;
}
> #reloadProgressBar {
position: absolute;
left: 0px;
bottom: 0px;
margin-bottom: 0px;
width: 100%;
border-radius: 0px;
}
}
&__title {
font-size: 18px;
font-weight: 600;
}
&__container {
align-self: center;
}
&__text {
margin-top: 10px;
font-size: 14px;
font-weight: 600;
}
&__icon {
font-size: 50px;
align-self: center;
color: $inlayIconColor;
opacity: 0.6;
}
&__icon-container {
text-align: center;
display: flex;
justify-content: center;
position: absolute;
width: 100%;
height: 100%;
top: 0px;
}
&__avatar-container {
height: 100%;
position: relative;
> img {
height: 100%;
}
}
&__icon-background {
background: $inlayIconBg;
opacity: 0.6;
position: absolute;
width: 100%;
height: 100%;
top: 0px;
}
}
}

View File

@@ -14,6 +14,19 @@
margin: 10px;
}
}
.form {
align-items: stretch;
display: flex;
flex-direction: column;
min-width: 400px;
}
.participant-info {
align-items: center;
display: flex;
flex-direction: column;
}
}
}
@@ -87,6 +100,19 @@
}
}
input {
align-self: stretch;
background-color: transparent;
border: 1px solid #B8C7E0;
border-radius: 4px;
color: white;
padding: 12px 8px;
&:focus {
border-color: rgb(3, 118, 218);
}
}
button {
align-self: stretch;
margin: 8px 0;

View File

@@ -7,8 +7,9 @@
display: flex;
flex-direction: column;
position: relative;
overflow: auto;
width: 100%;
height: 100%;
overflow: auto;
.meetings-list-empty {
text-align: center;
@@ -19,34 +20,11 @@
flex-direction: column;
.description {
color: #2f3237;
font-size: 14px;
line-height: 18px;
margin-bottom: 16px;
max-width: 436px;
font-size: 16px;
padding: 20px;
}
}
.meetings-list-empty-image {
text-align: center;
margin: 24px 0 20px 0;
}
.meetings-list-empty-button {
align-items: center;
color: #0163FF;
cursor: pointer;
display: flex;
font-size: 14px;
line-height: 18px;
margin: 24px 0 32px 0;
}
.meetings-list-empty-icon {
display: inline-block;
margin-right: 8px;
}
.button {
background: #0074E0;
border-radius: 4px;
@@ -54,7 +32,7 @@
display: flex;
justify-content: center;
align-items: center;
padding: 8px;
padding: 5px 10px;
cursor: pointer;
}
@@ -65,13 +43,12 @@
}
.item {
background: #fff;
background: rgba(255,255,255,0.50);
box-sizing: border-box;
border-radius: 4px;
display: inline-flex;
margin: 4px 4px 0 4px;
min-height: 60px;
width: calc(100% - 8px);
margin-top: 5px;
min-height: 92px;
width: 100%;
word-break: break-word;
display: flex;
flex-direction: row;
@@ -84,41 +61,37 @@
.left-column {
display: flex;
flex-direction: column;
width: 140px;
flex-grow: 0;
padding-left: 16px;
padding-top: 13px;
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: 16px;
padding-top: 13px;
position: relative;
}
padding-left: 30px;
padding-top: 25px;
.title {
font-size: 12px;
font-weight: 600;
line-height: 16px;
padding-bottom: 4px;
.title {
font-size: 16px;
font-weight: bold;
padding-bottom: 5px;
}
.subtitle {
color: #5E6D7A;
font-weight: normal;
font-size: 12px;
line-height: 16px;
}
.actions {
display: flex;
align-items: center;
justify-content: center;
flex-grow: 0;
margin-right: 16px;
padding-right: 30px;
}
&.with-click-handler {
@@ -126,7 +99,7 @@
}
&.with-click-handler:hover {
background-color: #c7ddff;
background-color: #75A7E7;
}
.add-button {
@@ -147,20 +120,4 @@
display: block
}
}
.delete-meeting {
display: none;
margin-right: 16px;
position: absolute;
&> svg {
fill: #0074e0;
}
}
.item:hover {
.delete-meeting {
display: block;
}
}
}

View File

@@ -3,6 +3,7 @@
&-input-area {
margin: 0 auto;
text-align: center;
width: 320px;
}
&-title {
@@ -41,11 +42,9 @@
&-error {
color: white;
background-color: rgba(225, 45, 45, 0.6);
border-radius: 3px;
background-color: rgba(229, 75, 75, 0.5);
width: 100%;
padding: 2px;
box-sizing: border-box;
padding: 3px;
margin-top: 4px;
font-size: 13px;
text-align: center;
@@ -59,18 +58,84 @@
}
.prejoin-preview {
height: 100%;
position: absolute;
width: 100%;
&--no-video {
background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF;
text-align: center;
}
&-video {
height: 100%;
object-fit: cover;
position: absolute;
width: 100%;
}
&-name {
color: #fff;
font-size: 19px;
line-height: 28px;
&--editable {
background: none;
border: 0;
border-bottom: 1px solid #D1DBE8;
margin: 24px 0 16px 0;
outline: none;
text-align: center;
width: 100%;
&::-webkit-input-placeholder {
@include name-placeholder;
}
&::-moz-placeholder {
@include name-placeholder;
}
&:-ms-input-placeholder {
@include name-placeholder;
}
}
&--text {
margin: 16px 0;
outline: none;
}
}
&-avatar.avatar {
background: #A4B8D1;
margin: 200px auto 0 auto;
}
&-overlay {
height: 100%;
position: absolute;
width: 100%;
z-index: 1;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3));
}
&-bottom-overlay {
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.9) 100%);
bottom: 0;
height: 50%;
position: absolute;
width: 100%;
z-index: 1;
}
&-status {
align-items: center;
align-self: stretch;
bottom: 0;
color: #fff;
display: flex;
font-size: 13px;
min-height: 24px;
justify-content: center;
position: absolute;
text-align: center;
width: 100%;
z-index: 1;
&--warning {

View File

@@ -12,23 +12,12 @@
.premeeting-screen {
align-items: stretch;
background: radial-gradient(50% 50% at 50% 50%, #2A3A4B 20.83%, #1E2A36 100%);
background: radial-gradient(50% 50% at 50% 50%, #5D95C7 0%, #376288 100%), #FFFFFF;
display: flex;
flex-direction: column;
font-size: 1.3em;
z-index: $toolbarZ + 1;
&-avatar {
background-color: #A4B8D1;
margin-bottom: 24px;
text {
fill: black;
font-size: 26px;
font-weight: 400;
}
}
.action-btn {
border-radius: 3px;
color: #fff;
@@ -70,26 +59,22 @@
fill: #AFB6BC;
}
}
.options {
border-left: 1px solid #AFB6BC;
}
}
.options {
border-radius: 3px;
align-items: center;
border-left: 1px solid #fff;
display: flex;
height: 100%;
justify-content: center;
position: absolute;
right: 0;
top: 0;
width: 36px;
&:hover {
background-color: #0262B6;
}
svg {
pointer-events: none;
}
width: 40px;
}
}
@@ -104,7 +89,6 @@
flex: 1;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 24px;
z-index: $toolbarZ + 2;
.title {
@@ -127,14 +111,12 @@
margin-bottom: 16px;
.url {
background: rgba(28, 32, 37, 0.5);
border-radius: 4px;
display: flex;
padding: 8px 10px;
transition: background 0.16s ease-out;
&:hover {
background: #1C2025;
border-radius: 4px;
}
&.done {
@@ -167,23 +149,20 @@
}
input.field {
background-color: white;
border: none;
outline: none;
border-radius: 3px;
font-size: 15px;
line-height: 24px;
color: #1C2025;
background-color: transparent;
border: 1px solid transparent;
color: white;
outline-width: 0;
padding: 8px 0;
text-align: center;
width: 320px;
&.error {
box-shadow: 0px 0px 4px 3px rgba(225, 45, 45, 0.4);
}
width: 100%;
&.focused {
box-shadow: 0px 0px 4px 3px #0376DA;
border-bottom: 1px solid white;
}
&.error::placeholder {
color: $defaultWarningColor;
}
}
}
@@ -191,7 +170,7 @@
.media-btn-container {
display: flex;
justify-content: center;
margin: 24px 0 16px 0;
margin: 32px 0;
width: 100%;
&> div {
@@ -254,7 +233,6 @@
font-size: 13px;
height: 40px;
margin: 0 auto;
transition: background 0.16s ease-out;
width: 320px;
@include flex-centered();
@@ -264,7 +242,7 @@
}
&:hover {
background: rgba(255, 255, 255, 0.1);
background: #1C2025;
@include icon-container(#A4B8D1, #1C2025);
}
@@ -283,6 +261,14 @@
}
&--toggled {
@include icon-container(white, #1C2025);
background: #75757A;
&:hover {
background: #75757A;
@include icon-container(#A4B8D1, #75757A);
}
@include icon-container(#A4B8D1, #75757A);
}
}

View File

@@ -1,67 +1,70 @@
@media only screen and (max-width: $verySmallScreen) {
.welcome {
display: block;
@media only screen and (max-width: $smallScreen) {
.watermark {
width: 20%;
height: 20%;
}
#enter_room {
position: relative;
height: 42px;
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.toolbox-button {
.toolbox-icon {
width: 28px;
height: 28px;
svg {
width: 18px;
height: 18px;
}
}
.welcome-page-button {
font-size: 16px;
left: 0;
position: absolute;
top: 68px;
text-align: center;
width: 100%;
}
}
.header {
background-color: #002637;
#enter_room {
.enter-room-input-container {
padding-right: 0;
}
.warning-without-link,
.warning-with-link {
top: 120px;
&:nth-child(2) {
.toolbox-icon {
width: 30px;
height: 30px;
}
}
}
}
}
.welcome-tabs {
display: none;
}
.header-text-title {
text-align: center;
}
.welcome-cards-container {
padding: 0;
}
&.without-content {
.header {
height: 100%;
}
}
#moderated-meetings {
display: none;
}
.welcome-footer-row-block {
display: block;
}
.welcome-badge {
margin-right: 16px;
}
.welcome-footer {
display: none;
}
}
}
@media only screen and (max-width: $verySmallScreen) {
#videoResolutionLabel {
display: none;
}
.desktop-browser {
.vertical-filmstrip .filmstrip {
display: none;
}
}
.new-toolbox {
.toolbox-content {
.button-group-center, .button-group-left, .button-group-right {
.settings-button-small-icon {
display: none;
}
.toolbox-button {
.toolbox-icon {
width: 18px;
height: 18px;
svg {
width: 12px;
height: 12px;
}
}
&:nth-child(2) {
.toolbox-icon {
width: 20px;
height: 20px;
}
}
}
}
}
}
.chrome-extension-banner {
display: none;
}
}

View File

@@ -161,47 +161,71 @@ $unsupportedDesktopBrowserTextFontSize: 21px;
/**
* The size of the default watermark.
*/
$watermarkWidth: 71px;
$watermarkHeight: 32px;
$watermarkWidth: 186px;
$watermarkHeight: 74px;
$welcomePageWatermarkWidth: 71px;
$welcomePageWatermarkHeight: 32px;
$welcomePageWatermarkWidth: 186px;
$welcomePageWatermarkHeight: 74px;
/**
* Welcome page variables.
*/
$welcomePageDescriptionColor: #fff;
$welcomePageFontFamily: inherit;
$welcomePageBackground: none;
$welcomePageBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageTitleColor: #fff;
$welcomePageHeaderBackground: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('../images/welcome-background.png');
$welcomePageHeaderBackgroundPosition: center;
$welcomePageHeaderBackground: none;
$welcomePageHeaderBackgroundSmall: none;
$welcomePageHeaderBackgroundPosition: none;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: cover;
$welcomePageHeaderBackgroundSize: none;
$welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderTitleMaxWidth: initial;
$welcomePageHeaderTextAlign: center;
$welcomePageHeaderMinHeight: fit-content;
$welcomePageHeaderContainerDisplay: flex;
$welcomePageHeaderContainerMargin: 104px 32px 0 32px;
$welcomePageHeaderTextMarginTop: 35px;
$welcomePageHeaderTextMarginBottom: 35px;
$welcomePageHeaderTextDisplay: flex;
$welcomePageHeaderTextWidth: 650px;
$welcomePageHeaderTextTitleMarginBottom: 0;
$welcomePageHeaderTextTitleFontSize: 42px;
$welcomePageHeaderTextTitleFontWeight: normal;
$welcomePageHeaderTextTitleLineHeight: 50px;
$welcomePageHeaderTextTitleMarginBottom: 16px;
$welcomePageHeaderTextTitleFontSize: 2.5rem;
$welcomePageHeaderTextTitleFontWeight: 500;
$welcomePageHeaderTextTitleLineHeight: 1.18;
$welcomePageHeaderTextTitleOpacity: 1;
$welcomePageHeaderTextDescriptionDisplay: inherit;
$welcomePageHeaderTextDescriptionFontSize: 1rem;
$welcomePageHeaderTextDescriptionFontWeight: 400;
$welcomePageHeaderTextDescriptionLineHeight: 24px;
$welcomePageHeaderTextDescriptionMarginBottom: 20px;
$welcomePageHeaderTextDescriptionAlignSelf: inherit;
$welcomePageEnterRoomDisplay: flex;
$welcomePageEnterRoomWidth: calc(100% - 32px);
$welcomePageEnterRoomPadding: 4px;
$welcomePageEnterRoomMargin: 0 auto;
$welcomePageEnterRoomWidth: 680px;
$welcomePageEnterRoomPadding: 25px 30px;
$welcomePageEnterRoomBorderRadius: 0px;
$welcomePageEnterRoomInputContainerPadding: 0 8px 5px 0px;
$welcomePageEnterRoomInputContainerBorderWidth: 0px 0px 2px 0px;
$welcomePageEnterRoomInputContainerBorderStyle: solid;
$welcomePageEnterRoomInputContainerBorderImage: linear-gradient(to right, #dee1e6, #fff) 1;
$welcomePageEnterRoomTitleDisplay: inherit;
$welcomePageTabContainerDisplay: flex;
$welcomePageTabContentDisplay: inherit;
$welcomePageTabButtonsDisplay: flex;
$welcomePageTabDisplay: block;
$welcomePageButtonWidth: 51px;
$welcomePageButtonMinWidth: inherit;
$welcomePageButtonFontSize: 14px;
$welcomePageButtonHeight: 35px;
$welcomePageButtonFontWeight: inherit;
$welcomePageButtonBorderRadius: 4px;
$welcomePageButtonLineHeight: 35px;
/**
* Deep-linking page variables.
*/

View File

@@ -5,7 +5,6 @@ body.welcome-page {
.welcome {
background-image: $welcomePageBackground;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: $welcomePageFontFamily;
@@ -19,15 +18,21 @@ body.welcome-page {
background-repeat: $welcomePageHeaderBackgroundRepeat;
background-size: $welcomePageHeaderBackgroundSize;
padding-bottom: $welcomePageHeaderPaddingBottom;
background-color: #131519;
height: 400px;
align-items: center;
display: flex;
flex-direction: column;
min-height: $welcomePageHeaderMinHeight;
overflow: hidden;
position: relative;
text-align: center;
.header-container {
display: $welcomePageHeaderContainerDisplay;
.header-text {
display: $welcomePageHeaderTextDisplay;
flex-direction: column;
margin: $welcomePageHeaderContainerMargin;
margin-top: $watermarkHeight + $welcomePageHeaderTextMarginTop;
margin-bottom: $welcomePageHeaderTextMarginBottom;
max-width: calc(100% - 40px);
width: $welcomePageHeaderTextWidth;
z-index: $zindex2;
}
@@ -37,52 +42,50 @@ body.welcome-page {
font-weight: $welcomePageHeaderTextTitleFontWeight;
line-height: $welcomePageHeaderTextTitleLineHeight;
margin-bottom: $welcomePageHeaderTextTitleMarginBottom;
max-width: $welcomePageHeaderTitleMaxWidth;
opacity: $welcomePageHeaderTextTitleOpacity;
text-align: $welcomePageHeaderTextAlign;
}
.header-text-subtitle {
color: #fff;
font-size: 20px;
font-weight: 600;
line-height: 26px;
margin: 16px 0 32px 0;
text-align: $welcomePageHeaderTextAlign;
.header-text-description {
display: $welcomePageHeaderTextDescriptionDisplay;
color: $welcomePageDescriptionColor;
font-size: $welcomePageHeaderTextDescriptionFontSize;
font-weight: $welcomePageHeaderTextDescriptionFontWeight;
line-height: $welcomePageHeaderTextDescriptionLineHeight;
margin-bottom: $welcomePageHeaderTextDescriptionMarginBottom;
align-self: $welcomePageHeaderTextDescriptionAlignSelf;
}
#enter_room {
display: $welcomePageEnterRoomDisplay;
align-items: center;
max-width: 480px;
max-width: calc(100% - 40px);
width: $welcomePageEnterRoomWidth;
z-index: $zindex2;
background-color: #fff;
padding: $welcomePageEnterRoomPadding;
border-radius: 4px;
margin: $welcomePageEnterRoomMargin;
border-radius: $welcomePageEnterRoomBorderRadius;
.enter-room-input-container {
width: 100%;
padding: $welcomePageEnterRoomInputContainerPadding;
text-align: left;
color: #253858;
flex-grow: 1;
height: fit-content;
padding-right: 4px;
position: relative;
.enter-room-title {
display: $welcomePageEnterRoomTitleDisplay;
font-size: 18px;
font-weight: bold;
padding-bottom: 5px;
}
.enter-room-input {
border: 0;
background: #fff;
border-width: $welcomePageEnterRoomInputContainerBorderWidth;
border-style: $welcomePageEnterRoomInputContainerBorderStyle;
border-image: $welcomePageEnterRoomInputContainerBorderImage;
display: inline-block;
height: 50px;
width: 100%;
font-size: 14px;
padding-left: 10px;
&:focus {
outline: auto 2px #005fcc;
}
}
.insecure-room-name-warning {
@@ -106,28 +109,16 @@ body.welcome-page {
}
}
.warning-without-link {
position: absolute;
top: 44px;
left: -10px;
}
.warning-with-link {
position: absolute;
top: 84px;
}
}
#moderated-meetings {
max-width: calc(100% - 40px);
padding: 16px 0 39px 0;
margin: $welcomePageEnterRoomMargin;
width: $welcomePageEnterRoomWidth;
p {
color: $welcomePageDescriptionColor;
text-align: $welcomePageHeaderTextAlign;
text-align: left;
a {
color: inherit;
@@ -135,70 +126,76 @@ body.welcome-page {
}
}
}
}
.tab-container {
font-size: 16px;
position: relative;
text-align: left;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
.tab-content{
display: $welcomePageTabContentDisplay;
height: 250px;
margin: 5px 0px;
overflow: hidden;
flex-grow: 1;
.tab-container {
font-size: 16px;
position: relative;
}
text-align: left;
min-height: 354px;
width: 710px;
background: #75A7E7;
display: $welcomePageTabContainerDisplay;
flex-direction: column;
.tab-buttons {
background-color: #c7ddff;
border-radius: 6px;
color: #0163FF;
font-size: 14px;
line-height: 18px;
margin: 4px;
display: $welcomePageTabButtonsDisplay;
.tab {
background-color: #c7ddff;
border-radius: 7px;
cursor: pointer;
display: $welcomePageTabDisplay;
.tab-content{
display: $welcomePageTabContentDisplay;
margin: 5px 0px;
overflow: hidden;
flex-grow: 1;
margin: 2px;
padding: 7px 0;
text-align: center;
position: relative;
&.selected {
background-color: #FFF;
> * {
position: absolute;
}
}
.tab-buttons {
font-size: 18px;
color: #FFFFFF;
display: $welcomePageTabButtonsDisplay;
flex-grow: 0;
flex-direction: row;
min-height: 54px;
width: 100%;
.tab {
display: $welcomePageTabDisplay;
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 {
border: 0;
font-size: 14px;
width: $welcomePageButtonWidth;
min-width: $welcomePageButtonMinWidth;
height: $welcomePageButtonHeight;
font-size: $welcomePageButtonFontSize;
font-weight: $welcomePageButtonFontWeight;
background: #0074E0;
border-radius: 3px;
border-radius: $welcomePageButtonBorderRadius;
color: #FFFFFF;
text-align: center;
vertical-align: middle;
line-height: $welcomePageButtonLineHeight;
cursor: pointer;
padding: 16px 20px;
&:focus-within {
outline: auto 2px #022e61;
}
}
.welcome-page-settings {
background: rgba(255, 255, 255, 0.38);
border-radius: 3px;
color: $welcomePageDescriptionColor;
padding: 4px;
position: absolute;
top: 32px;
right: 32px;
@@ -220,84 +217,4 @@ body.welcome-page {
height: $welcomePageWatermarkHeight;
}
}
&.without-content {
.welcome-card {
min-width: 500px;
max-width: 580px;
}
}
.welcome-cards-container {
color:#131519;
padding-top: 40px;
}
.welcome-card-row {
display: flex;
justify-content: center;
padding: 0 32px;
}
.welcome-card-text {
padding: 32px;
}
.welcome-card {
width: 49%;
border-radius: 8px;
&--dark {
background: #444447;
color: #fff;
}
&--blue {
background: #D5E5FF;
}
&--grey {
background: #F2F3F4;
}
&--shadow {
box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.15);
}
}
.welcome-footer {
background: #131519;
color: #fff;
margin-top: 40px;
position: relative;
}
.welcome-footer-centered {
max-width: 688px;
margin: 0 auto;
}
.welcome-footer-padded {
padding: 0px 16px;
}
.welcome-footer-row-block {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #424447;
&:last-child {
border-bottom: none;
}
}
.welcome-footer--row-1 {
padding: 40px 0 24px 0;
}
.welcome-footer-row-1-text {
max-width: 200px;
margin-right: 16px;
}
}

View File

@@ -67,6 +67,20 @@
}
}
/**
* Style the filmstrip videos in filmstrip-only mode.
*/
&__videos-filmstripOnly {
margin-top: auto;
margin-bottom: auto;
.filmstrip__videos {
&#filmstripLocalVideo {
bottom: 0px;
}
}
}
.remote-videos-container {
transition: opacity 1s;
}

View File

@@ -145,6 +145,26 @@
}
}
/**
* Override other styles to support vertical filmstrip mode.
*/
.filmstrip-only .vertical-filmstrip {
.filmstrip {
flex-direction: row-reverse;
}
.filmstrip__videos-filmstripOnly {
margin-top: auto;
margin-bottom: auto;
height: 100%;
}
.filmstrip__videos {
&#filmstripLocalVideo {
bottom: 0px;
}
}
}
/**
* Workarounds for Edge and Firefox not handling scrolling properly with
* flex-direction: column-reverse. The remove videos in filmstrip should

View File

@@ -1,4 +1,9 @@
.video-quality-dialog {
.hide-warning {
height: 0;
visibility: hidden;
}
.video-quality-dialog-title {
margin-bottom: 10px;
}
@@ -104,6 +109,30 @@
word-spacing: unset;
}
}
&.video-not-supported {
.video-quality-dialog-labels {
color: gray;
}
.video-quality-dialog-slider {
@mixin sliderTrackDisabledStyles() {
background: rgba(14, 22, 36, 0.1);
}
&::-ms-track {
@include sliderTrackDisabledStyles();
}
&::-moz-range-track {
@include sliderTrackDisabledStyles();
}
&::-webkit-slider-runnable-track {
@include sliderTrackDisabledStyles();
}
}
}
}
.modal-dialog-form {

View File

@@ -8,10 +8,16 @@
position: fixed;
z-index: $overlayZ;
background: $defaultBackground;
&.filmstrip-only {
@include transparentBg($filmstripOnlyOverlayBg, 0.8);
}
}
&__container-light {
@include transparentBg($defaultBackground, 0.7);
&.filmstrip-only {
@include transparentBg($filmstripOnlyOverlayBg, 0.2);
}
}
&__content {
@@ -21,6 +27,11 @@
width: 56%;
left: 50%;
@include transform(translateX(-50%));
&.filmstrip-only {
left: 0px;
width: 100%;
@include transform(none);
}
&_bottom {
position: absolute;

View File

@@ -41,6 +41,7 @@ $overlayButtonBg: #0074E0;
* Color variables
**/
$defaultBackground: #474747;
$filmstripOnlyOverlayBg: #000;
$reloadProgressBarBg: #0074E0;
/**
@@ -58,6 +59,10 @@ $dialogErrorText: #344563;
**/
$inlayColorBg: lighten($defaultBackground, 20%);
$inlayBorderColor: lighten($baseLight, 10%);
$inlayIconBg: #000;
$inlayIconColor: #fff;
$inlayFilmstripOnlyColor: #474747;
$inlayFilmstripOnlyBg: #fff;
// Main controls
$placeHolderColor: #a7a7a7;

View File

@@ -150,6 +150,11 @@ denied-peer-ip=240.0.0.0-255.255.255.255" >> $TURN_CONFIG
echo "------------------------------------------------"
fi
# Enable turn server in config.js
if [ -f $JITSI_MEET_CONFIG ] ; then
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
fi
# and we're done with debconf
db_stop
;;

View File

@@ -13,5 +13,3 @@ lang /usr/share/jitsi-meet/
connection_optimization /usr/share/jitsi-meet/
resources/robots.txt /usr/share/jitsi-meet/
resources/*.sh /usr/share/jitsi-meet/scripts/
pwa-worker.js /usr/share/jitsi-meet/
manifest.json /usr/share/jitsi-meet/

View File

@@ -4,5 +4,6 @@ var config = {
muc: 'conference.jitsi.example.com', // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.jitsi.example.com' // FIXME: use XEP-0030
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that
};

View File

@@ -9,6 +9,7 @@ var config = {
muc: 'conference.'+subdomain+'jitsi.example.com', // FIXME: use XEP-0030
focus: 'focus.jitsi.example.com',
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
websocket: 'wss://jitsi.example.com/xmpp-websocket'
};

View File

@@ -13,7 +13,9 @@
height: 180,
parentNode: undefined,
configOverwrite: {},
interfaceConfigOverwrite: {}
interfaceConfigOverwrite: {
filmStripOnly: true
}
}
var api = new JitsiMeetExternalAPI(domain, options);
</script>

View File

@@ -1,5 +1,5 @@
// flow-typed signature: 8da1e134b3de1d6f6bf9ba1cc7e2dc7e
// flow-typed version: 387a235736/react-redux_v7.x.x/flow_>=v0.104.x
// flow-typed signature: d2ddacbbca9700881249a9435381e689
// flow-typed version: c6154227d1/react-redux_v7.x.x/flow_>=v0.89.x <=v0.103.x
/**
The order of type arguments for connect() is as follows:
@@ -219,7 +219,6 @@ declare module "react-redux" {
declare export class Provider<Store> extends React$Component<{
store: Store,
children?: React$Node,
...
}> {}
declare export function createProvider(
@@ -238,7 +237,6 @@ declare module "react-redux" {
shouldHandleStateChanges?: boolean,
storeKey?: string,
forwardRef?: boolean,
...
};
declare type SelectorFactoryOptions<Com> = {
@@ -251,7 +249,6 @@ declare module "react-redux" {
displayName: string,
wrappedComponentName: string,
WrappedComponent: Com,
...
};
declare type MapStateToPropsEx<S: Object, SP: Object, RSP: Object> = (
@@ -278,14 +275,12 @@ declare module "react-redux" {
OP: Object,
CP: Object,
EFO: Object,
ST: { [_: $Keys<Com>]: any, ... },
ST: { [_: $Keys<Com>]: any },
>(
selectorFactory: SelectorFactory<Com, D, S, OP, EFO, CP>,
connectAdvancedOptions: ?(ConnectAdvancedOptions & EFO),
): (component: Com) => React$ComponentType<OP> & $Shape<ST>;
declare export function batch(() => void): void
declare export default {
Provider: typeof Provider,
createProvider: typeof createProvider,
@@ -294,7 +289,5 @@ declare module "react-redux" {
useDispatch: typeof useDispatch,
useSelector: typeof useSelector,
useStore: typeof useStore,
batch: typeof batch,
...
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,21 +0,0 @@
<svg width="68" height="72" viewBox="0 0 68 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="5.64514" width="65.3548" height="65.3548" rx="7" stroke="#A4B8D1" stroke-width="2"/>
<rect y="23.2258" width="67.3548" height="2.0213" fill="#A4B8D1"/>
<rect x="14.5161" width="2.32258" height="14.5161" fill="#A4B8D1"/>
<rect x="11.6129" y="12.1935" width="8.12903" height="2.32258" fill="#A4B8D1"/>
<rect x="50.5161" width="2.32258" height="14.5161" fill="#A4B8D1"/>
<rect x="47.6129" y="12.1935" width="8.12903" height="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="52.258" cy="37.7419" r="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="15.0968" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
<circle cx="24.387" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="15.0968" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="33.6774" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="42.9677" cy="56.3226" r="2.32258" fill="#A4B8D1"/>
<circle cx="52.258" cy="47.0322" r="2.32258" fill="#A4B8D1"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

BIN
images/watermark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

View File

@@ -4,18 +4,11 @@
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#2A3A4B">
<!--#include virtual="base.html" -->
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
<link rel="stylesheet" href="css/all.css">
<link rel="manifest" id="manifest-placeholder">
<script>
// Dynamically generate the manifest location URL. It must be served from the document origin, and we may have
// the base pointing to the CDN. This way we can generate a full URL which will bypass the base.
document.querySelector('#manifest-placeholder').setAttribute('href', window.location.origin + '/manifest.json');
document.addEventListener('DOMContentLoaded', () => {
if (!JitsiMeetJS.app) {
return;
@@ -24,21 +17,7 @@
JitsiMeetJS.app.renderEntryPoint({
Component: JitsiMeetJS.app.entryPoints.APP
})
const isElectron = navigator.userAgent.includes('Electron');
const shouldRegisterWorker = !isElectron && 'serviceWorker' in navigator;
if (shouldRegisterWorker) {
navigator.serviceWorker
.register(window.location.origin + '/pwa-worker.js')
.then(reg => {
console.log('Service worker registered.', reg);
})
.catch(err => {
console.log(err);
});
}
});
})
</script>
<script>
// IE11 and earlier can be identified via their user agent and be
@@ -182,13 +161,9 @@
<!--#include virtual="title.html" -->
<!--#include virtual="plugin.head.html" -->
<!--#include virtual="static/welcomePageAdditionalContent.html" -->
<!--#include virtual="static/welcomePageAdditionalCard.html" -->
<!--#include virtual="static/settingsToolbarAdditionalContent.html" -->
</head>
<body>
<noscript>
<div>JavaScript is disabled. </br>For this site to work you have to enable JavaScript.</div>
</noscript>
<!--#include virtual="body.html" -->
<div id="react"></div>
</body>

View File

@@ -46,9 +46,9 @@ var interfaceConfig = {
DEFAULT_BACKGROUND: '#474747',
DEFAULT_LOCAL_DISPLAY_NAME: 'me',
DEFAULT_LOGO_URL: 'images/watermark.svg',
DEFAULT_LOGO_URL: 'images/watermark.png',
DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.png',
DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
@@ -86,9 +86,7 @@ var interfaceConfig = {
*/
DISABLE_VIDEO_BACKGROUND: false,
DISPLAY_WELCOME_FOOTER: true,
DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: false,
DISPLAY_WELCOME_PAGE_CONTENT: false,
DISPLAY_WELCOME_PAGE_CONTENT: true,
DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false,
ENABLE_DIAL_OUT: true,
@@ -97,6 +95,11 @@ var interfaceConfig = {
FILM_STRIP_MAX_HEIGHT: 120,
/**
* Whether to only show the filmstrip (and hide the toolbar).
*/
filmStripOnly: false,
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
/**
@@ -133,21 +136,6 @@ var interfaceConfig = {
*/
MOBILE_APP_PROMO: true,
/**
* Specify custom URL for downloading android mobile app.
*/
MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
/**
* Specify custom URL for downloading f droid app.
*/
MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/',
/**
* Specify URL for downloading ios mobile app.
*/
MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
NATIVE_APP_NAME: 'Jitsi Meet',
// Names of browsers which should show a warning stating the current browser
@@ -181,6 +169,7 @@ var interfaceConfig = {
SHOW_JITSI_WATERMARK: true,
SHOW_POWERED_BY: false,
SHOW_PROMOTIONAL_CLOSE_PAGE: false,
SHOW_WATERMARK_FOR_GUESTS: true, // if watermark is disabled by default, it can be shown only for guests
/*
* If indicated some of the error dialogs may point to the support URL for
@@ -234,12 +223,27 @@ var interfaceConfig = {
*/
VIDEO_QUALITY_LABEL_DISABLED: false,
/**
* When enabled, the kick participant button will not be presented for users without a JWT
*/
// HIDE_KICK_BUTTON_FOR_GUESTS: false,
/**
* How many columns the tile view can expand to. The respected range is
* between 1 and 5.
*/
// TILE_VIEW_MAX_COLUMNS: 5,
/**
* Specify custom URL for downloading android mobile app.
*/
// MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
/**
* Specify URL for downloading ios mobile app.
*/
// MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
/**
* Specify Firebase dynamic link properties for the mobile apps.
*/

View File

@@ -66,7 +66,6 @@ target 'JitsiMeet' do
pod 'RNSVG', :path => '../node_modules/react-native-svg'
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
pod 'RNDefaultPreference', :path => '../node_modules/react-native-default-preference'
pod 'react-native-splash-screen', :path => '../node_modules/react-native-splash-screen'
# Native pod dependencies
#

View File

@@ -12,14 +12,14 @@ PODS:
- CocoaLumberjack/Core (= 3.5.3)
- CocoaLumberjack/Core (3.5.3)
- DoubleConversion (1.1.6)
- FBLazyVector (0.61.5-jitsi.2)
- FBReactNativeSpec (0.61.5-jitsi.2):
- FBLazyVector (0.61.5-jitsi.1)
- FBReactNativeSpec (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- RCTRequired (= 0.61.5-jitsi.2)
- RCTTypeSafety (= 0.61.5-jitsi.2)
- React-Core (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- RCTRequired (= 0.61.5-jitsi.1)
- RCTTypeSafety (= 0.61.5-jitsi.1)
- React-Core (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
- Firebase/Analytics (6.33.0):
- Firebase/Core
- Firebase/Core (6.33.0):
@@ -120,169 +120,169 @@ PODS:
- nanopb/encode (1.30906.0)
- ObjectiveDropboxOfficial (3.9.4)
- PromisesObjC (1.2.10)
- RCTRequired (0.61.5-jitsi.2)
- RCTTypeSafety (0.61.5-jitsi.2):
- FBLazyVector (= 0.61.5-jitsi.2)
- RCTRequired (0.61.5-jitsi.1)
- RCTTypeSafety (0.61.5-jitsi.1):
- FBLazyVector (= 0.61.5-jitsi.1)
- Folly (= 2018.10.22.00)
- RCTRequired (= 0.61.5-jitsi.2)
- React-Core (= 0.61.5-jitsi.2)
- React (0.61.5-jitsi.2):
- React-Core (= 0.61.5-jitsi.2)
- React-Core/DevSupport (= 0.61.5-jitsi.2)
- React-Core/RCTWebSocket (= 0.61.5-jitsi.2)
- React-RCTActionSheet (= 0.61.5-jitsi.2)
- React-RCTAnimation (= 0.61.5-jitsi.2)
- React-RCTBlob (= 0.61.5-jitsi.2)
- React-RCTImage (= 0.61.5-jitsi.2)
- React-RCTLinking (= 0.61.5-jitsi.2)
- React-RCTNetwork (= 0.61.5-jitsi.2)
- React-RCTSettings (= 0.61.5-jitsi.2)
- React-RCTText (= 0.61.5-jitsi.2)
- React-RCTVibration (= 0.61.5-jitsi.2)
- React-Core (0.61.5-jitsi.2):
- RCTRequired (= 0.61.5-jitsi.1)
- React-Core (= 0.61.5-jitsi.1)
- React (0.61.5-jitsi.1):
- React-Core (= 0.61.5-jitsi.1)
- React-Core/DevSupport (= 0.61.5-jitsi.1)
- React-Core/RCTWebSocket (= 0.61.5-jitsi.1)
- React-RCTActionSheet (= 0.61.5-jitsi.1)
- React-RCTAnimation (= 0.61.5-jitsi.1)
- React-RCTBlob (= 0.61.5-jitsi.1)
- React-RCTImage (= 0.61.5-jitsi.1)
- React-RCTLinking (= 0.61.5-jitsi.1)
- React-RCTNetwork (= 0.61.5-jitsi.1)
- React-RCTSettings (= 0.61.5-jitsi.1)
- React-RCTText (= 0.61.5-jitsi.1)
- React-RCTVibration (= 0.61.5-jitsi.1)
- React-Core (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-Core/Default (= 0.61.5-jitsi.1)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/CoreModulesHeaders (0.61.5-jitsi.2):
- React-Core/CoreModulesHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/Default (0.61.5-jitsi.2):
- React-Core/Default (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/DevSupport (0.61.5-jitsi.2):
- React-Core/DevSupport (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default (= 0.61.5-jitsi.2)
- React-Core/RCTWebSocket (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-jsinspector (= 0.61.5-jitsi.2)
- React-Core/Default (= 0.61.5-jitsi.1)
- React-Core/RCTWebSocket (= 0.61.5-jitsi.1)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- React-jsinspector (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTActionSheetHeaders (0.61.5-jitsi.2):
- React-Core/RCTActionSheetHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTAnimationHeaders (0.61.5-jitsi.2):
- React-Core/RCTAnimationHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTBlobHeaders (0.61.5-jitsi.2):
- React-Core/RCTBlobHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTImageHeaders (0.61.5-jitsi.2):
- React-Core/RCTImageHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTLinkingHeaders (0.61.5-jitsi.2):
- React-Core/RCTLinkingHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTNetworkHeaders (0.61.5-jitsi.2):
- React-Core/RCTNetworkHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTSettingsHeaders (0.61.5-jitsi.2):
- React-Core/RCTSettingsHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTTextHeaders (0.61.5-jitsi.2):
- React-Core/RCTTextHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTVibrationHeaders (0.61.5-jitsi.2):
- React-Core/RCTVibrationHeaders (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-Core/RCTWebSocket (0.61.5-jitsi.2):
- React-Core/RCTWebSocket (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsiexecutor (= 0.61.5-jitsi.2)
- React-Core/Default (= 0.61.5-jitsi.1)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsiexecutor (= 0.61.5-jitsi.1)
- Yoga
- React-CoreModules (0.61.5-jitsi.2):
- FBReactNativeSpec (= 0.61.5-jitsi.2)
- React-CoreModules (0.61.5-jitsi.1):
- FBReactNativeSpec (= 0.61.5-jitsi.1)
- Folly (= 2018.10.22.00)
- RCTTypeSafety (= 0.61.5-jitsi.2)
- React-Core/CoreModulesHeaders (= 0.61.5-jitsi.2)
- React-RCTImage (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- React-cxxreact (0.61.5-jitsi.2):
- RCTTypeSafety (= 0.61.5-jitsi.1)
- React-Core/CoreModulesHeaders (= 0.61.5-jitsi.1)
- React-RCTImage (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
- React-cxxreact (0.61.5-jitsi.1):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-jsinspector (= 0.61.5-jitsi.2)
- React-jsi (0.61.5-jitsi.2):
- React-jsinspector (= 0.61.5-jitsi.1)
- React-jsi (0.61.5-jitsi.1):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-jsi/Default (= 0.61.5-jitsi.2)
- React-jsi/Default (0.61.5-jitsi.2):
- React-jsi/Default (= 0.61.5-jitsi.1)
- React-jsi/Default (0.61.5-jitsi.1):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-jsiexecutor (0.61.5-jitsi.2):
- React-jsiexecutor (0.61.5-jitsi.1):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-jsinspector (0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-jsinspector (0.61.5-jitsi.1)
- react-native-background-timer (2.4.0):
- React
- react-native-calendar-events (2.0.0):
@@ -291,66 +291,64 @@ PODS:
- React
- react-native-netinfo (4.1.5):
- React
- react-native-splash-screen (3.2.0):
- react-native-webrtc (1.84.0):
- React
- react-native-webrtc (1.84.1):
- React-Core
- react-native-webview (10.9.0):
- React
- React-RCTActionSheet (0.61.5-jitsi.2):
- React-Core/RCTActionSheetHeaders (= 0.61.5-jitsi.2)
- React-RCTAnimation (0.61.5-jitsi.2):
- React-Core/RCTAnimationHeaders (= 0.61.5-jitsi.2)
- React-RCTBlob (0.61.5-jitsi.2):
- React-Core/RCTBlobHeaders (= 0.61.5-jitsi.2)
- React-Core/RCTWebSocket (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- React-RCTNetwork (= 0.61.5-jitsi.2)
- React-RCTImage (0.61.5-jitsi.2):
- React-Core/RCTImageHeaders (= 0.61.5-jitsi.2)
- React-RCTNetwork (= 0.61.5-jitsi.2)
- React-RCTLinking (0.61.5-jitsi.2):
- React-Core/RCTLinkingHeaders (= 0.61.5-jitsi.2)
- React-RCTNetwork (0.61.5-jitsi.2):
- React-Core/RCTNetworkHeaders (= 0.61.5-jitsi.2)
- React-RCTSettings (0.61.5-jitsi.2):
- React-Core/RCTSettingsHeaders (= 0.61.5-jitsi.2)
- React-RCTText (0.61.5-jitsi.2):
- React-Core/RCTTextHeaders (= 0.61.5-jitsi.2)
- React-RCTVibration (0.61.5-jitsi.2):
- React-Core/RCTVibrationHeaders (= 0.61.5-jitsi.2)
- ReactCommon/jscallinvoker (0.61.5-jitsi.2):
- React-RCTActionSheet (0.61.5-jitsi.1):
- React-Core/RCTActionSheetHeaders (= 0.61.5-jitsi.1)
- React-RCTAnimation (0.61.5-jitsi.1):
- React-Core/RCTAnimationHeaders (= 0.61.5-jitsi.1)
- React-RCTBlob (0.61.5-jitsi.1):
- React-Core/RCTBlobHeaders (= 0.61.5-jitsi.1)
- React-Core/RCTWebSocket (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- React-RCTNetwork (= 0.61.5-jitsi.1)
- React-RCTImage (0.61.5-jitsi.1):
- React-Core/RCTImageHeaders (= 0.61.5-jitsi.1)
- React-RCTNetwork (= 0.61.5-jitsi.1)
- React-RCTLinking (0.61.5-jitsi.1):
- React-Core/RCTLinkingHeaders (= 0.61.5-jitsi.1)
- React-RCTNetwork (0.61.5-jitsi.1):
- React-Core/RCTNetworkHeaders (= 0.61.5-jitsi.1)
- React-RCTSettings (0.61.5-jitsi.1):
- React-Core/RCTSettingsHeaders (= 0.61.5-jitsi.1)
- React-RCTText (0.61.5-jitsi.1):
- React-Core/RCTTextHeaders (= 0.61.5-jitsi.1)
- React-RCTVibration (0.61.5-jitsi.1):
- React-Core/RCTVibrationHeaders (= 0.61.5-jitsi.1)
- ReactCommon/jscallinvoker (0.61.5-jitsi.1):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-cxxreact (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule (0.61.5-jitsi.2):
- React-cxxreact (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule (0.61.5-jitsi.1):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-Core (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/samples (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/core (0.61.5-jitsi.2):
- React-Core (= 0.61.5-jitsi.1)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/samples (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/core (0.61.5-jitsi.1):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-Core (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/samples (0.61.5-jitsi.2):
- React-Core (= 0.61.5-jitsi.1)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/samples (0.61.5-jitsi.1):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-Core (= 0.61.5-jitsi.2)
- React-cxxreact (= 0.61.5-jitsi.2)
- React-jsi (= 0.61.5-jitsi.2)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.2)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- React-Core (= 0.61.5-jitsi.1)
- React-cxxreact (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
- RNCAsyncStorage (1.3.4):
- React
- RNDefaultPreference (1.4.2):
@@ -363,7 +361,7 @@ PODS:
- RNSound/Core (= 0.11.0)
- RNSound/Core (0.11.0):
- React
- RNSVG (10.1.0):
- RNSVG (9.7.1):
- React
- RNWatch (0.4.3):
- React
@@ -397,7 +395,6 @@ DEPENDENCIES:
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
@@ -480,8 +477,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-keep-awake"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
react-native-splash-screen:
:path: "../node_modules/react-native-splash-screen"
react-native-webrtc:
:path: "../node_modules/react-native-webrtc"
react-native-webview:
@@ -528,8 +523,8 @@ SPEC CHECKSUMS:
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
FBLazyVector: ca7f56c8ff6cd8590f7a673d7903b06019805581
FBReactNativeSpec: 8136c3cf27de2bb310a69cffbb423c5643f5c1c4
FBLazyVector: 4a5251159a3ed05dc11cc8b74cf937869935814b
FBReactNativeSpec: 6fa602a20993212cc9877a81838578ffb0008bc9
Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
FirebaseAnalytics: 5dd088bd2e67bb9d13dbf792d1164ceaf3052193
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
@@ -548,40 +543,39 @@ SPEC CHECKSUMS:
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151
RCTRequired: a686731276578c125dff205f08b6ec9cee6ede32
RCTTypeSafety: 88e5500e801c00d16a3d1895e3470d13beed6584
React: 8b2bcf6a93846e47a7a365a54ec6edeb78b37701
React-Core: 3fbdbc87c18c4742b735ff9a0c02fa38c87e0fba
React-CoreModules: f6f8a8212aec52a21251c0af58bdb037b57c70b3
React-cxxreact: c6ad34143db06a5c3cb0e8e169c775475287ac9c
React-jsi: ddb471a56185e4007835a1bba0882ecb5ce3dc0e
React-jsiexecutor: 67106691c60030ec888d7cbbc4f48a3168e27a02
React-jsinspector: 92ceee6c66dc19886289b52436ade7e020b89602
RCTRequired: f63dd90a89a60602acdd44c42e5d2645ca60ab79
RCTTypeSafety: 24a3c6d55684046ed550b1d0ef083a9bf71c8bd4
React: 71c5a51135f291c3b32c0b558e167b858ae50e84
React-Core: e82c03ff91062abf963f35bf99a357154e570285
React-CoreModules: e236aeecd18cec37743c8c50562431db5302f668
React-cxxreact: 526ec106aa1bf2b3f6aab2a7d528d1d23d5f59c2
React-jsi: 4f35c1a2273d193a80c1c3831c808413840c260c
React-jsiexecutor: de1c37cf59ae9adcbf2be82eea0e090dc3f3205e
React-jsinspector: b76c4e84a7833bb4c90549d59ed53ec299ff912b
react-native-background-timer: e0384ea2fa5a98f67f84f9c4dc274260ddd674ed
react-native-calendar-events: 1442fad71a00388f933cfa25512588fec300fcf8
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webrtc: edd689b0d5a462d7a6f6f52bca3f9414fc0ee11c
react-native-webrtc: 9268ae9a2bc9730796b0968d012327e92c392adf
react-native-webview: 6ee7868ca8eba635dbf7963986d1ab7959da0391
React-RCTActionSheet: bcbc311dc3b47bc8efb2737ff0940239a45789a9
React-RCTAnimation: 65f61080ce632f6dea23d52e354ffac9948396c6
React-RCTBlob: 70d88f7b68b5c44953cdb286ac2e36a7a509a97e
React-RCTImage: e0d25b620e42de91ed791ef129e2d3a0df1eb5ab
React-RCTLinking: bc2287cfd9e56403ecea5dafdbdac8c57fa1ac36
React-RCTNetwork: cd8ae8fc787c02ed5152fe9cbf7521ee70c1bce7
React-RCTSettings: f6667271ccd8876a934134b73002b5a2714e1525
React-RCTText: 4f1b99f228278d2a5e9008eced8dc9c974c4a270
React-RCTVibration: c1041024893fdfdb8371e7c720c437751b711676
ReactCommon: 18014e1d98dbeb9141e935cfe35fc93bd511ffb6
React-RCTActionSheet: b72ddbfbe15b44ce691d128e4b582f4bb9abb540
React-RCTAnimation: cfaefba5024499d336b76ab850e6bd33b232b5e3
React-RCTBlob: c427e643bef82999deeab97489ba43298ecfbe24
React-RCTImage: 79934bc96f3349da6a75b1d61cad594a932e4097
React-RCTLinking: 12b153399567c30efac0b32bb00f9c064587dc26
React-RCTNetwork: 603ad75778a54521b7797fd07c67dff562317526
React-RCTSettings: 8d45fcf14513582539ea1ddea69391207de7f046
React-RCTText: b4c29897c3df0c9f112e29aa3167fa6caf40b690
React-RCTVibration: a1bcfcdc0b5a73a1b0829a34cee22bd0e95bacba
ReactCommon: 675681aba4fecff5acbc0e440530cc422103c610
RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
RNDefaultPreference: 56a405ce61033ac77b95004dccd7ac54c2eb50d1
RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
RNSound: c980916b596cc15c8dcd2f6ecd3b13c4881dbe20
RNSVG: 069864be08c9fe065a2cf7e63656a34c78653c99
RNSVG: aac12785382e8fd4f28d072fe640612e34914631
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
Yoga: 7b4209fda2441f99d54dd6cf4c82b094409bb68f
PODFILE CHECKSUM: f2400f8e5a52c4d91697cbacba6956569efc5ab8
PODFILE CHECKSUM: 224e84629bf45ae487c4ebc66faf33ec8304fb67
COCOAPODS: 1.9.3

View File

@@ -53,9 +53,6 @@
[[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:![jitsiMeet isCrashReportingDisabled]];
}
ViewController *rootController = (ViewController *)self.window.rootViewController;
[jitsiMeet showSplashScreen:rootController.view];
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
return YES;

View File

@@ -67,6 +67,4 @@
- (BOOL)isCrashReportingDisabled;
- (void)showSplashScreen:(UIView * _Nonnull) rootView;
@end

View File

@@ -22,7 +22,6 @@
#import "JitsiMeetView+Private.h"
#import "RCTBridgeWrapper.h"
#import "ReactUtils.h"
#import "RNSplashScreen.h"
#import <RNGoogleSignin/RNGoogleSignin.h>
#import <WebRTC/RTCLogging.h>
@@ -184,10 +183,6 @@
return nil;
}
- (void)showSplashScreen:(UIView*)rootView {
[RNSplashScreen showSplash:@"LaunchScreen" inRootView:rootView];
}
#pragma mark - Property getter / setters
- (NSArray<NSString *> *)universalLinkDomains {

View File

@@ -27,12 +27,12 @@
"enGB": "Inglese (Regno Unito)",
"da": "Danese",
"ca": "Catalano",
"zhTW": "Cinese (Taiwan)",
"nl": "Olandese",
"hu": "Ungaro",
"hr": "Croato",
"frCA": "Francese (Canada)",
"fi": "Finlandese",
"et": "Etiope",
"esUS": "Spagnolo (USA)"
"zhTW": "",
"nl": "",
"hu": "",
"hr": "",
"frCA": "",
"fi": "",
"et": "",
"esUS": ""
}

View File

@@ -1,48 +1,27 @@
{
"en": "英語",
"af": "アフリカーンス語",
"ar": "アラビア語",
"az": "アゼルバイジャン語",
"bg": "ブルガリア語",
"ca": "カタルーニャ語",
"cs": "チェコ語",
"da": "デンマーク語",
"de": "ドイツ語",
"el": "ギリシア語",
"enGB": "英語 (英国)",
"eo": "エスペラント語",
"es": "スペイン語",
"esUS": "スペイン語 (ラテンアメリカ)",
"et": "エストニア語",
"eu": "バスク語",
"fi": "フィンランド語",
"fr": "フランス語",
"frCA": "フランス語 (カナダ)",
"he": "ヘブライ語",
"mr": "マラーティー語",
"hr": "クロアチア語",
"hu": "ハンガリー語",
"hy": "アルメニア語",
"id": "インドネシア語",
"it": "イタリア語",
"ja": "日本語",
"kab": "カビル語",
"ko": "韓国語",
"lt": "リトアニア語",
"nl": "オランダ語",
"nb": "ノルウェー語 (ブークモール)",
"oc": "オック語",
"pl": "ポーランド語",
"ptBR": "ポルトガル語 (ブラジル)",
"ru": "ロシア語",
"ro": "ルーマニア語",
"sc": "サルデーニャ語",
"sk": "スロバキア語",
"sl": "スロベニア語",
"sr": "セルビア語",
"sv": "スウェーデン語",
"th": "タイ語",
"tr": "トルコ語",
"uk": "ウクライナ語",
"vi": "ベトナム語",
"zhCN": "中国語 (中国)",
"zhTW": "中国語 (台湾)"
}
"zhCN": "中国語 (中国)"
}

View File

@@ -1,50 +1,48 @@
{
"en": "Anglés",
"af": "Afrikaans",
"ar": "Arabi",
"bg": "Bulgar",
"ca": "Catalan",
"cs": "Chèc",
"da": "Danés",
"de": "Aleman",
"el": "Grèc",
"enGB": "Anglés (Reialme Unit)",
"eo": "Esperanto",
"es": "Castelhan",
"esUS": "Espanhòl (America latina)",
"et": "Estonian",
"eu": "Basc",
"fi": "Finés",
"fr": "Francés",
"frCA": "Francés (Canadian)",
"he": "Ebrèu",
"mr":"Marathi",
"hr": "Croat",
"hu": "Ongrés",
"hy": "Armenian",
"id": "Indonesian",
"it": "Italian",
"ja": "Japonés",
"kab": "Cabil",
"ko": "Corean",
"lt": "Lituanian",
"nl": "Neerlandés",
"oc": "Occitan",
"pl": "Polonés",
"ptBR": "Portugués (Brasil)",
"ru": "Rus",
"ro": "Romanian",
"sc": "Sarde",
"sk": "Eslovac",
"sl": "Eslovèn",
"sr": "Sèrbe",
"sv": "Suedés",
"th": "Tai",
"tr": "Turc",
"uk": "Ucraïnian",
"vi": "Vietnamian",
"zhCN": "Chinés (China)",
"zhTW": "Chinés (Taiwan)"
"zhTW": "Chinés (Taiwan)",
"et": "Estonian",
"da": "Danés",
"uk": "Ucraïnian",
"th": "Tai",
"sk": "Eslovac",
"sc": "Sarde",
"lt": "Lituanian",
"id": "Indonesian",
"he": "Ebrèu",
"eu": "Basc",
"mr": "Marathi",
"sl": "Eslovèn",
"ro": "Romanian",
"ar": "Arabi"
}

View File

@@ -99,11 +99,9 @@
},
"connectionindicator": {
"address": "Adresse:",
"audio_ssrc": "Audio-SSRC:",
"bandwidth": "Geschätzte Bandbreite:",
"bitrate": "Bitrate:",
"bridgeCount": "Serverzahl: ",
"codecs": "Codecs (A/V): ",
"connectedTo": "Verbunden mit:",
"e2e_rtt": "E2E RTT:",
"framerate": "Bildwiederholrate:",
@@ -127,12 +125,9 @@
"remoteport": "Entfernter Port:",
"remoteport_plural": "Entfernte Ports:",
"resolution": "Auflösung:",
"savelogs": "Logs speichern",
"participant_id": "Teilnehmer-ID:",
"status": "Verbindung:",
"transport": "Protokoll:",
"transport_plural": "Protokolle:",
"video_ssrc": "Video-SSRC:"
"transport_plural": "Protokolle:"
},
"dateUtils": {
"earlier": "Früher",
@@ -201,7 +196,10 @@
"displayNameRequired": "Hallo! Wie ist Ihr Name?",
"done": "Fertig",
"e2eeDescription": "Ende-zu-Ende-Verschlüsselung ist derzeit noch EXPERIMENTELL. Bitte beachten Sie, dass das Aktivieren der Ende-zu-Ende-Verschlüsselung diverse serverseitige Funktionen deaktiviert: Aufnahmen, Livestreaming und Telefoneinwahl. Bitte beachten Sie außerdem, dass der Konferenz dann nur noch mit Browsern beigetreten werden kann, die Insertable Streams unterstützen.",
"e2eeLabel": "Ende-zu-Ende-Verschlüsselung aktivieren",
"e2eeLabel": "E2EE-Schlüssel",
"e2eeNoKey": "Keiner",
"e2eeToggleSet": "Schlüssel festlegen",
"e2eeSet": "Setzen",
"e2eeWarning": "WARNUNG: Nicht alle Teilnehmer dieser Konferenz scheinen Ende-zu-Ende-Verschlüsselung zu unterstützen. Wenn Sie diese aktivieren, können die entsprechenden Teilnehmer nichts mehr sehen oder hören.",
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
"error": "Fehler",
@@ -366,7 +364,7 @@
"password": "$t(lockRoomPasswordUppercase):",
"title": "Teilen",
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
"label": "Einwahlinformationen"
"label": "Konferenzinformationen"
},
"inviteDialog": {
"alertText": "Die Einladung einiger Teilnehmer ist fehlgeschlagen.",
@@ -505,7 +503,6 @@
"poweredby": "Betrieben von",
"prejoin": {
"audioAndVideoError": "Audio- und Videofehler:",
"audioDeviceProblem": "Es gibt ein Problem mit Ihrem Audiogerät.",
"audioOnlyError": "Audiofehler:",
"audioTrackError": "Audiotrack konnte nicht erstellt werden.",
"calling": "Rufaufbau",
@@ -513,35 +510,15 @@
"callMeAtNumber": "Mich unter dieser Nummer anrufen:",
"configuringDevices": "Geräte werden eingerichtet …",
"connectedWithAudioQ": "Sie sind mit Audio verbunden?",
"connection": {
"good": "Ihre Internetverbindung sieht gut aus!",
"nonOptimal": "Ihre Internetverbindung ist nicht optimal.",
"poor": "Sie haben eine schlechte Internetverbindung."
},
"connectionDetails": {
"audioClipping": "Ihr Ton wird wahrscheinlich abgehackt sein.",
"audioHighQuality": "Ihr Ton sollte exzellent klingen.",
"audioLowNoVideo": "Ihr Ton wird wahrscheinlich schlecht klingen und es wird kein Video geben.",
"goodQuality": "Großartig! Ihre Bild- und Tonqualität sollte super sein.",
"noMediaConnectivity": "Es konnte für diesen Test keine Medienverbindung hergestellt werden. Das wird gewöhnlich durch eine Firewall oder ein NAT ausgelöst.",
"noVideo": "Ihr Bild wird wahrscheinlich eine schlechte Qualität haben.",
"undetectable": "Wenn Sie mit Ihrem Browser weiterhin Probleme in Konferenzen haben, sollten Sie die Verbindung und Funktion Ihrer Lautsprecher, Ihres Mikrofons und Ihrer Kamera überprüfen. Stellen Sie außerdem sicher, dass Ihr Browser die erforderlichen Rechte hat, auf das Mikrofon und die Kamera zuzugreifen, und dass Sie die neuste Browserversion installiert haben. Sollten Sie immer noch Probleme haben, kontaktieren Sie bitte den Entwickler der Webanwendung.",
"veryPoorConnection": "Ihre Konferenzqualität wird wahrscheinlich sehr schlecht sein.",
"videoFreezing": "Ihr Bild wird wahrscheinlich einfrieren, schwarz werden und eine geringe Auflösung haben.",
"videoHighQuality": "Ihr Bild sollte sehr gut aussehen.",
"videoLowQuality": "Ihr Bild wird wahrscheinlich eine geringe Auflösung und Bildrate haben.",
"videoTearing": "Ihr Bild wird wahrscheinlich eine geringe Auflösung haben oder Artefakte aufweisen."
},
"copyAndShare": "Konferenzlink kopieren & teilen",
"dialInMeeting": "Telefoneinwahl",
"dialInPin": "In die Konferenz einwählen und PIN eingeben:",
"dialing": "Wählen",
"doNotShow": "Diesen Bildschirm nicht mehr anzeigen",
"doNotShow": "Nicht mehr anzeigen",
"errorDialOut": "Anruf fehlgeschlagen",
"errorDialOutDisconnected": "Anruf fehlgeschlagen. Verbindungsabbruch",
"errorDialOutFailed": "Anruf fehlgeschlagen. Anruf fehlgeschlagen",
"errorDialOutStatus": "Fehler beim Abrufen des Anrufstatus",
"errorMissingName": "Bitte geben Sie Ihren Namen ein, um der Konferenz beizutreten.",
"errorStatusCode": "Anruf fehlgeschlagen. Statuscode: {{status}}",
"errorValidation": "Nummerverifikation fehlgeschlagen",
"iWantToDialIn": "Ich möchte mich einwählen",
@@ -698,6 +675,7 @@
"document": "Geteiltes Dokument schließen",
"download": "Unsere Apps herunterladen",
"embedMeeting": "Konferenz einbetten",
"e2ee": "Ende-zu-Ende-Verschlüsselung",
"feedback": "Feedback hinterlassen",
"fullScreen": "Vollbildmodus ein-/ausschalten",
"grantModerator": "Zum Moderator machen",
@@ -876,12 +854,12 @@
"getHelp": "Hilfe",
"go": "Los",
"goSmall": "Los",
"info": "Einwahlinformationen",
"info": "Informationen",
"join": "ERSTELLEN / BEITRETEN",
"moderatedMessage": "Oder <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservieren Sie sich eine Konferenz-URL</a>, unter der Sie der einzige Moderator sind.",
"privacy": "Datenschutz",
"recentList": "Verlauf",
"recentListDelete": "Eintrag löschen",
"recentListDelete": "Löschen",
"recentListEmpty": "Ihr Konferenzverlauf ist derzeit leer. Reden Sie mit Ihrem Team und Ihre vergangenen Konferenzen landen hier.",
"reducedUIText": "Willkommen bei {{app}}!",
"roomNameAllowedChars": "Der Konferenzname sollte keines der folgenden Zeichen enthalten: ?, &, :, ', \", %, #.",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -730,7 +730,7 @@
"stopScreenSharing": "Arrêter le partage d'écran",
"stopSubtitles": "Désactiver les sous-titres",
"stopSharedVideo": "Arrêter la vidéo YouTube",
"talkWhileMutedPopup": "Vous voulez parler ? Votre micro est coupé.",
"talkWhileMutedPopup": "Vous voulez parler ? Vôtre micro est coupé.",
"tileViewToggle": "Activer/désactiver la vue mosaïque",
"toggleCamera": "Changer de caméra",
"videomute": "Démarrer / Arrêter la caméra",
@@ -769,7 +769,7 @@
"errorAlreadyInvited": "{{displayName}} est déjà invité(e)",
"errorInvite": "La conférence n'est pas encore établie. Veuillez réessayer plus tard.",
"errorInviteFailed": "Nous tentons de résoudre le problème. Veuillez réessayer plus tard.",
"errorInviteFailedTitle": "L'invitation de {{displayName}} a échoué",
"errorInviteFailedTitle": "l'invitation de {{displayName}} a échoué",
"errorInviteTitle": "Erreur lors de l'invitation",
"pending": "{{displayName}} a été invité(e)"
},
@@ -809,7 +809,7 @@
"join": "Touchez pour rejoindre",
"roomname": "Saisissez un nom de salle"
},
"appDescription": "Foncez tchater en vidéo avec toute le monde. En fait, vous pouvez inviter tout ceux que vous connaissez. {{app}} est une solution de visioconférence entièrement chiffrée et 100% libre que vous pouvez utiliser en permanence, chaque jour, et sans aucun compte requis.",
"appDescription": "Foncez tchater en vidéo avec toute le monde. En fait, vous pouvez inviter tout ceux que vous connaissez. {{app}} est une solution de visioconférence entièrement chiffrée et 100% libre que vous pouvez utiliser en permanence, chaque jours, et sans aucun compte requis.",
"audioVideoSwitch": {
"audio": "Voix",
"video": "Vidéo"
@@ -847,10 +847,10 @@
"allow": "Autoriser",
"backToKnockModeButton": "Aucun mot de passe, demander à rejoindre plutôt",
"dialogTitle": "Mode lobby",
"disableDialogContent": "Le mode lobby est actuellement activé. Cette fonctionnalité garantit que les participants indésirables ne peuvent pas rejoindre votre réunion. Souhaitez-vous la désactiver ?",
"disableDialogContent": "Le mode lobby est actuellement activé. Cette fonctionnalité garantit que les participants indésirables ne peuvent pas rejoindre votre réunion. Souhaitez-vous la désactiver?",
"disableDialogSubmit": "Désactiver",
"emailField": "Saisissez votre adresse email",
"enableDialogPasswordField": "Définir le mot de passe (optionnel)",
"enableDialogPasswordField": "Définir le mot de passe (optionel)",
"enableDialogSubmit": "Activer",
"enableDialogText": "Le mode lobby vous permet de protéger votre réunion en autorisant les personnes à entrer qu'après l'approbation formelle d'un modérateur.",
"enterPasswordButton": "Saisissez un mot de passe de réunion",

View File

@@ -21,8 +21,7 @@
"bluetooth": "Bluetooth",
"headphones": "Cuffie",
"phone": "Telefono",
"speaker": "Vivavoce",
"none": ""
"speaker": "Vivavoce"
},
"audioOnly": {
"audioOnly": "Solo audio"
@@ -52,12 +51,7 @@
"popover": "Scegli un nickname",
"title": "Inserire un nickname per utilizzare la chat"
},
"title": "Chat",
"you": "",
"privateNotice": "",
"noMessagesMessage": "",
"messageTo": "",
"fieldPlaceHolder": ""
"title": "Chat"
},
"connectingOverlay": {
"joiningRoom": "Collegamento al tuo meeting in corso…"
@@ -72,11 +66,7 @@
"DISCONNECTED": "Disconnesso",
"DISCONNECTING": "Disconnessione in corso",
"ERROR": "Errore",
"RECONNECTING": "Si è verificato un problema di rete. Riconnessione...",
"LOW_BANDWIDTH": "",
"GOT_SESSION_ID": "",
"GET_SESSION_ID_ERROR": "",
"FETCH_SESSION_ID": ""
"RECONNECTING": "Si è verificato un problema di rete. Riconnessione..."
},
"connectionindicator": {
"address": "Indirizzo:",
@@ -106,9 +96,7 @@
"resolution": "Risoluzione:",
"status": "Connessione:",
"transport": "Trasporto:",
"transport_plural": "Trasporti:",
"turn": "(ruota)",
"e2e_rtt": ""
"turn": "(ruota)"
},
"dateUtils": {
"earlier": "Prima",
@@ -120,10 +108,8 @@
"description": "Non è successo nulla? Abbiamo provato ad avviare la tua videoconferenza sull'app desktop di {{app}}. Prova di nuovo o avviala nell'app web di {{app}}.",
"descriptionWithoutWeb": "",
"downloadApp": "Scarica l'app",
"ifDoNotHaveApp": "Se non hai ancora l'app:",
"ifHaveApp": "Se hai già l'app:",
"joinInApp": "Entra in riunione usando l'app",
"launchWebButton": "Avvia sul web",
"openApp": "Prosegui verso l'app",
"title": "Sto avviando la tua videoconferenza su {{app}}…",
"tryAgainButton": "Prova di nuovo sul desktop"
},
@@ -191,7 +177,7 @@
"kickParticipantDialog": "Sei sicuro di voler espellere questo partecipante?",
"kickParticipantTitle": "Espellere questi partecipante?",
"kickTitle": "Espulso dal meeting",
"liveStreaming": "Diretta",
"liveStreaming": "Live Streaming",
"liveStreamingDisabledForGuestTooltip": "Gli ospiti non possono avviare una diretta.",
"liveStreamingDisabledTooltip": "Trasmissioni in diretta disabilitate.",
"lockMessage": "Impossibile bloccare la conferenza.",
@@ -255,7 +241,7 @@
"stopLiveStreaming": "Ferma la diretta",
"stopRecording": "Ferma registrazione",
"stopRecordingWarning": "Sei sicuro di voler interrompere la registrazione?",
"stopStreamingWarning": "Sei sicuro di voler interrompere la diretta?",
"stopStreamingWarning": "Sei sicuro di voler interrompere il live streaming?",
"streamKey": "Chiave per trasmissione in diretta",
"Submit": "Invia",
"thankYou": "Grazie per aver usato {{appName}}!",
@@ -269,18 +255,7 @@
"WaitForHostMsgWOk": "La conferenza <b>{{room}}</b> non è ancora cominciata. Se sei l'organizzatore, allora premi OK per autenticarti. Altrimenti, aspetta l'arrivo dell'organizzatore.",
"WaitingForHost": "In attesa dell'organizzatore ...",
"Yes": "Sì",
"yourEntireScreen": "Schermo intero",
"sendPrivateMessageTitle": "",
"sendPrivateMessageOk": "",
"sendPrivateMessageCancel": "",
"sendPrivateMessage": "",
"screenSharingAudio": "",
"muteEveryoneStartMuted": "",
"muteEveryoneSelf": "",
"muteEveryoneTitle": "",
"muteEveryoneDialog": "",
"muteEveryoneElseTitle": "",
"muteEveryoneElseDialog": ""
"yourEntireScreen": "Schermo intero"
},
"dialOut": {
"statusMessage": "è ora {{status}}"
@@ -359,28 +334,27 @@
"toggleFilmstrip": "Mostra o nascondi anteprime video",
"toggleScreensharing": "Cambia modalità tra videocamera e condivisione schermo",
"toggleShortcuts": "Mostra o nascondi le scorciatoie",
"videoMute": "Accendo o spegni la videocamera",
"videoQuality": ""
"videoMute": "Accendo o spegni la videocamera"
},
"liveStreaming": {
"busy": "Stiamo cercando di liberare risorse per la diretta. Riprova tra qualche minuto.",
"busy": "Stiamo cercando di liberare risorse per lo streaming. Riprova tra qualche minuto.",
"busyTitle": "Tutti gli streamer sono impegnati al momento",
"changeSignIn": "Cambia account",
"choose": "Scegli una trasmissione in diretta",
"chooseCTA": "Scegli un'opzione di trasmissione. Attualmente sei loggato come {{email}}.",
"enterStreamKey": "Inserisci qui la tua chiave YouTube per le trasmissioni in diretta.",
"error": "Diretta fallita. Prova di nuovo.",
"error": "Live streaming fallito. Prova di nuovo.",
"errorAPI": "Si è verificato un errore durante l'accesso ai tuoi broadcast YouTube. Prova a effettuare nuovamente il login.",
"errorLiveStreamNotEnabled": "La diretta non è attivata su {{email}}. Per favore abilita la diretta o effettua l'accesso con un account abilitato alle dirette.",
"expandedOff": "La diretta è stata interrotta",
"expandedOn": "La conferenza è attualmente in diretta su YouTube.",
"expandedPending": "La diretta è in fase di avvio...",
"expandedOff": "La diretta &egrave; stata interrotta",
"expandedOn": "La conferenza &egrave; attualmente in diretta su YouTube.",
"expandedPending": "La diretta &egrave; in fase di avvio...",
"failedToStart": "Avvio trasmissione in diretta fallito",
"getStreamKeyManually": "Non siamo stati in grado di trovare nessuna trasmissione dal vivo. Prova ad ottenere una chiave stream da Youtube",
"invalidStreamKey": "La chiave stream potrebbe non essere corretta.",
"off": "La diretta si è interrotta",
"on": "Trasmissione in diretta",
"pending": "Avvio diretta...",
"pending": "Avvio live stream...",
"serviceName": "Servizio live streaming",
"signedInAs": "Sei attualmente collegato come:",
"signIn": "Registrati con Google",
@@ -388,11 +362,7 @@
"signOut": "Esci",
"start": "Inizia una diretta",
"streamIdHelp": "Cos'è questo?",
"unavailableTitle": "La diretta non è disponibile",
"onBy": "",
"offBy": "",
"googlePrivacyPolicy": "Google Privacy Policy",
"youtubeTerms": "YouTube terms of services"
"unavailableTitle": "Live streaming non disponibile"
},
"localRecording": {
"clientState": {
@@ -410,8 +380,8 @@
"me": "io",
"messages": {
"engaged": "Registrazione locale avviata.",
"finished": "La registrazione della sessione {{token}} è terminata. Invia il file della registrazione al moderatore.",
"finishedModerator": "La registrazione della sessione {{token}} è terminata. Il file della traccia local è stato salvato. Richiedere ai partecipanti di inviare le loro registrazioni.",
"finished": "La registrazione della sessione {{token}} &egrave; terminata. Invia il file della registrazione al moderatore.",
"finishedModerator": "La registrazione della sessione {{token}} &egrave; terminata. Il file della traccia local &egrave; stato salvato. Richiedere ai partecipanti di inviare le loro registrazioni.",
"notModerator": "Non sei un moderatore. Non puoi avviare o interrompere la registrazione"
},
"moderator": "Moderatore",
@@ -455,8 +425,7 @@
"unmute": "",
"newDeviceCameraTitle": "Trovata nuova videocamera",
"newDeviceAudioTitle": "Trovata nuova origine audio",
"newDeviceAction": "Usala",
"suboptimalBrowserWarning": "Ci spiace che la tua videoconferenza non sarà ottimale, qui. Stiamo cercando modi per migliorare, ma fino ad allora per favore prova ad usare <a href='{{recommendedBrowserPageLink}}' target='_blank'>browser supportati completamente/a>."
"newDeviceAction": "Usala"
},
"passwordSetRemotely": "definita da altro utente",
"passwordDigitsOnly": "Fino a {{number}} cifre",
@@ -504,9 +473,7 @@
"signIn": "Entra",
"signOut": "Esci",
"unavailable": "Ops! Il {{serviceName}} non è al momento disponibile. Stiamo lavorando per risolvere il problema. Riprova più tardi.",
"unavailableTitle": "Registrazione non disponibile",
"onBy": "{{name}} registrazione iniziata",
"offBy": "{{name}} registrazione fermata"
"unavailableTitle": "Registrazione non disponibile"
},
"sectionList": {
"pullToRefresh": "Trascina per aggiornare"
@@ -532,9 +499,7 @@
"selectMic": "Microfono",
"startAudioMuted": "Tutti cominciano con il microfono disattivato",
"startVideoMuted": "Tutti cominciano con il video disattivato",
"title": "Impostazioni",
"speakers": "Altoparlanti",
"microphones": "Microfoni"
"title": "Impostazioni"
},
"settingsView": {
"alertOk": "OK",
@@ -549,11 +514,7 @@
"serverURL": "URL del server",
"startWithAudioMuted": "Inizia con l'audio disattivato",
"startWithVideoMuted": "Avvia con il video disattivato",
"version": "Versione",
"showAdvanced": "Mostra impostazioni avanzate",
"disableP2P": "Disabilita modalità uno-a-uno",
"disableCallIntegration": "Disabilita integrazione nativa delle chiamate",
"advanced": "Avanzate"
"version": "Versione"
},
"share": {
"dialInfoText": "\n\n=====\n\nVuoi solo ascoltare la conferenza da un telefono?\n\n{{defaultDialInNumber}}Clicca questo link per vedere i numeri telefonici di questo meeting\n{{dialInfoPageUrl}}",
@@ -610,12 +571,7 @@
"tileView": "Vedi tutti i partecipanti insieme, o uno solo",
"toggleCamera": "Cambia videocamera",
"videomute": "Attiva/disattiva videocamera",
"videoblur": "Attiva/disattiva offuscamento video",
"privateMessage": "Invia messaggio privato",
"muteEveryone": "Zittisci tutti",
"moreOptions": "Mostra più opzioni",
"help": "Aiuto",
"download": "Scarica altre app"
"videoblur": "Attiva/disattiva offuscamento video"
},
"addPeople": "Aggiungi persone alla chiamata",
"audioOnlyOff": "Anche video",
@@ -638,6 +594,7 @@
"logout": "Logout",
"lowerYourHand": "Abbassa la mano",
"moreActions": "Più azioni",
"moreOptions": "Più opzioni",
"mute": "Microfono Attiva / Disattiva",
"openChat": "Apri una chat",
"pip": "Abilita visualizzazione immagine nellimmagine",
@@ -741,7 +698,7 @@
"privacy": "Privacy",
"recentList": "Recente",
"recentListDelete": "Cancella",
"recentListEmpty": "La tua lista è vuota. Chatta con qualcuno del tuo team e lo vedrai apparire nella lista di meeting recenti.",
"recentListEmpty": "La tua lista &egrave; vuota. Chatta con qualcuno del tuo team e lo vedrai apparire nella lista di meeting recenti.",
"reducedUIText": "",
"roomname": "Inserisci Nome Stanza",
"roomnameHint": "Inserisci il nome o l'URL della stanza alla quale vuoi accedere. Puoi anche inventarti un nome, assicurati solo che le persone che vuoi contattare lo sappiano, così che possano inserire lo stesso nome.",
@@ -749,18 +706,8 @@
"terms": "Termini di utilizzo",
"title": "Il sistema di conferenza sicuro, funzionale e completamente gratuito."
},
"documentSharing": {
"title": ""
},
"defaultNickname": "",
"chromeExtensionBanner": {
"dontShowAgain": "",
"buttonText": "",
"installExtensionText": ""
},
"raisedHand": "Vorrebbe parlare",
"lonelyMeetingExperience": {
"button": "Invita altri",
"button": "Invita gli altri",
"youAreAlone": "Sei l'unico in riunione"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -122,13 +122,7 @@
"status": "Connexion :",
"transport": "Transpòrt :",
"transport_plural": "Transpòrts :",
"e2e_rtt": "E2E RTT:",
"codecs": "Codecs (A/V): ",
"video_ssrc": "Vidèo SSRC:",
"maxEnabledResolution": "enviar max",
"savelogs": "Enregistrar jornals",
"participant_id": "Id participant:",
"audio_ssrc": "Àudio SSRC:"
"e2e_rtt": "E2E RTT:"
},
"dateUtils": {
"earlier": "Mai dora",
@@ -301,20 +295,7 @@
"muteEveryoneSelf": "vos",
"muteEveryoneTitle": "Rendre mut tot lo monde?",
"muteEveryoneDialog": "Volètz vertadièrament copar lo son a tot lo monde? Poiretz pas lo restablir, mas eles poiràn o far quora que vòlgan.",
"muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}}?",
"add": "Ajustar",
"copied": "Copiat",
"grantModeratorDialog": "Volètz vertadièrament far venir aqueste participant moderator?",
"readMore": "mai",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Impossible pendent una difusion activa",
"screenSharingFailedTitle": "Fracàs del partiment d'ecran!",
"e2eeLabel": "Activar lo chiframant del cap a la fin",
"grantModeratorTitle": "Passar moderator",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Impossible pendent un enregistrament actiu",
"e2eeDescription": "Lo chiframent del cap a la fin es actualament EXPERIMENTALA. Mercés de gardar a l'esperit qu'activar lo chiframent del cap a la fin desactivarà en efièch los servicis costat servidor coma: l'enregistrament, la difusion en dirècte e las participacions telefonicas. Remembratz tanben que la conferéncia foncionarà pas que per lo monde que participan amb un navigador compatible amb los fluxes inseribles.",
"screenSharingFailed": "Ops! Quicòm a trucat, avèm pas pogut començar lo partiment d'ecran!",
"e2eeWarning": "AVERTIMENT: pas totes los participants d'aquesta conferéncia semblan poder suportar lo chiframent del cap a la fin. Se l'activatz poiràn pas vos veire nimai vos entendre.",
"muteEveryoneElseDialog": "Un còp mut, poiretz pas mai lo tornar la paraula, mas la se pòdon tornar quora vòlgan."
"muteEveryoneElseTitle": "Copar lo son a totes levat {{whom}}?"
},
"dialOut": {
"statusMessage": "ara es {{status}}"
@@ -429,9 +410,7 @@
"streamIdHelp": "Ques aquò ?",
"unavailableTitle": "Difusion en dirècte indisponibla",
"googlePrivacyPolicy": "Politica de confidencialitat de Google",
"youtubeTerms": "Condicions dutilizacion de YouTube",
"limitNotificationDescriptionWeb": "A causa d'una brava demanda vòstra difusion serà limitada a {{limit}} min. Per de difusion illimitada ensajatz <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "Vòstra difusion serà limitada a {{limit}} min. Per de difusions illimitada ensajatz {{app}}."
"youtubeTerms": "Condicions dutilizacion de YouTube"
},
"localRecording": {
"clientState": {
@@ -663,10 +642,7 @@
"muteEveryone": "Rendre mut tot lo monde",
"moreOptions": "Mostrar mai dopcions",
"e2ee": "Chiframent del cap a la fin",
"security": "Opcions de seguretat",
"embedMeeting": "Conferéncia integrada",
"grantModerator": "Passar moderator",
"lobbyButton": "Activar/Desactivar mòde sala d'espèra"
"security": "Opcions de seguretat"
},
"addPeople": "Ajustar de monde a vòstra sonada",
"audioOnlyOff": "Desactivar lo mòde connexion febla",
@@ -717,17 +693,14 @@
"videomute": "Aviar / Arrestar la camèra",
"startvideoblur": "Trebolar mon rèire-plan",
"stopvideoblur": "Desactivar lo borrolatge del rèire-plan",
"noisyAudioInputDesc": "Sembla que vòstre microfòn mene bruch, pensatz de lo copar o de lo cambiar.",
"noisyAudioInputDesc": "",
"noisyAudioInputTitle": "Vòstre microfòn sembla brusent !",
"noAudioSignalDialInLinkDesc": "",
"noAudioSignalDialInDesc": "",
"muteEveryone": "Rendre mut tot lo monde",
"moreOptions": "Autras opcions",
"e2ee": "Chiframent del cap a la fin",
"security": "Opcions de seguretat",
"embedMeeting": "Integrar conferéncia",
"lobbyButtonDisable": "Desactivar lo mòde sala d'espèra",
"lobbyButtonEnable": "Activar mòde sala d'espèra"
"security": "Opcions de seguretat"
},
"transcribing": {
"ccButtonTooltip": "Aviar / Arrestat los sostítols",
@@ -822,12 +795,7 @@
"sendFeedback": "Mandar vòstra opinion",
"terms": "Tèrmes",
"title": "Conferéncias vidèo securizadas amb plen de foncionalitats e complètament gratuitas",
"getHelp": "Obténer dajuda",
"startMeeting": "Començar la reünion",
"jitsiOnMobile": "Jitsi sus mobil telecargatz nòstra aplicacion e començatz de conferéncias de pertot",
"moderatedMessage": "O <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservatz una URL de conferéncia</a> a l'avança ont sètz l'unic moderator.",
"jitsiMeet": "Jitsi Meet",
"secureMeetings": "Conferéncias seguras e de nauta qualitat"
"getHelp": "Obténer dajuda"
},
"helpView": {
"header": "Centre dajuda"
@@ -864,27 +832,7 @@
"initiated": "Sonada aviada",
"joinWithoutAudio": "Rejónher sens àudio",
"joinMeeting": "Rejónher la conferéncia",
"joinAudioByPhone": "Rejónher amb làudio del telefòn",
"audioDeviceProblem": "I a un problèma amb vòstre periferic àudio",
"showScreen": "Activar l'ecran de preconferéncia",
"connection": {
"good": "Vòstra connexion Internet sembla bona!",
"nonOptimal": "Vòstra connexion Internet es pas optimala",
"poor": "Vòstra connexion Internet es febla"
},
"connectionDetails": {
"videoHighQuality": "Nos esperam a trobar vòstra qualitat vidèo de bona qualitat.",
"audioClipping": "Nos esperam a trobar vòstre àudio troncat.",
"audioHighQuality": "Nos esperam a trobar vòstra qualitat àudio excellenta.",
"audioLowNoVideo": "Nos esperam a trobar vòstra qualitat àudio febla e cap de vidèo.",
"goodQuality": "Crane! Vòstra qualitat serà geniala.",
"noMediaConnectivity": "Avèm pas trobat cap de biais d'establir una connectivitat mèdia per aquesta pròva. Sovent es a causat d'un parafòc o un NAT.",
"noVideo": "Nos esperam a trobat vòstra qualitat vidèo òrra.",
"veryPoorConnection": "Nos esperam a trobar vòstra qualitat vidèo plan òrra.",
"videoFreezing": "Nos esperam a veire vòstra vidèo se gelar, venir negra e se pixelizar."
},
"premeeting": "Preconferéncia",
"errorMissingName": "Mercés de picar vòstre nom per rejónher la conferéncia"
"joinAudioByPhone": "Rejónher amb làudio del telefòn"
},
"lobby": {
"reject": "Regetar",
@@ -907,15 +855,7 @@
"emailField": "Picata vòstra adreça electronica",
"disableDialogSubmit": "Desactivar",
"backToKnockModeButton": "Cap de senhal, demandar a participar a la plaça",
"allow": "Autorizar",
"knockingParticipantList": "Lista de participants en espèra",
"dialogTitle": "Mòde sala d'espèra",
"notificationLobbyDisabled": "Lo mòde sala d'espèra es estat desactivat per {{originParticipantName}}",
"notificationLobbyEnabled": "Lo mòde sala d'espèra activat per {{originParticipantName}}",
"notificationTitle": "Sala d'espèra",
"toggleLabel": "Activar la sala d'espèra",
"notificationLobbyAccessDenied": "{{originParticipantName}} a decidit de regetar la demanda de {{targetParticipantName}}",
"notificationLobbyAccessGranted": "{{originParticipantName}} a autorizat {{targetParticipantName}} a dintrar"
"allow": "Autorizar"
},
"security": {
"securityOptions": "Opcions de seguretat",
@@ -925,8 +865,7 @@
},
"e2ee": {
"labelToolTip": "La comunicacion àudio e vidèo daquesta sonada es chifrada del cap a la fin"
},
"embedMeeting": {
"title": "Integrar aquesta conferéncia"
}
}

View File

@@ -512,12 +512,6 @@
"sectionList": {
"pullToRefresh": "Puxe para atualizar"
},
"security": {
"about": "Voce pode adicionar uma $t(lockRoomPassword) em sua reunião. Participantes irão precisar informar a $t(lockRoomPassword) antes de se juntarem na reunião.",
"aboutReadOnly": "Moderadores podem adicionar uma $t(lockRoomPassword) na reunião. Participantes irão precisar informar a $t(lockRoomPassword) antes de se juntarem na reunião",
"insecureRoomNameWarning": "Essa sala não está protegida. Participantes indesejados poderão entrar na sua reunião. Considere configurar a segurança da sua reunião utilizando o botão de segurança.",
"securityOptions": "Opções de segurança"
},
"settings": {
"calendar": {
"about": "A integração do calendário {{appName}} é usada para acessar com segurança o seu calendário para que ele possa ler os próximos eventos.",

View File

@@ -149,10 +149,7 @@
"launchWebButton": "Запустить в браузере",
"openApp": "Перейти к приложению",
"title": "Запуск вашей встречи в {{app}}...",
"tryAgainButton": "Повторите в настольном приложении",
"joinInApp": "Присоединиться к этой встрече с помощью приложения",
"ifHaveApp": "Если у Вас уже есть приложение:",
"ifDoNotHaveApp": "Если у Вас ещё нет приложения:"
"tryAgainButton": "Повторите в настольном приложении"
},
"defaultLink": "напр. {{url}}",
"defaultNickname": "напр. Яна Цветкова",

View File

@@ -220,6 +220,7 @@
"kickTitle": "Ouch! {{participantDisplayName}} kicked you out of the meeting",
"liveStreaming": "Live Streaming",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Not possible while recording is active",
"liveStreamingDisabledForGuestTooltip": "Guests can't start live streaming.",
"liveStreamingDisabledTooltip": "Start live stream disabled.",
"lockMessage": "Failed to lock the conference.",
"lockRoom": "Add meeting $t(lockRoomPasswordUppercase)",
@@ -254,6 +255,7 @@
"readMore": "more",
"recording": "Recording",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Not possible while a live stream is active",
"recordingDisabledForGuestTooltip": "Guests can't start recordings.",
"recordingDisabledTooltip": "Start recording disabled.",
"rejoinNow": "Rejoin now",
"remoteControlAllowedMessage": "{{user}} accepted your remote control request!",
@@ -285,6 +287,7 @@
"shareVideoTitle": "Share a video",
"shareYourScreen": "Share your screen",
"shareYourScreenDisabled": "Screen sharing disabled.",
"shareYourScreenDisabledForGuest": "Guests can't screen share.",
"startLiveStreaming": "Start live stream",
"startRecording": "Start recording",
"startRemoteControlErrorMessage": "An error occurred while trying to start the remote control session!",
@@ -363,7 +366,7 @@
"password": "$t(lockRoomPasswordUppercase):",
"title": "Share",
"tooltip": "Share link and dial-in info for this meeting",
"label": "Dial-in info"
"label": "Meeting info"
},
"inviteDialog": {
"alertText": "Failed to invite some participants.",
@@ -533,7 +536,7 @@
"dialInMeeting": "Dial into the meeting",
"dialInPin": "Dial into the meeting and enter PIN code:",
"dialing": "Dialing",
"doNotShow": "Don't show this screen again",
"doNotShow": "Don't show this again",
"errorDialOut": "Could not dial out",
"errorDialOutDisconnected": "Could not dial out. Disconnected",
"errorDialOutFailed": "Could not dial out. Call failed",
@@ -678,7 +681,6 @@
},
"startupoverlay": {
"policyText": " ",
"genericTitle": "The meeting needs to use your microphone and camera.",
"title": "{{app}} needs to use your microphone and camera."
},
"suspendedoverlay": {
@@ -838,6 +840,8 @@
"ld": "LD",
"ldTooltip": "Viewing low definition video",
"lowDefinition": "Low definition",
"onlyAudioAvailable": "Only audio is available",
"onlyAudioSupported": "We only support audio in this browser.",
"sd": "SD",
"sdTooltip": "Viewing standard definition video",
"standardDefinition": "Standard definition"
@@ -872,22 +876,18 @@
"getHelp": "Get help",
"go": "GO",
"goSmall": "GO",
"headerTitle": "Jitsi Meet",
"headerSubtitle": "Secure and high quality meetings",
"info": "Dial-in info",
"info": "Info",
"join": "CREATE / JOIN",
"jitsiOnMobile": "Jitsi on mobile download our apps and start a meeting from anywhere",
"moderatedMessage": "Or <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">book a meeting URL</a> in advance where you are the only moderator.",
"privacy": "Privacy",
"recentList": "Recent",
"recentListDelete": "Delete entry",
"recentListDelete": "Delete",
"recentListEmpty": "Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",
"reducedUIText": "Welcome to {{app}}!",
"roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.",
"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",
"startMeeting": "Start meeting",
"terms": "Terms",
"title": "Secure, fully featured, and completely free video conferencing"
},

View File

@@ -1,36 +0,0 @@
{
"android_package_name": "org.jitsi.meet",
"prefer_related_applications": true,
"related_applications": [
{
"id": "org.jitsi.meet",
"platform": "chromeos_play"
}
],
"short_name": "Jitsi Meet",
"name": "Jitsi Meet",
"icons": [
{
"src": "static/pwa/icons/icon192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "static/pwa/icons/icon512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "static/pwa/icons/iconMask.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"background_color": "#17A0DB",
"display": "standalone",
"scope": "/",
"theme_color": "#17A0DB"
}

View File

@@ -2,6 +2,7 @@
import Logger from 'jitsi-meet-logger';
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
import {
createApiEvent,
sendAnalytics
@@ -13,7 +14,7 @@ import {
setSubject
} from '../../react/features/base/conference';
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
import JitsiMeetJS, { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
import { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
import { pinParticipant } from '../../react/features/base/participants';
import {
processExternalDeviceRequest
@@ -45,6 +46,14 @@ declare var APP: Object;
*/
let commands = {};
/**
* The state of screen sharing(started/stopped) before the screen sharing is
* enabled and initialized.
* NOTE: This flag help us to cache the state and use it if toggle-share-screen
* was received before the initialization.
*/
let initialScreenSharingState = false;
/**
* The transport instance used for communication with external apps.
*
@@ -214,8 +223,7 @@ function initCommands() {
},
/**
* Starts a file recording or streaming session depending on the passed on params.
* For RTMP streams, `rtmpStreamKey` must be passed on. `rtmpBroadcastID` is optional.
* Starts a file recording or streaming depending on the passed on params.
* For youtube streams, `youtubeStreamKey` must be passed on. `youtubeBroadcastID` is optional.
* For dropbox recording, recording `mode` should be `file` and a dropbox oauth2 token must be provided.
* For file recording, recording `mode` should be `file` and optionally `shouldShare` could be passed on.
@@ -223,23 +231,13 @@ function initCommands() {
*
* @param { string } arg.mode - Recording mode, either `file` or `stream`.
* @param { string } arg.dropboxToken - Dropbox oauth2 token.
* @param { string } arg.rtmpStreamKey - The RTMP stream key.
* @param { string } arg.rtmpBroadcastID - The RTMP braodcast ID.
* @param { boolean } arg.shouldShare - Whether the recording should be shared with the participants or not.
* Only applies to certain jitsi meet deploys.
* @param { string } arg.youtubeStreamKey - The youtube stream key.
* @param { string } arg.youtubeBroadcastID - The youtube broacast ID.
* @returns {void}
*/
'start-recording': ({
mode,
dropboxToken,
shouldShare,
rtmpStreamKey,
rtmpBroadcastID,
youtubeStreamKey,
youtubeBroadcastID
}) => {
'start-recording': ({ mode, dropboxToken, shouldShare, youtubeStreamKey, youtubeBroadcastID }) => {
const state = APP.store.getState();
const conference = getCurrentConference(state);
@@ -255,8 +253,8 @@ function initCommands() {
return;
}
if (mode === JitsiRecordingConstants.mode.STREAM && !(youtubeStreamKey || rtmpStreamKey)) {
logger.error('Failed starting recording: missing youtube or RTMP stream key');
if (mode === JitsiRecordingConstants.mode.STREAM && !youtubeStreamKey) {
logger.error('Failed starting recording: missing youtube stream key');
return;
}
@@ -288,9 +286,9 @@ function initCommands() {
}
} else if (mode === JitsiRecordingConstants.mode.STREAM) {
recordingConfig = {
broadcastId: youtubeBroadcastID || rtmpBroadcastID,
broadcastId: youtubeBroadcastID,
mode: JitsiRecordingConstants.mode.STREAM,
streamId: youtubeStreamKey || rtmpStreamKey
streamId: youtubeStreamKey
};
} else {
logger.error('Invalid recording mode provided');
@@ -421,6 +419,19 @@ function initCommands() {
});
}
/**
* Listens for desktop/screen sharing enabled events and toggles the screen
* sharing if needed.
*
* @param {boolean} enabled - Current screen sharing enabled status.
* @returns {void}
*/
function onDesktopSharingEnabledChanged(enabled = false) {
if (enabled && initialScreenSharingState) {
toggleScreenSharing();
}
}
/**
* Check whether the API should be enabled or not.
*
@@ -448,10 +459,12 @@ function shouldBeEnabled() {
* @returns {void}
*/
function toggleScreenSharing(enable) {
if (JitsiMeetJS.isDesktopSharingEnabled()) {
APP.conference.toggleScreenSharing(enable).catch(() => {
logger.warn('Failed to toggle screen-sharing');
});
if (APP.conference.isDesktopSharingEnabled) {
// eslint-disable-next-line no-empty-function
APP.conference.toggleScreenSharing(enable).catch(() => {});
} else {
initialScreenSharingState = !initialScreenSharingState;
}
}
@@ -484,6 +497,10 @@ class API {
*/
this._enabled = true;
APP.conference.addListener(
JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
onDesktopSharingEnabledChanged);
initCommands();
}
@@ -1030,6 +1047,9 @@ class API {
dispose() {
if (this._enabled) {
this._enabled = false;
APP.conference.removeListener(
JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
onDesktopSharingEnabledChanged);
}
}
}

View File

@@ -124,13 +124,16 @@ function changeParticipantNumber(APIInstance, number) {
* configuration options defined in interface_config.js to be overridden.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication.
* @param {boolean} [options.noSSL] - If the value is true https won't be used.
* @param {string} [options.roomName] - The name of the room to join.
* @returns {string} The URL.
*/
function generateURL(domain, options = {}) {
return urlObjectToString({
...options,
url: `https://${domain}/#jitsi_meet_external_api_id=${id}`
url:
`${options.noSSL ? 'http' : 'https'}://${
domain}/#jitsi_meet_external_api_id=${id}`
});
}
@@ -161,6 +164,7 @@ function parseArguments(args) {
parentNode,
configOverwrite,
interfaceConfigOverwrite,
noSSL,
jwt,
onload
] = args;
@@ -172,6 +176,7 @@ function parseArguments(args) {
parentNode,
configOverwrite,
interfaceConfigOverwrite,
noSSL,
jwt,
onload
};
@@ -232,6 +237,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* configuration options defined in config.js to be overridden.
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
* configuration options defined in interface_config.js to be overridden.
* @param {boolean} [options.noSSL] - If the value is true https won't be
* used.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication.
* @param {string} [options.onload] - The onload function that will listen
@@ -254,6 +261,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
parentNode = document.body,
configOverwrite = {},
interfaceConfigOverwrite = {},
noSSL = false,
jwt = undefined,
onload = undefined,
invitees,
@@ -268,6 +276,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
configOverwrite,
interfaceConfigOverwrite,
jwt,
noSSL,
roomName,
devices,
userInfo,
@@ -1038,39 +1047,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
return setVideoInputDevice(this._transport, label, deviceId);
}
/**
* Starts a file recording or streaming session depending on the passed on params.
* For RTMP streams, `rtmpStreamKey` must be passed on. `rtmpBroadcastID` is optional.
* For youtube streams, `youtubeStreamKey` must be passed on. `youtubeBroadcastID` is optional.
* For dropbox recording, recording `mode` should be `file` and a dropbox oauth2 token must be provided.
* For file recording, recording `mode` should be `file` and optionally `shouldShare` could be passed on.
* No other params should be passed.
*
* @param {Object} options - An object with config options to pass along.
* @param { string } options.mode - Recording mode, either `file` or `stream`.
* @param { string } options.dropboxToken - Dropbox oauth2 token.
* @param { boolean } options.shouldShare - Whether the recording should be shared with the participants or not.
* Only applies to certain jitsi meet deploys.
* @param { string } options.rtmpStreamKey - The RTMP stream key.
* @param { string } options.rtmpBroadcastID - The RTMP broacast ID.
* @param { string } options.youtubeStreamKey - The youtube stream key.
* @param { string } options.youtubeBroadcastID - The youtube broacast ID.
* @returns {void}
*/
startRecording(options) {
this.executeCommand('startRecording', options);
}
/**
* Stops a recording or streaming session that is in progress.
*
* @param {string} mode - `file` or `stream`.
* @returns {void}
*/
stopRecording(mode) {
this.executeCommand('startRecording', mode);
}
/**
* Returns the configuration for electron for the windows that are open
* from Jitsi Meet.

View File

@@ -1,4 +1,4 @@
/* global APP, $, config */
/* global APP, $, config, interfaceConfig */
const UI = {};
@@ -59,6 +59,14 @@ UI.isFullScreen = function() {
return UIUtil.isFullScreen();
};
/**
* Returns true if the etherpad window is currently visible.
* @returns {Boolean} - true if the etherpad window is currently visible.
*/
UI.isEtherpadVisible = function() {
return Boolean(etherpadManager && etherpadManager.isVisible());
};
/**
* Returns true if there is a shared video which is being shown (?).
* @returns {boolean} - true if there is a shared video which is being shown.
@@ -135,7 +143,9 @@ UI.start = function() {
$.prompt.setDefaults({ persistent: false });
VideoLayout.init(eventEmitter);
VideoLayout.initLargeVideo();
if (!interfaceConfig.filmStripOnly) {
VideoLayout.initLargeVideo();
}
// Do not animate the video area on UI start (second argument passed into
// resizeVideoArea) because the animation is not visible anyway. Plus with
@@ -151,7 +161,10 @@ UI.start = function() {
$('body').addClass('desktop-browser');
}
if (config.iAmRecorder) {
if (interfaceConfig.filmStripOnly) {
$('body').addClass('filmstrip-only');
APP.store.dispatch(setNotificationsEnabled(false));
} else if (config.iAmRecorder) {
// in case of iAmSipGateway keep local video visible
if (!config.iAmSipGateway) {
VideoLayout.setLocalVideoVisible(false);
@@ -297,11 +310,54 @@ UI.toggleFilmstrip = function() {
*/
UI.toggleChat = () => APP.store.dispatch(toggleChat());
/**
* Handle new user display name.
*/
UI.inputDisplayNameHandler = function(newDisplayName) {
eventEmitter.emit(UIEvents.NICKNAME_CHANGED, newDisplayName);
};
// FIXME check if someone user this
UI.showLoginPopup = function(callback) {
logger.log('password is required');
const message
= `<input name="username" type="text"
placeholder="user@domain.net"
class="input-control" autofocus>
<input name="password" type="password"
data-i18n="[placeholder]dialog.userPassword"
class="input-control"
placeholder="user password">`
;
// eslint-disable-next-line max-params
const submitFunction = (e, v, m, f) => {
if (v && f.username && f.password) {
callback(f.username, f.password);
}
};
messageHandler.openTwoButtonDialog({
titleKey: 'dialog.passwordRequired',
msgString: message,
leftButtonKey: 'dialog.Ok',
submitFunction,
focus: ':input:first'
});
};
UI.askForNickname = function() {
// eslint-disable-next-line no-alert
return window.prompt('Your nickname (optional)');
};
/**
* Sets muted audio state for participant
*/
UI.setAudioMuted = function(id) {
// FIXME: Maybe this can be removed!
UI.setAudioMuted = function(id, muted) {
VideoLayout.onAudioMute(id, muted);
if (APP.conference.isLocalId(id)) {
APP.conference.updateAudioIconEnabled();
}
@@ -310,8 +366,8 @@ UI.setAudioMuted = function(id) {
/**
* Sets muted video state for participant
*/
UI.setVideoMuted = function(id) {
VideoLayout.onVideoMute(id);
UI.setVideoMuted = function(id, muted) {
VideoLayout.onVideoMute(id, muted);
if (APP.conference.isLocalId(id)) {
APP.conference.updateVideoIconEnabled();
}
@@ -453,6 +509,14 @@ UI.notifyTokenAuthFailed = function() {
});
};
UI.notifyInternalError = function(error) {
messageHandler.showError({
descriptionArguments: { error },
descriptionKey: 'dialog.internalError',
titleKey: 'dialog.internalErrorTitle'
});
};
UI.notifyFocusDisconnected = function(focus, retrySec) {
messageHandler.participantNotification(
null, 'notify.focus',
@@ -462,6 +526,16 @@ UI.notifyFocusDisconnected = function(focus, retrySec) {
);
};
/**
* Notifies interested listeners that the raise hand property has changed.
*
* @param {boolean} isRaisedHand indicates the current state of the
* "raised hand"
*/
UI.onLocalRaiseHandChanged = function(isRaisedHand) {
eventEmitter.emit(UIEvents.LOCAL_RAISE_HAND_CHANGED, isRaisedHand);
};
/**
* Update list of available physical devices.
*/
@@ -521,6 +595,38 @@ UI.onSharedVideoStop = function(id, attributes) {
}
};
/**
* Handles user's features changes.
*/
UI.onUserFeaturesChanged = user => VideoLayout.onUserFeaturesChanged(user);
/**
* Returns the number of known remote videos.
*
* @returns {number} The number of remote videos.
*/
UI.getRemoteVideosCount = () => VideoLayout.getRemoteVideosCount();
/**
* Sets the remote control active status for a remote participant.
*
* @param {string} participantID - The id of the remote participant.
* @param {boolean} isActive - The new remote control active status.
* @returns {void}
*/
UI.setRemoteControlActiveStatus = function(participantID, isActive) {
VideoLayout.setRemoteControlActiveStatus(participantID, isActive);
};
/**
* Sets the remote control active status for the local participant.
*
* @returns {void}
*/
UI.setLocalRemoteControlActiveChanged = function() {
VideoLayout.setLocalRemoteControlActiveChanged();
};
// TODO: Export every function separately. For now there is no point of doing
// this because we are importing everything.
export default UI;

View File

@@ -6,6 +6,48 @@ import UIUtil from '../util/UIUtil';
* Responsible for drawing audio levels.
*/
const AudioLevels = {
/**
* Fills the dot(s) with the specified "index", with as much opacity as
* indicated by "opacity".
*
* @param {string} elementID the parent audio indicator span element
* @param {number} index the index of the dots to fill, where 0 indicates
* the middle dot and the following increments point toward the
* corresponding pair of dots.
* @param {number} opacity the opacity to set for the specified dot.
*/
_setDotLevel(elementID, index, opacity) {
let audioSpan
= document.getElementById(elementID)
.getElementsByClassName('audioindicator');
// Make sure the audio span is still around.
if (audioSpan && audioSpan.length > 0) {
audioSpan = audioSpan[0];
} else {
return;
}
const audioTopDots
= audioSpan.getElementsByClassName('audiodot-top');
const audioDotMiddle
= audioSpan.getElementsByClassName('audiodot-middle');
const audioBottomDots
= audioSpan.getElementsByClassName('audiodot-bottom');
// First take care of the middle dot case.
if (index === 0) {
audioDotMiddle[0].style.opacity = opacity;
return;
}
// Index > 0 : we are setting non-middle dots.
index--;// eslint-disable-line no-param-reassign
audioBottomDots[index].style.opacity = opacity;
audioTopDots[this.sideDotsCount - index - 1].style.opacity = opacity;
},
/**
* Updates the audio level of the large video.
*

View File

@@ -26,6 +26,7 @@ export default class SharedVideoThumb extends SmallVideo {
this.$container = $(this.container);
this._setThumbnailSize();
this.bindHoverHandler();
this.isVideoMuted = true;
this.updateDisplayName();
this.container.onclick = this._onContainerClick;
}

View File

@@ -12,7 +12,6 @@ import {
JitsiParticipantConnectionStatus
} from '../../../react/features/base/lib-jitsi-meet';
import { VIDEO_TYPE } from '../../../react/features/base/media';
import { getParticipantById } from '../../../react/features/base/participants';
import { CHAT_SIZE } from '../../../react/features/chat';
import {
updateKnownLargeVideoResolution
@@ -225,8 +224,9 @@ export default class LargeVideoManager {
const wasUsersImageCached
= !isUserSwitch && container.wasVideoRendered;
const isVideoMuted = !stream || stream.isMuted();
const participant = getParticipantById(APP.store.getState(), id);
const connectionStatus = participant?.connectionStatus;
const connectionStatus
= APP.conference.getParticipantConnectionStatus(id);
const isVideoRenderable
= !isVideoMuted
&& (APP.conference.isLocalId(id)
@@ -356,12 +356,17 @@ export default class LargeVideoManager {
let widthToUse = this.preferredWidth || window.innerWidth;
const { isOpen } = APP.store.getState()['features/chat'];
if (isOpen) {
/**
* If chat state is open, we re-compute the container width
* by subtracting the default width of the chat.
*/
/**
* If chat state is open, we re-compute the container width by subtracting the default width of
* the chat. We re-compute the width again after the chat window is closed. This is needed when
* custom styling is configured on the large video container through the iFrame API.
*/
if (isOpen && !this.resizedForChat) {
widthToUse -= CHAT_SIZE;
this.resizedForChat = true;
} else if (this.resizedForChat) {
this.resizedForChat = false;
widthToUse += CHAT_SIZE;
}
this.width = widthToUse;
@@ -479,8 +484,8 @@ export default class LargeVideoManager {
*/
showRemoteConnectionMessage(show) {
if (typeof show !== 'boolean') {
const participant = getParticipantById(APP.store.getState(), this.id);
const connStatus = participant?.connectionStatus;
const connStatus
= APP.conference.getParticipantConnectionStatus(this.id);
// eslint-disable-next-line no-param-reassign
show = !APP.conference.isLocalId(this.id)

View File

@@ -63,7 +63,6 @@ export default class LocalVideo extends SmallVideo {
this.addAudioLevelIndicator();
this.updateIndicators();
this.updateStatusBar();
this.container.onclick = this._onContainerClick;
}
@@ -104,7 +103,7 @@ export default class LocalVideo extends SmallVideo {
}
this._renderDisplayName({
allowEditing: !config.disableProfile,
allowEditing: APP.store.getState()['features/base/jwt'].isGuest,
displayNameSuffix: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME,
elementID: 'localDisplayName',
participantID: this.id

View File

@@ -12,10 +12,16 @@ import { i18next } from '../../../react/features/base/i18n';
import {
JitsiParticipantConnectionStatus
} from '../../../react/features/base/lib-jitsi-meet';
import { getParticipantById } from '../../../react/features/base/participants';
import {
getPinnedParticipant,
pinParticipant
} from '../../../react/features/base/participants';
import { PresenceLabel } from '../../../react/features/presence-status';
import { stopController, requestRemoteControl } from '../../../react/features/remote-control';
import { RemoteVideoMenuTriggerButton } from '../../../react/features/remote-video-menu';
import {
REMOTE_CONTROL_MENU_STATES,
RemoteVideoMenuTriggerButton
} from '../../../react/features/remote-video-menu';
import { LAYOUTS, getCurrentLayout } from '../../../react/features/video-layout';
/* eslint-enable no-unused-vars */
import UIUtils from '../util/UIUtil';
@@ -72,6 +78,7 @@ export default class RemoteVideo extends SmallVideo {
this.videoSpanId = `participant_${this.id}`;
this._audioStreamElement = null;
this._supportsRemoteControl = false;
this.statsPopoverLocation = interfaceConfig.VERTICAL_FILMSTRIP ? 'left bottom' : 'top center';
this.addRemoteVideoContainer();
this.updateIndicators();
@@ -79,6 +86,8 @@ export default class RemoteVideo extends SmallVideo {
this.bindHoverHandler();
this.flipX = false;
this.isLocal = false;
this.popupMenuIsHovered = false;
this._isRemoteControlSessionActive = false;
/**
* The flag is set to <tt>true</tt> after the 'canplay' event has been
@@ -89,10 +98,24 @@ export default class RemoteVideo extends SmallVideo {
*/
this._canPlayEventReceived = false;
/**
* The flag is set to <tt>true</tt> if remote participant's video gets muted
* during his media connection disruption. This is to prevent black video
* being render on the thumbnail, because even though once the video has
* been played the image usually remains on the video element it seems that
* after longer period of the video element being hidden this image can be
* lost.
* @type {boolean}
*/
this.mutedWhileDisconnected = false;
// Bind event handlers so they are only bound once for every instance.
// TODO The event handlers should be turned into actions so changes can be
// handled through reducers and middleware.
this._requestRemoteControlPermissions
= this._requestRemoteControlPermissions.bind(this);
this._setAudioVolume = this._setAudioVolume.bind(this);
this._stopRemoteControl = this._stopRemoteControl.bind(this);
this.container.onclick = this._onContainerClick;
}
@@ -114,6 +137,17 @@ export default class RemoteVideo extends SmallVideo {
return this.container;
}
/**
* Checks whether current video is considered hovered. Currently it is hovered
* if the mouse is over the video, or if the connection indicator or the popup
* menu is shown(hovered).
* @private
* NOTE: extends SmallVideo's method
*/
_isHovered() {
return super._isHovered() || this.popupMenuIsHovered;
}
/**
* Generates the popup menu content.
*
@@ -121,6 +155,10 @@ export default class RemoteVideo extends SmallVideo {
* @private
*/
_generatePopupContent() {
if (interfaceConfig.filmStripOnly) {
return;
}
const remoteVideoMenuContainer
= this.container.querySelector('.remotevideomenu');
@@ -128,11 +166,40 @@ export default class RemoteVideo extends SmallVideo {
return;
}
const { controller } = APP.remoteControl;
let remoteControlState = null;
let onRemoteControlToggle;
if (this._supportsRemoteControl
&& ((!APP.remoteControl.active && !this._isRemoteControlSessionActive)
|| APP.remoteControl.controller.activeParticipant === this.id)) {
if (controller.getRequestedParticipant() === this.id) {
remoteControlState = REMOTE_CONTROL_MENU_STATES.REQUESTING;
} else if (controller.isStarted()) {
onRemoteControlToggle = this._stopRemoteControl;
remoteControlState = REMOTE_CONTROL_MENU_STATES.STARTED;
} else {
onRemoteControlToggle = this._requestRemoteControlPermissions;
remoteControlState = REMOTE_CONTROL_MENU_STATES.NOT_STARTED;
}
}
const initialVolumeValue = this._audioStreamElement && this._audioStreamElement.volume;
// hide volume when in silent mode
const onVolumeChange
= APP.store.getState()['features/base/config'].startSilent ? undefined : this._setAudioVolume;
const participantID = this.id;
const currentLayout = getCurrentLayout(APP.store.getState());
let remoteMenuPosition;
if (currentLayout === LAYOUTS.TILE_VIEW) {
remoteMenuPosition = 'left top';
} else if (currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
remoteMenuPosition = 'left bottom';
} else {
remoteMenuPosition = 'top center';
}
ReactDOM.render(
<Provider store = { APP.store }>
@@ -140,10 +207,14 @@ export default class RemoteVideo extends SmallVideo {
<AtlasKitThemeProvider mode = 'dark'>
<RemoteVideoMenuTriggerButton
initialVolumeValue = { initialVolumeValue }
isAudioMuted = { this.isAudioMuted }
menuPosition = { remoteMenuPosition }
onMenuDisplay
= {this._onRemoteVideoMenuDisplay.bind(this)}
onRemoteControlToggle = { onRemoteControlToggle }
onVolumeChange = { onVolumeChange }
participantID = { this.id } />
participantID = { participantID }
remoteControlState = { remoteControlState } />
</AtlasKitThemeProvider>
</I18nextProvider>
</Provider>,
@@ -157,6 +228,76 @@ export default class RemoteVideo extends SmallVideo {
this.updateRemoteVideoMenu();
}
/**
* Sets the remote control active status for the remote video.
*
* @param {boolean} isActive - The new remote control active status.
* @returns {void}
*/
setRemoteControlActiveStatus(isActive) {
this._isRemoteControlSessionActive = isActive;
this.updateRemoteVideoMenu();
}
/**
* Sets the remote control supported value and initializes or updates the menu
* depending on the remote control is supported or not.
* @param {boolean} isSupported
*/
setRemoteControlSupport(isSupported = false) {
if (this._supportsRemoteControl === isSupported) {
return;
}
this._supportsRemoteControl = isSupported;
this.updateRemoteVideoMenu();
}
/**
* Requests permissions for remote control session.
*/
_requestRemoteControlPermissions() {
APP.remoteControl.controller.requestPermissions(this.id, this.VideoLayout.getLargeVideoWrapper())
.then(result => {
if (result === null) {
return;
}
this.updateRemoteVideoMenu();
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
result === false ? 'dialog.remoteControlDeniedMessage' : 'dialog.remoteControlAllowedMessage',
{ user: this.user.getDisplayName() || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME }
);
if (result === true) {
// the remote control permissions has been granted
// pin the controlled participant
const pinnedParticipant = getPinnedParticipant(APP.store.getState()) || {};
const pinnedId = pinnedParticipant.id;
if (pinnedId !== this.id) {
APP.store.dispatch(pinParticipant(this.id));
}
}
}, error => {
logger.error(error);
this.updateRemoteVideoMenu();
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
'dialog.remoteControlErrorMessage',
{ user: this.user.getDisplayName() || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME }
);
});
this.updateRemoteVideoMenu();
}
/**
* Stops remote control session.
*/
_stopRemoteControl() {
// send message about stopping
APP.remoteControl.controller.stop();
this.updateRemoteVideoMenu();
}
/**
* Change the remote participant's volume level.
*
@@ -170,11 +311,43 @@ export default class RemoteVideo extends SmallVideo {
/**
* Updates the remote video menu.
*
* @param isMuted the new muted state to update to
*/
updateRemoteVideoMenu() {
updateRemoteVideoMenu(isMuted) {
if (typeof isMuted !== 'undefined') {
this.isAudioMuted = isMuted;
}
this._generatePopupContent();
}
/**
* @inheritDoc
* @override
*/
setVideoMutedView(isMuted) {
super.setVideoMutedView(isMuted);
// Update 'mutedWhileDisconnected' flag
this._figureOutMutedWhileDisconnected();
}
/**
* Figures out the value of {@link #mutedWhileDisconnected} flag by taking into
* account remote participant's network connectivity and video muted status.
*
* @private
*/
_figureOutMutedWhileDisconnected() {
const isActive = this.isConnectionActive();
if (!isActive && this.isVideoMuted) {
this.mutedWhileDisconnected = true;
} else if (isActive && !this.isVideoMuted) {
this.mutedWhileDisconnected = false;
}
}
/**
* Removes the remote stream element corresponding to the given stream and
* parent container.
@@ -205,6 +378,17 @@ export default class RemoteVideo extends SmallVideo {
this.updateView();
}
/**
* Checks whether the remote user associated with this <tt>RemoteVideo</tt>
* has connectivity issues.
*
* @return {boolean} <tt>true</tt> if the user's connection is fine or
* <tt>false</tt> otherwise.
*/
isConnectionActive() {
return this.user.getConnectionStatus() === JitsiParticipantConnectionStatus.ACTIVE;
}
/**
* The remote video is considered "playable" once the can play event has been received. It will be allowed to
* display video also in {@link JitsiParticipantConnectionStatus.INTERRUPTED} if the video has received the canplay
@@ -216,13 +400,12 @@ export default class RemoteVideo extends SmallVideo {
* @override
*/
isVideoPlayable() {
const participant = getParticipantById(APP.store.getState(), this.id);
const { connectionStatus, mutedWhileDisconnected } = participant || {};
const connectionState = APP.conference.getParticipantConnectionStatus(this.id);
return super.isVideoPlayable()
&& this._canPlayEventReceived
&& (connectionStatus === JitsiParticipantConnectionStatus.ACTIVE
|| (connectionStatus === JitsiParticipantConnectionStatus.INTERRUPTED && !mutedWhileDisconnected));
&& (connectionState === JitsiParticipantConnectionStatus.ACTIVE
|| (connectionState === JitsiParticipantConnectionStatus.INTERRUPTED && !this.mutedWhileDisconnected));
}
/**
@@ -230,9 +413,27 @@ export default class RemoteVideo extends SmallVideo {
*/
updateView() {
this.$container.toggleClass('audio-only', APP.conference.isAudioOnly());
this.updateConnectionStatusIndicator();
// This must be called after 'updateConnectionStatusIndicator' because it
// affects the display mode by modifying 'mutedWhileDisconnected' flag
super.updateView();
}
/**
* Updates the UI to reflect user's connectivity status.
*/
updateConnectionStatusIndicator() {
const connectionStatus = this.user.getConnectionStatus();
logger.debug(`${this.id} thumbnail connection status: ${connectionStatus}`);
// FIXME rename 'mutedWhileDisconnected' to 'mutedWhileNotRendering'
// Update 'mutedWhileDisconnected' flag
this._figureOutMutedWhileDisconnected();
this.updateConnectionStatus(connectionStatus);
}
/**
* Removes RemoteVideo from the page.
*/

View File

@@ -11,19 +11,11 @@ import { Provider } from 'react-redux';
import { AudioLevelIndicator } from '../../../react/features/audio-level-indicator';
import { Avatar as AvatarDisplay } from '../../../react/features/base/avatar';
import { i18next } from '../../../react/features/base/i18n';
import { MEDIA_TYPE } from '../../../react/features/base/media';
import {
getLocalParticipant,
getParticipantById,
getParticipantCount,
getPinnedParticipant,
pinParticipant
} from '../../../react/features/base/participants';
import {
getTrackByMediaTypeAndParticipant,
isLocalTrackMuted,
isRemoteTrackMuted
} from '../../../react/features/base/tracks';
import { ConnectionIndicator } from '../../../react/features/connection-indicator';
import { DisplayName } from '../../../react/features/display-name';
import {
@@ -89,12 +81,34 @@ export default class SmallVideo {
* Constructor.
*/
constructor(VideoLayout) {
this.isAudioMuted = false;
this.isVideoMuted = false;
this.isScreenSharing = false;
this.videoStream = null;
this.audioStream = null;
this.VideoLayout = VideoLayout;
this.videoIsHovered = false;
this.videoType = undefined;
/**
* The current state of the user's bridge connection. The value should be
* a string as enumerated in the library's participantConnectionStatus
* constants.
*
* @private
* @type {string|null}
*/
this._connectionStatus = null;
/**
* Whether or not the ConnectionIndicator's popover is hovered. Modifies
* how the video overlays display based on hover state.
*
* @private
* @type {boolean}
*/
this._popoverIsHovered = false;
/**
* Whether or not the connection indicator should be displayed.
*
@@ -120,6 +134,7 @@ export default class SmallVideo {
this._showRaisedHand = false;
// Bind event handlers so they are only bound once for every instance.
this._onPopoverHover = this._onPopoverHover.bind(this);
this.updateView = this.updateView.bind(this);
this._onContainerClick = this._onContainerClick.bind(this);
@@ -200,6 +215,56 @@ export default class SmallVideo {
this.updateIndicators();
}
/**
* Updates the connectionStatus stat which displays in the ConnectionIndicator.
* @returns {void}
*/
updateConnectionStatus(connectionStatus) {
this._connectionStatus = connectionStatus;
this.updateIndicators();
}
/**
* Shows / hides the audio muted indicator over small videos.
*
* @param {boolean} isMuted indicates if the muted element should be shown
* or hidden
*/
showAudioIndicator(isMuted) {
this.isAudioMuted = isMuted;
this.updateStatusBar();
}
/**
* Shows / hides the screen-share indicator over small videos.
*
* @param {boolean} isScreenSharing indicates if the screen-share element should be shown
* or hidden
*/
setScreenSharing(isScreenSharing) {
if (isScreenSharing === this.isScreenSharing) {
return;
}
this.isScreenSharing = isScreenSharing;
this.updateView();
this.updateStatusBar();
}
/**
* Shows video muted indicator over small videos and disables/enables avatar
* if video muted.
*
* @param {boolean} isMuted indicates if we should set the view to muted view
* or not
*/
setVideoMutedView(isMuted) {
this.isVideoMuted = isMuted;
this.updateView();
this.updateStatusBar();
}
/**
* Create or updates the ReactElement for displaying status indicators about
* audio mute, video mute, and moderator status.
@@ -217,6 +282,9 @@ export default class SmallVideo {
<Provider store = { APP.store }>
<I18nextProvider i18n = { i18next }>
<StatusIndicators
showAudioMutedIndicator = { this.isAudioMuted }
showScreenShareIndicator = { this.isScreenSharing }
showVideoMutedIndicator = { this.isVideoMuted }
participantID = { this.id } />
</I18nextProvider>
</Provider>,
@@ -391,18 +459,7 @@ export default class SmallVideo {
* or <tt>false</tt> otherwise.
*/
isVideoPlayable() {
const state = APP.store.getState();
const tracks = state['features/base/tracks'];
const participant = this.id ? getParticipantById(state, this.id) : getLocalParticipant(state);
let isVideoMuted = true;
if (participant?.local) {
isVideoMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO);
} else if (!participant?.isFakeParticipant) { // remote participants excluding shared video
isVideoMuted = isRemoteTrackMuted(tracks, MEDIA_TYPE.VIDEO, this.id);
}
return this.videoStream && !isVideoMuted && !APP.conference.isAudioOnly();
return this.videoStream && !this.isVideoMuted && !APP.conference.isAudioOnly();
}
/**
@@ -432,32 +489,19 @@ export default class SmallVideo {
* @returns {Object}
*/
computeDisplayModeInput() {
let isScreenSharing = false;
let connectionStatus, mutedWhileDisconnected;
const state = APP.store.getState();
const participant = getParticipantById(state, this.id);
if (typeof participant !== 'undefined' && !participant.isFakeParticipant && !participant.local) {
const tracks = state['features/base/tracks'];
const track = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, this.id);
isScreenSharing = typeof track !== 'undefined' && track.videoType === 'desktop';
connectionStatus = participant.connectionStatus;
mutedWhileDisconnected = participant.mutedWhileDisconnected;
}
return {
isCurrentlyOnLargeVideo: this.isCurrentlyOnLargeVideo(),
isHovered: this._isHovered(),
isAudioOnly: APP.conference.isAudioOnly(),
tileViewActive: shouldDisplayTileView(state),
tileViewActive: shouldDisplayTileView(APP.store.getState()),
isVideoPlayable: this.isVideoPlayable(),
hasVideo: Boolean(this.selectVideoElement().length),
connectionStatus,
mutedWhileDisconnected,
connectionStatus: APP.conference.getParticipantConnectionStatus(this.id),
mutedWhileDisconnected: this.mutedWhileDisconnected,
canPlayEventReceived: this._canPlayEventReceived,
videoStream: Boolean(this.videoStream),
isScreenSharing,
isVideoMuted: this.isVideoMuted,
isScreenSharing: this.isScreenSharing,
videoStreamMuted: this.videoStream ? this.videoStream.isMuted() : 'no stream'
};
}
@@ -469,7 +513,7 @@ export default class SmallVideo {
* @private
*/
_isHovered() {
return this.videoIsHovered;
return this.videoIsHovered || this._popoverIsHovered;
}
/**
@@ -697,9 +741,10 @@ export default class SmallVideo {
{ this._showConnectionIndicator
? <ConnectionIndicator
alwaysVisible = { showConnectionIndicator }
connectionStatus = { this._connectionStatus }
iconSize = { iconSize }
isLocalVideo = { this.isLocal }
enableStatsDisplay = { true }
enableStatsDisplay = { !interfaceConfig.filmStripOnly }
participantId = { this.id }
statsPopoverPosition = { statsPopoverPosition } />
: null }
@@ -791,6 +836,19 @@ export default class SmallVideo {
}
}
/**
* Updates the current state of the connection indicator popover being hovered.
* If hovered, display the small video as if it is hovered.
*
* @param {boolean} popoverIsHovered - Whether or not the mouse cursor is
* currently over the connection indicator popover.
* @returns {void}
*/
_onPopoverHover(popoverIsHovered) {
this._popoverIsHovered = popoverIsHovered;
this.updateView();
}
/**
* Sets the size of the thumbnail.
*/

View File

@@ -1,4 +1,4 @@
/* global APP, $ */
/* global APP, $, interfaceConfig */
import Logger from 'jitsi-meet-logger';
@@ -52,7 +52,7 @@ function onLocalFlipXChanged(val) {
*/
function getAllThumbnails() {
return [
...localVideoThumbnail ? [ localVideoThumbnail ] : [],
localVideoThumbnail,
...Object.values(remoteVideos)
];
}
@@ -173,9 +173,11 @@ const VideoLayout = {
remoteVideo.addRemoteStreamElement(stream);
// Make sure track's muted state is reflected
if (stream.getType() !== 'audio') {
this.onVideoMute(id);
remoteVideo.updateView();
if (stream.getType() === 'audio') {
this.onAudioMute(id, stream.isMuted());
} else {
this.onVideoMute(id, stream.isMuted());
remoteVideo.setScreenSharing(stream.videoType === 'desktop');
}
},
@@ -187,7 +189,7 @@ const VideoLayout = {
if (remoteVideo) {
remoteVideo.removeRemoteStreamElement(stream);
remoteVideo.updateView();
remoteVideo.setScreenSharing(false);
}
this.updateMutedForNoTracks(id, stream.getType());
@@ -208,7 +210,7 @@ const VideoLayout = {
if (mediaType === 'audio') {
APP.UI.setAudioMuted(participantId, true);
} else if (mediaType === 'video') {
APP.UI.setVideoMuted(participantId);
APP.UI.setVideoMuted(participantId, true);
} else {
logger.error(`Unsupported media type: ${mediaType}`);
}
@@ -264,6 +266,10 @@ const VideoLayout = {
* @returns {void}
*/
onPinChange(pinnedParticipantID) {
if (interfaceConfig.filmStripOnly) {
return;
}
getAllThumbnails().forEach(thumbnail =>
thumbnail.focus(pinnedParticipantID === thumbnail.getId()));
},
@@ -293,6 +299,7 @@ const VideoLayout = {
const jitsiParticipant = APP.conference.getParticipantById(id);
const remoteVideo = new RemoteVideo(jitsiParticipant, VideoLayout);
this._setRemoteControlProperties(jitsiParticipant, remoteVideo);
this.addRemoteVideoContainer(id, remoteVideo);
this.updateMutedForNoTracks(id, 'audio');
@@ -322,17 +329,35 @@ const VideoLayout = {
this._updateLargeVideoIfDisplayed(resourceJid, true);
},
/**
* On audio muted event.
*/
onAudioMute(id, isMuted) {
if (APP.conference.isLocalId(id)) {
localVideoThumbnail.showAudioIndicator(isMuted);
} else {
const remoteVideo = remoteVideos[id];
if (!remoteVideo) {
return;
}
remoteVideo.showAudioIndicator(isMuted);
remoteVideo.updateRemoteVideoMenu();
}
},
/**
* On video muted event.
*/
onVideoMute(id) {
onVideoMute(id, value) {
if (APP.conference.isLocalId(id)) {
localVideoThumbnail && localVideoThumbnail.updateView();
localVideoThumbnail && localVideoThumbnail.setVideoMutedView(value);
} else {
const remoteVideo = remoteVideos[id];
if (remoteVideo) {
remoteVideo.updateView();
remoteVideo.setVideoMutedView(value);
}
}
@@ -386,6 +411,12 @@ const VideoLayout = {
const remoteVideo = remoteVideos[id];
if (remoteVideo) {
// Updating only connection status indicator is not enough, because
// when we the connection is restored while the avatar was displayed
// (due to 'muted while disconnected' condition) we may want to show
// the video stream again and in order to do that the display mode
// must be updated.
// remoteVideo.updateConnectionStatusIndicator(isActive);
remoteVideo.updateView();
}
},
@@ -463,7 +494,7 @@ const VideoLayout = {
}
logger.info('Peer video type changed: ', id, newVideoType);
remoteVideo.updateView();
remoteVideo.setScreenSharing(newVideoType === 'desktop');
},
/**
@@ -653,6 +684,33 @@ const VideoLayout = {
this.localFlipX = val;
},
/**
* Handles user's features changes.
*/
onUserFeaturesChanged(user) {
const video = this.getSmallVideo(user.getId());
if (!video) {
return;
}
this._setRemoteControlProperties(user, video);
},
/**
* Sets the remote control properties (checks whether remote control
* is supported and executes remoteVideo.setRemoteControlSupport).
* @param {JitsiParticipant} user the user that will be checked for remote
* control support.
* @param {RemoteVideo} remoteVideo the remoteVideo on which the properties
* will be set.
*/
_setRemoteControlProperties(user, remoteVideo) {
APP.remoteControl.checkUserRemoteControlSupport(user)
.then(result => remoteVideo.setRemoteControlSupport(result))
.catch(error =>
logger.warn(`could not get remote control properties for: ${user.getJid()}`, error));
},
/**
* Returns the wrapper jquery selector for the largeVideo
* @returns {JQuerySelector} the wrapper jquery selector for the largeVideo
@@ -670,6 +728,28 @@ const VideoLayout = {
return Object.keys(remoteVideos).length;
},
/**
* Sets the remote control active status for a remote participant.
*
* @param {string} participantID - The id of the remote participant.
* @param {boolean} isActive - The new remote control active status.
* @returns {void}
*/
setRemoteControlActiveStatus(participantID, isActive) {
remoteVideos[participantID].setRemoteControlActiveStatus(isActive);
},
/**
* Sets the remote control active status for the local participant.
*
* @returns {void}
*/
setLocalRemoteControlActiveChanged() {
Object.values(remoteVideos).forEach(
remoteVideo => remoteVideo.updateRemoteVideoMenu()
);
},
/**
* Helper method to invoke when the video layout has changed and elements
* have to be re-arranged and resized.

View File

@@ -1,4 +1,4 @@
/* global APP, $ */
/* global APP, $, interfaceConfig */
import Logger from 'jitsi-meet-logger';
@@ -203,12 +203,14 @@ const KeyboardShortcut = {
});
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
this.registerShortcut('T', null, () => {
sendAnalytics(createShortcutEvent('speaker.stats'));
APP.store.dispatch(toggleDialog(SpeakerStats, {
conference: APP.conference
}));
}, 'keyboardShortcuts.showSpeakerStats');
if (!interfaceConfig.filmStripOnly) {
this.registerShortcut('T', null, () => {
sendAnalytics(createShortcutEvent('speaker.stats'));
APP.store.dispatch(toggleDialog(SpeakerStats, {
conference: APP.conference
}));
}, 'keyboardShortcuts.showSpeakerStats');
}
/**
* FIXME: Currently focus keys are directly implemented below in

View File

@@ -158,9 +158,8 @@ for (let i = 0; i < 26; i++) {
/**
* Returns key associated with the keyCode from the passed event.
*
* @param {KeyboardEvent} event - The event.
* @returns {KEYS} - The key on the keyboard.
* @param {KeyboardEvent} event the event
* @returns {KEYS} the key on the keyboard.
*/
export function keyboardEventToKey(event) {
return keyCodeToKey[event.which];

View File

@@ -0,0 +1,3 @@
module.exports = {
'extends': '../../react/.eslintrc.js'
};

View File

@@ -0,0 +1,474 @@
/* @flow */
import { getLogger } from 'jitsi-meet-logger';
import {
JitsiConferenceEvents
} from '../../react/features/base/lib-jitsi-meet';
import UIEvents from '../../service/UI/UIEvents';
import {
EVENTS,
PERMISSIONS_ACTIONS,
REMOTE_CONTROL_MESSAGE_NAME
} from '../../service/remotecontrol/Constants';
import * as RemoteControlEvents
from '../../service/remotecontrol/RemoteControlEvents';
import * as KeyCodes from '../keycode/keycode';
import RemoteControlParticipant from './RemoteControlParticipant';
declare var $: Function;
declare var APP: Object;
const logger = getLogger(__filename);
/**
* Extract the keyboard key from the keyboard event.
*
* @param {KeyboardEvent} event - The event.
* @returns {KEYS} The key that is pressed or undefined.
*/
function getKey(event) {
return KeyCodes.keyboardEventToKey(event);
}
/**
* Extract the modifiers from the keyboard event.
*
* @param {KeyboardEvent} event - The event.
* @returns {Array} With possible values: "shift", "control", "alt", "command".
*/
function getModifiers(event) {
const modifiers = [];
if (event.shiftKey) {
modifiers.push('shift');
}
if (event.ctrlKey) {
modifiers.push('control');
}
if (event.altKey) {
modifiers.push('alt');
}
if (event.metaKey) {
modifiers.push('command');
}
return modifiers;
}
/**
* This class represents the controller party for a remote controller session.
* It listens for mouse and keyboard events and sends them to the receiver
* party of the remote control session.
*/
export default class Controller extends RemoteControlParticipant {
_area: ?Object;
_controlledParticipant: string | null;
_isCollectingEvents: boolean;
_largeVideoChangedListener: Function;
_requestedParticipant: string | null;
_stopListener: Function;
_userLeftListener: Function;
/**
* Creates new instance.
*/
constructor() {
super();
this._isCollectingEvents = false;
this._controlledParticipant = null;
this._requestedParticipant = null;
this._stopListener = this._handleRemoteControlStoppedEvent.bind(this);
this._userLeftListener = this._onUserLeft.bind(this);
this._largeVideoChangedListener
= this._onLargeVideoIdChanged.bind(this);
}
/**
* Returns the current active participant's id.
*
* @returns {string|null} - The id of the current active participant.
*/
get activeParticipant(): string | null {
return this._requestedParticipant || this._controlledParticipant;
}
/**
* Requests permissions from the remote control receiver side.
*
* @param {string} userId - The user id of the participant that will be
* requested.
* @param {JQuerySelector} eventCaptureArea - The area that is going to be
* used mouse and keyboard event capture.
* @returns {Promise<boolean>} Resolve values - true(accept), false(deny),
* null(the participant has left).
*/
requestPermissions(
userId: string,
eventCaptureArea: Object
): Promise<boolean | null> {
if (!this._enabled) {
return Promise.reject(new Error('Remote control is disabled!'));
}
this.emit(RemoteControlEvents.ACTIVE_CHANGED, true);
this._area = eventCaptureArea;// $("#largeVideoWrapper")
logger.log(`Requsting remote control permissions from: ${userId}`);
return new Promise((resolve, reject) => {
// eslint-disable-next-line prefer-const
let onUserLeft, permissionsReplyListener;
const clearRequest = () => {
this._requestedParticipant = null;
APP.conference.removeConferenceListener(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
permissionsReplyListener);
APP.conference.removeConferenceListener(
JitsiConferenceEvents.USER_LEFT,
onUserLeft);
};
permissionsReplyListener = (participant, event) => {
let result = null;
try {
result = this._handleReply(participant, event);
} catch (e) {
clearRequest();
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
reject(e);
}
if (result !== null) {
clearRequest();
if (result === false) {
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
}
resolve(result);
}
};
onUserLeft = id => {
if (id === this._requestedParticipant) {
clearRequest();
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
resolve(null);
}
};
APP.conference.addConferenceListener(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
permissionsReplyListener);
APP.conference.addConferenceListener(
JitsiConferenceEvents.USER_LEFT,
onUserLeft);
this._requestedParticipant = userId;
this.sendRemoteControlEndpointMessage(
userId,
{
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.request
},
e => {
clearRequest();
reject(e);
});
});
}
/**
* Handles the reply of the permissions request.
*
* @param {JitsiParticipant} participant - The participant that has sent the
* reply.
* @param {RemoteControlEvent} event - The remote control event.
* @returns {boolean|null}
*/
_handleReply(participant: Object, event: Object) {
const userId = participant.getId();
if (this._enabled
&& event.name === REMOTE_CONTROL_MESSAGE_NAME
&& event.type === EVENTS.permissions
&& userId === this._requestedParticipant) {
if (event.action !== PERMISSIONS_ACTIONS.grant) {
this._area = undefined;
}
switch (event.action) {
case PERMISSIONS_ACTIONS.grant: {
this._controlledParticipant = userId;
logger.log('Remote control permissions granted to:', userId);
this._start();
return true;
}
case PERMISSIONS_ACTIONS.deny:
return false;
case PERMISSIONS_ACTIONS.error:
throw new Error('Error occurred on receiver side');
default:
throw new Error('Unknown reply received!');
}
} else {
// different message type or another user -> ignoring the message
return null;
}
}
/**
* Handles remote control stopped.
*
* @param {JitsiParticipant} participant - The participant that has sent the
* event.
* @param {Object} event - EndpointMessage event from the data channels.
* @property {string} type - The function process only events with
* name REMOTE_CONTROL_MESSAGE_NAME.
* @returns {void}
*/
_handleRemoteControlStoppedEvent(participant: Object, event: Object) {
if (this._enabled
&& event.name === REMOTE_CONTROL_MESSAGE_NAME
&& event.type === EVENTS.stop
&& participant.getId() === this._controlledParticipant) {
this._stop();
}
}
/**
* Starts processing the mouse and keyboard events. Sets conference
* listeners. Disables keyboard events.
*
* @returns {void}
*/
_start() {
logger.log('Starting remote control controller.');
APP.UI.addListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
this._largeVideoChangedListener);
APP.conference.addConferenceListener(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
this._stopListener);
APP.conference.addConferenceListener(JitsiConferenceEvents.USER_LEFT,
this._userLeftListener);
this.resume();
}
/**
* Disables the keyboatd shortcuts. Starts collecting remote control
* events. It can be used to resume an active remote control session wchich
* was paused with this.pause().
*
* @returns {void}
*/
resume() {
let area;
if (!this._enabled
|| this._isCollectingEvents
|| !(area = this._area)) {
return;
}
logger.log('Resuming remote control controller.');
this._isCollectingEvents = true;
APP.keyboardshortcut.enable(false);
area.mousemove(event => {
const area = this._area; // eslint-disable-line no-shadow
if (!area) {
return;
}
const position = area.position();
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type: EVENTS.mousemove,
x: (event.pageX - position.left) / area.width(),
y: (event.pageY - position.top) / area.height()
});
});
area.mousedown(this._onMouseClickHandler.bind(this, EVENTS.mousedown));
area.mouseup(this._onMouseClickHandler.bind(this, EVENTS.mouseup));
area.dblclick(
this._onMouseClickHandler.bind(this, EVENTS.mousedblclick));
area.contextmenu(() => false);
area[0].onmousewheel = event => {
event.preventDefault();
event.stopPropagation();
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type: EVENTS.mousescroll,
x: event.deltaX,
y: event.deltaY
});
return false;
};
$(window).keydown(this._onKeyPessHandler.bind(this,
EVENTS.keydown));
$(window).keyup(this._onKeyPessHandler.bind(this, EVENTS.keyup));
}
/**
* Stops processing the mouse and keyboard events. Removes added listeners.
* Enables the keyboard shortcuts. Displays dialog to notify the user that
* remote control session has ended.
*
* @returns {void}
*/
_stop() {
if (!this._controlledParticipant) {
return;
}
logger.log('Stopping remote control controller.');
APP.UI.removeListener(UIEvents.LARGE_VIDEO_ID_CHANGED,
this._largeVideoChangedListener);
APP.conference.removeConferenceListener(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
this._stopListener);
APP.conference.removeConferenceListener(JitsiConferenceEvents.USER_LEFT,
this._userLeftListener);
this.pause();
this._controlledParticipant = null;
this._area = undefined;
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
'dialog.remoteControlStopMessage'
);
}
/**
* Executes this._stop() mehtod which stops processing the mouse and
* keyboard events, removes added listeners, enables the keyboard shortcuts,
* displays dialog to notify the user that remote control session has ended.
* In addition sends stop message to the controlled participant.
*
* @returns {void}
*/
stop() {
if (!this._controlledParticipant) {
return;
}
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type: EVENTS.stop
});
this._stop();
}
/**
* Pauses the collecting of events and enables the keyboard shortcus. But
* it doesn't removes any other listeners. Basically the remote control
* session will be still active after this.pause(), but no events from the
* controller side will be captured and sent. You can resume the collecting
* of the events with this.resume().
*
* @returns {void}
*/
pause() {
if (!this._controlledParticipant) {
return;
}
logger.log('Pausing remote control controller.');
this._isCollectingEvents = false;
APP.keyboardshortcut.enable(true);
const area = this._area;
if (area) {
area.off('contextmenu');
area.off('dblclick');
area.off('mousedown');
area.off('mousemove');
area.off('mouseup');
area[0].onmousewheel = undefined;
}
$(window).off('keydown');
$(window).off('keyup');
}
/**
* Handler for mouse click events.
*
* @param {string} type - The type of event ("mousedown"/"mouseup").
* @param {Event} event - The mouse event.
* @returns {void}
*/
_onMouseClickHandler(type: string, event: Object) {
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type,
button: event.which
});
}
/**
* Returns true if the remote control session is started.
*
* @returns {boolean}
*/
isStarted() {
return this._controlledParticipant !== null;
}
/**
* Returns the id of the requested participant.
*
* @returns {string} The id of the requested participant.
* NOTE: This id should be the result of JitsiParticipant.getId() call.
*/
getRequestedParticipant() {
return this._requestedParticipant;
}
/**
* Handler for key press events.
*
* @param {string} type - The type of event ("keydown"/"keyup").
* @param {Event} event - The key event.
* @returns {void}
*/
_onKeyPessHandler(type: string, event: Object) {
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type,
key: getKey(event),
modifiers: getModifiers(event)
});
}
/**
* Calls the stop method if the other side have left.
*
* @param {string} id - The user id for the participant that have left.
* @returns {void}
*/
_onUserLeft(id: string) {
if (this._controlledParticipant === id) {
this._stop();
}
}
/**
* Handles changes of the participant displayed on the large video.
*
* @param {string} id - The user id for the participant that is displayed.
* @returns {void}
*/
_onLargeVideoIdChanged(id: string) {
if (!this._controlledParticipant) {
return;
}
if (this._controlledParticipant === id) {
this.resume();
} else {
this.pause();
}
}
}

View File

@@ -0,0 +1,331 @@
/* @flow */
import { getLogger } from 'jitsi-meet-logger';
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
import {
JitsiConferenceEvents
} from '../../react/features/base/lib-jitsi-meet';
import {
openRemoteControlAuthorizationDialog
} from '../../react/features/remote-control';
import {
DISCO_REMOTE_CONTROL_FEATURE,
EVENTS,
PERMISSIONS_ACTIONS,
REMOTE_CONTROL_MESSAGE_NAME,
REQUESTS
} from '../../service/remotecontrol/Constants';
import * as RemoteControlEvents
from '../../service/remotecontrol/RemoteControlEvents';
import { Transport, PostMessageTransportBackend } from '../transport';
import RemoteControlParticipant from './RemoteControlParticipant';
declare var APP: Object;
declare var config: Object;
declare var interfaceConfig: Object;
const logger = getLogger(__filename);
/**
* The transport instance used for communication with external apps.
*
* @type {Transport}
*/
const transport = new Transport({
backend: new PostMessageTransportBackend({
postisOptions: { scope: 'jitsi-remote-control' }
})
});
/**
* This class represents the receiver party for a remote controller session.
* It handles "remote-control-event" events and sends them to the
* API module. From there the events can be received from wrapper application
* and executed.
*/
export default class Receiver extends RemoteControlParticipant {
_controller: ?string;
_enabled: boolean;
_hangupListener: Function;
_remoteControlEventsListener: Function;
_userLeftListener: Function;
/**
* Creates new instance.
*/
constructor() {
super();
this._controller = null;
this._remoteControlEventsListener
= this._onRemoteControlMessage.bind(this);
this._userLeftListener = this._onUserLeft.bind(this);
this._hangupListener = this._onHangup.bind(this);
// We expect here that even if we receive the supported event earlier
// it will be cached and we'll receive it.
transport.on('event', event => {
if (event.name === REMOTE_CONTROL_MESSAGE_NAME) {
this._onRemoteControlAPIEvent(event);
return true;
}
return false;
});
}
/**
* Enables / Disables the remote control.
*
* @param {boolean} enabled - The new state.
* @returns {void}
*/
_enable(enabled: boolean) {
if (this._enabled === enabled) {
return;
}
this._enabled = enabled;
if (enabled === true) {
logger.log('Remote control receiver enabled.');
// Announce remote control support.
APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true);
APP.conference.addConferenceListener(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
this._remoteControlEventsListener);
APP.conference.addListener(JitsiMeetConferenceEvents.BEFORE_HANGUP,
this._hangupListener);
} else {
logger.log('Remote control receiver disabled.');
this._stop(true);
APP.connection.removeFeature(DISCO_REMOTE_CONTROL_FEATURE);
APP.conference.removeConferenceListener(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
this._remoteControlEventsListener);
APP.conference.removeListener(
JitsiMeetConferenceEvents.BEFORE_HANGUP,
this._hangupListener);
}
}
/**
* Removes the listener for JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED
* events. Sends stop message to the wrapper application. Optionally
* displays dialog for informing the user that remote control session
* ended.
*
* @param {boolean} [dontNotify] - If true - a notification about stopping
* the remote control won't be displayed.
* @returns {void}
*/
_stop(dontNotify: boolean = false) {
if (!this._controller) {
return;
}
logger.log('Remote control receiver stop.');
this._controller = null;
APP.conference.removeConferenceListener(
JitsiConferenceEvents.USER_LEFT,
this._userLeftListener);
transport.sendEvent({
name: REMOTE_CONTROL_MESSAGE_NAME,
type: EVENTS.stop
});
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
if (!dontNotify) {
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
'dialog.remoteControlStopMessage'
);
}
}
/**
* Calls this._stop() and sends stop message to the controller participant.
*
* @returns {void}
*/
stop() {
if (!this._controller) {
return;
}
this.sendRemoteControlEndpointMessage(this._controller, {
type: EVENTS.stop
});
this._stop();
}
/**
* Listens for data channel EndpointMessage. Handles only remote control
* messages. Sends the remote control messages to the external app that
* will execute them.
*
* @param {JitsiParticipant} participant - The controller participant.
* @param {Object} message - EndpointMessage from the data channels.
* @param {string} message.name - The function processes only messages with
* name REMOTE_CONTROL_MESSAGE_NAME.
* @returns {void}
*/
_onRemoteControlMessage(participant: Object, message: Object) {
if (message.name !== REMOTE_CONTROL_MESSAGE_NAME) {
return;
}
if (this._enabled) {
if (this._controller === null
&& message.type === EVENTS.permissions
&& message.action === PERMISSIONS_ACTIONS.request) {
const userId = participant.getId();
this.emit(RemoteControlEvents.ACTIVE_CHANGED, true);
APP.store.dispatch(
openRemoteControlAuthorizationDialog(userId));
} else if (this._controller === participant.getId()) {
if (message.type === EVENTS.stop) {
this._stop();
} else { // forward the message
transport.sendEvent(message);
}
} // else ignore
} else {
logger.log('Remote control message is ignored because remote '
+ 'control is disabled', message);
}
}
/**
* Denies remote control access for user associated with the passed user id.
*
* @param {string} userId - The id associated with the user who sent the
* request for remote control authorization.
* @returns {void}
*/
deny(userId: string) {
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.deny
});
}
/**
* Grants remote control access to user associated with the passed user id.
*
* @param {string} userId - The id associated with the user who sent the
* request for remote control authorization.
* @returns {void}
*/
grant(userId: string) {
APP.conference.addConferenceListener(JitsiConferenceEvents.USER_LEFT,
this._userLeftListener);
this._controller = userId;
logger.log(`Remote control permissions granted to: ${userId}`);
let promise;
if (APP.conference.isSharingScreen
&& APP.conference.getDesktopSharingSourceType() === 'screen') {
promise = this._sendStartRequest();
} else {
promise = APP.conference.toggleScreenSharing(
true,
{
desktopSharingSources: [ 'screen' ]
})
.then(() => this._sendStartRequest());
}
promise
.then(() =>
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.grant
})
)
.catch(error => {
logger.error(error);
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.error
});
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
'dialog.startRemoteControlErrorMessage'
);
this._stop(true);
});
}
/**
* Sends remote control start request.
*
* @returns {Promise}
*/
_sendStartRequest() {
return transport.sendRequest({
name: REMOTE_CONTROL_MESSAGE_NAME,
type: REQUESTS.start,
sourceId: APP.conference.getDesktopSharingSourceId()
});
}
/**
* Handles remote control events from the external app. Currently only
* events with type EVENTS.supported and EVENTS.stop are
* supported.
*
* @param {RemoteControlEvent} event - The remote control event.
* @returns {void}
*/
_onRemoteControlAPIEvent(event: Object) {
switch (event.type) {
case EVENTS.supported:
this._onRemoteControlSupported();
break;
case EVENTS.stop:
this.stop();
break;
}
}
/**
* Handles events for support for executing remote control events into
* the wrapper application.
*
* @returns {void}
*/
_onRemoteControlSupported() {
logger.log('Remote Control supported.');
if (config.disableRemoteControl) {
logger.log('Remote Control disabled.');
} else {
this._enable(true);
}
}
/**
* Calls the stop method if the other side have left.
*
* @param {string} id - The user id for the participant that have left.
* @returns {void}
*/
_onUserLeft(id: string) {
if (this._controller === id) {
this._stop();
}
}
/**
* Handles hangup events. Disables the receiver.
*
* @returns {void}
*/
_onHangup() {
this._enable(false);
}
}

View File

@@ -0,0 +1,99 @@
/* @flow */
import EventEmitter from 'events';
import { getLogger } from 'jitsi-meet-logger';
import { DISCO_REMOTE_CONTROL_FEATURE }
from '../../service/remotecontrol/Constants';
import * as RemoteControlEvents
from '../../service/remotecontrol/RemoteControlEvents';
import Controller from './Controller';
import Receiver from './Receiver';
const logger = getLogger(__filename);
declare var APP: Object;
declare var config: Object;
/**
* Implements the remote control functionality.
*/
class RemoteControl extends EventEmitter {
_active: boolean;
_initialized: boolean;
controller: Controller;
receiver: Receiver;
/**
* Constructs new instance. Creates controller and receiver properties.
*/
constructor() {
super();
this.controller = new Controller();
this._active = false;
this._initialized = false;
this.controller.on(RemoteControlEvents.ACTIVE_CHANGED, active => {
this.active = active;
});
}
/**
* Sets the remote control session active status.
*
* @param {boolean} isActive - True - if the controller or the receiver is
* currently in remote control session and false otherwise.
* @returns {void}
*/
set active(isActive: boolean) {
this._active = isActive;
this.emit(RemoteControlEvents.ACTIVE_CHANGED, isActive);
}
/**
* Returns the remote control session active status.
*
* @returns {boolean} - True - if the controller or the receiver is
* currently in remote control session and false otherwise.
*/
get active(): boolean {
return this._active;
}
/**
* Initializes the remote control - checks if the remote control should be
* enabled or not.
*
* @returns {void}
*/
init() {
if (config.disableRemoteControl
|| this._initialized
|| !APP.conference.isDesktopSharingEnabled) {
return;
}
logger.log('Initializing remote control.');
this._initialized = true;
this.controller.enable(true);
this.receiver = new Receiver();
this.receiver.on(RemoteControlEvents.ACTIVE_CHANGED, active => {
this.active = active;
});
}
/**
* Checks whether the passed user supports remote control or not.
*
* @param {JitsiParticipant} user - The user to be tested.
* @returns {Promise<boolean>} The promise will be resolved with true if
* the user supports remote control and with false if not.
*/
checkUserRemoteControlSupport(user: Object) {
return user.getFeatures()
.then(features => features.has(DISCO_REMOTE_CONTROL_FEATURE));
}
}
export default new RemoteControl();

View File

@@ -0,0 +1,72 @@
/* @flow */
import EventEmitter from 'events';
import { getLogger } from 'jitsi-meet-logger';
import {
REMOTE_CONTROL_MESSAGE_NAME
} from '../../service/remotecontrol/Constants';
const logger = getLogger(__filename);
declare var APP: Object;
/**
* Implements common logic for Receiver class and Controller class.
*/
export default class RemoteControlParticipant extends EventEmitter {
_enabled: boolean;
/**
* Creates new instance.
*/
constructor() {
super();
this._enabled = false;
}
/**
* Enables / Disables the remote control.
*
* @param {boolean} enabled - The new state.
* @returns {void}
*/
enable(enabled: boolean) {
this._enabled = enabled;
}
/**
* Sends remote control message to other participant trough data channel.
*
* @param {string} to - The participant who will receive the event.
* @param {RemoteControlEvent} event - The remote control event.
* @param {Function} onDataChannelFail - Handler for data channel failure.
* @returns {void}
*/
sendRemoteControlEndpointMessage(
to: ?string,
event: Object,
onDataChannelFail: ?Function) {
if (!this._enabled || !to) {
logger.warn(
'Remote control: Skip sending remote control event. Params:',
this.enable,
to);
return;
}
try {
APP.conference.sendEndpointMessage(to, {
name: REMOTE_CONTROL_MESSAGE_NAME,
...event
});
} catch (e) {
logger.error(
'Failed to send EndpointMessage via the datachannels',
e);
if (typeof onDataChannelFail === 'function') {
onDataChannelFail(e);
}
}
}
}

399
package-lock.json generated
View File

@@ -4833,6 +4833,11 @@
"@xtuc/long": "4.2.2"
}
},
"@webcomponents/url": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@webcomponents/url/-/url-0.7.1.tgz",
"integrity": "sha512-9oFDpuZ+tAogjPYQPhNEX86Npzb73A4kv9DOPsSO9aWoWgoevoP6eKx+TKMwO8BJxtTpSM9nKenHQTJ56SP2Cw=="
},
"@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -4922,9 +4927,9 @@
"dev": true
},
"amplitude-js": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-7.3.1.tgz",
"integrity": "sha512-dsJU9MdtDDAOtKnbHrJuVBgsL5UGxD1P2B7doGdAQ1hxxT/5mFrmJTFzi1tKe+2ir3QtcRa9B0qvH8TMsGw22A==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-7.1.1.tgz",
"integrity": "sha512-grEQf0p4V/q4aIcGYdGEJ6EquBXu91R/RorsYTQvh9O6sxjpwHf5vSDICQJq7twEElBrSHoSF77GUvC9ZTBj4A==",
"requires": {
"@amplitude/ua-parser-js": "0.7.24",
"blueimp-md5": "^2.10.0",
@@ -6405,7 +6410,8 @@
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true
},
"chrome-trace-event": {
"version": "1.0.2",
@@ -7144,17 +7150,6 @@
}
}
},
"css-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
"integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==",
"requires": {
"boolbase": "^1.0.0",
"css-what": "^3.2.1",
"domutils": "^1.7.0",
"nth-check": "^1.0.2"
}
},
"css-to-react-native": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz",
@@ -7165,27 +7160,6 @@
"postcss-value-parser": "^3.3.0"
}
},
"css-tree": {
"version": "1.0.0-alpha.39",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz",
"integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==",
"requires": {
"mdn-data": "2.0.6",
"source-map": "^0.6.1"
},
"dependencies": {
"mdn-data": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz",
"integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"css-what": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz",
@@ -8171,11 +8145,6 @@
}
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"esquery": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
@@ -8766,11 +8735,6 @@
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.3.tgz",
"integrity": "sha512-EU6ePgEauhWrzJEN5RtG1d1ayrWXhEnfzTjnieHj+jG9tNHDEhKTAnCn1TN3gs9h6XWCDH6cpeX1VXY/lzLwZg=="
},
"focus-visible": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.1.0.tgz",
"integrity": "sha512-nPer0rjtzdZ7csVIu233P2cUm/ks/4aVSI+5KUkYrYpgA7ujgC3p6J7FtFU+AIMWwnwYQOB/yeiOITxFeYIXiw=="
},
"follow-redirects": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz",
@@ -8854,15 +8818,6 @@
"universalify": "^0.1.0"
}
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"optional": true,
"requires": {
"minipass": "^2.6.0"
}
},
"fs-write-stream-atomic": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
@@ -8934,6 +8889,12 @@
"concat-map": "0.0.1"
}
},
"chownr": {
"version": "1.1.1",
"resolved": false,
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": false,
@@ -8985,6 +8946,15 @@
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"resolved": false,
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"optional": true,
"requires": {
"minipass": "^2.2.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": false,
@@ -9091,13 +9061,38 @@
"brace-expansion": "^1.1.7"
}
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"minimist": {
"version": "0.0.8",
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"optional": true
},
"minipass": {
"version": "2.3.5",
"resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"optional": true,
"requires": {
"minimist": "^1.2.5"
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.2.1",
"resolved": false,
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"optional": true,
"requires": {
"minipass": "^2.2.1"
}
},
"mkdirp": {
"version": "0.5.1",
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"optional": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
@@ -9241,9 +9236,9 @@
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"version": "1.2.0",
"resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"optional": true
}
}
@@ -9343,6 +9338,21 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"optional": true
},
"tar": {
"version": "4.4.8",
"resolved": false,
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": false,
@@ -9363,6 +9373,12 @@
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
},
"yallist": {
"version": "3.0.3",
"resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"optional": true
}
}
},
@@ -10621,15 +10637,6 @@
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"js-yaml": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsc-android": {
"version": "245459.0.0",
"resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-245459.0.0.tgz",
@@ -10771,8 +10778,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#d2153eb404ddadef6d5b89ae8c499fa144280531",
"from": "github:jitsi/lib-jitsi-meet#d2153eb404ddadef6d5b89ae8c499fa144280531",
"version": "github:jitsi/lib-jitsi-meet#0cffc064e644ad87ff381cc6c4df1c1a9f2c73ff",
"from": "github:jitsi/lib-jitsi-meet#0cffc064e644ad87ff381cc6c4df1c1a9f2c73ff",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "1.0.3",
@@ -11879,39 +11886,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"optional": true
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"optional": true
}
}
},
"minizlib": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"optional": true,
"requires": {
"minipass": "^2.9.0"
}
},
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -12125,9 +12099,9 @@
}
},
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
"integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
"dev": true
},
"node-int64": {
@@ -12585,8 +12559,8 @@
"dev": true
},
"olm": {
"version": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"integrity": "sha512-B87bTpGIGieuV2FNauChjjQtVltwTGagQFoHm+3Dcse4amKAAGJB/I54dnP/JtbHZ+RYVoApM2OQ46Z4VH6eNg=="
"version": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
"integrity": "sha512-kumW7B+xWMdiGSU0BrECOd+9GnhvsnnHP6qTHGPIcHTL2F0m8sYlP08hkEpN7uX/TlnHCwqpkaZXPQ0GYtVe8A=="
},
"on-finished": {
"version": "2.3.0",
@@ -12909,7 +12883,8 @@
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
"dev": true
},
"path-exists": {
"version": "3.0.0",
@@ -12964,6 +12939,11 @@
"sha.js": "^2.4.8"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
@@ -13478,6 +13458,14 @@
"integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
"dev": true
},
"raf": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz",
"integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==",
"requires": {
"performance-now": "^2.1.0"
}
},
"raf-schd": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-2.1.2.tgz",
@@ -13738,8 +13726,8 @@
}
},
"react-native": {
"version": "github:jitsi/react-native#891986ec5ecaef65d1c8a7fe472f86cf84fe7551",
"from": "github:jitsi/react-native#891986ec5ecaef65d1c8a7fe472f86cf84fe7551",
"version": "github:jitsi/react-native#efd2aff5661d75a230e36406b698cfe0ee545be2",
"from": "github:jitsi/react-native#efd2aff5661d75a230e36406b698cfe0ee545be2",
"requires": {
"@babel/runtime": "^7.0.0",
"@react-native-community/cli": "^3.0.0",
@@ -14158,92 +14146,20 @@
"version": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190",
"from": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190"
},
"react-native-splash-screen": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz",
"integrity": "sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg=="
},
"react-native-svg": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-10.1.0.tgz",
"integrity": "sha512-mgo6CshQIQrDDBVUPqJK/iOsJEdlagk7N4q8fyo1sqCiSUP2efpt+AQ1IRXZtHXut210/7TliAamvM59NV0Bzg==",
"requires": {
"css-select": "^2.1.0",
"css-tree": "^1.0.0-alpha.39"
}
"version": "9.7.1",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-9.7.1.tgz",
"integrity": "sha512-Yr54SyLPCdovLCJ08V7syJUe1iKrTYG9V5wB08z6lh/9FdC2R9CtBnMyz83GDLKfzUONqqH9nN1l+o61CgD3tg=="
},
"react-native-svg-transformer": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-0.14.3.tgz",
"integrity": "sha512-agDGdMeeBAsWEgg/u7mjtR2Z3c8smGCLep/n3svwifut9dpswZCP+bSIrU8ekg6RNtxAJL+eGJbWjJ38vWxw6g==",
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-0.13.0.tgz",
"integrity": "sha512-tfcnIDC2Q6FN8+g/BXPootZtb+sWLzKJde3o5jSuUJdXSmKwwSazAbk+V808n/Ez5kd2arzsuPTKONT66qx5Xw==",
"requires": {
"@svgr/core": "^4.3.3",
"@svgr/plugin-svgo": "^4.3.1",
"path-dirname": "^1.0.2",
"@svgr/core": "^4.1.0",
"semver": "^5.6.0"
},
"dependencies": {
"@svgr/babel-plugin-svg-dynamic-title": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz",
"integrity": "sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w=="
},
"@svgr/babel-preset": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.3.tgz",
"integrity": "sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A==",
"requires": {
"@svgr/babel-plugin-add-jsx-attribute": "^4.2.0",
"@svgr/babel-plugin-remove-jsx-attribute": "^4.2.0",
"@svgr/babel-plugin-remove-jsx-empty-expression": "^4.2.0",
"@svgr/babel-plugin-replace-jsx-attribute-value": "^4.2.0",
"@svgr/babel-plugin-svg-dynamic-title": "^4.3.3",
"@svgr/babel-plugin-svg-em-dimensions": "^4.2.0",
"@svgr/babel-plugin-transform-react-native-svg": "^4.2.0",
"@svgr/babel-plugin-transform-svg-component": "^4.2.0"
}
},
"@svgr/core": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.3.tgz",
"integrity": "sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w==",
"requires": {
"@svgr/plugin-jsx": "^4.3.3",
"camelcase": "^5.3.1",
"cosmiconfig": "^5.2.1"
}
},
"@svgr/plugin-jsx": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz",
"integrity": "sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w==",
"requires": {
"@babel/core": "^7.4.5",
"@svgr/babel-preset": "^4.3.3",
"@svgr/hast-util-to-babel-ast": "^4.3.2",
"svg-parser": "^2.0.0"
}
},
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.13.1",
"parse-json": "^4.0.0"
}
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -14251,12 +14167,14 @@
}
}
},
"react-native-url-polyfill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.2.0.tgz",
"integrity": "sha512-hpLZ8RyS3oGVyTOe/HjoqVoCOSkeJvrCoEB3bJsY7t9uh7kpQDV6kgvdlECEafYpxe3RzMrKLVcmWRbPU7CuAw==",
"react-native-swipeout": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz",
"integrity": "sha512-t9suUCspzck4vp2pWggWe0frS/QOtX6yYCawHnEes75A7dZCEE74bxX2A1bQzGH9cUMjq6xsdfC94RbiDKIkJg==",
"requires": {
"whatwg-url-without-unicode": "8.0.0-3"
"create-react-class": "^15.6.0",
"prop-types": "^15.5.10",
"react-tween-state": "^0.1.5"
}
},
"react-native-watch-connectivity": {
@@ -14265,9 +14183,9 @@
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
},
"react-native-webrtc": {
"version": "1.84.1",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.84.1.tgz",
"integrity": "sha512-ewZBgKE+YhLaivo9Wh6aiaEp8ZRvFMqblrkDl1nptQiNNH6CungoAzSOxGDnHWAxepRfiUrW5qnADrsYKmaNeQ==",
"version": "1.84.0",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.84.0.tgz",
"integrity": "sha512-xPOFbrcehuBzLnFy3keCM2HyMsyCVDQjQNAn8SIHKH/PA8Q7kZ4spuytc2E1hBTr7zH/vQ2Px+DWqu7on12jag==",
"requires": {
"base64-js": "^1.1.2",
"event-target-shim": "^1.0.5",
@@ -14433,6 +14351,15 @@
}
}
},
"react-tween-state": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/react-tween-state/-/react-tween-state-0.1.5.tgz",
"integrity": "sha1-6YsGZVHvuTy5LdG+FJlcLj3q4zk=",
"requires": {
"raf": "^3.1.0",
"tween-functions": "^1.0.1"
}
},
"react-uid": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/react-uid/-/react-uid-2.2.0.tgz",
@@ -15013,12 +14940,12 @@
"dev": true
},
"selfsigned": {
"version": "1.10.8",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz",
"integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==",
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
"integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
"dev": true,
"requires": {
"node-forge": "^0.10.0"
"node-forge": "0.9.0"
}
},
"semver": {
@@ -16337,35 +16264,6 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
"tar": {
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"optional": true
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"optional": true
}
}
},
"temp": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
@@ -16757,6 +16655,11 @@
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
"dev": true
},
"tween-functions": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz",
"integrity": "sha1-GuOlDnxguz3vd06scHrLynO7w/8="
},
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@@ -17232,11 +17135,6 @@
"defaults": "^1.0.3"
}
},
"webidl-conversions": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
"integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="
},
"webpack": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz",
@@ -18549,27 +18447,6 @@
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
"integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
},
"whatwg-url-without-unicode": {
"version": "8.0.0-3",
"resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz",
"integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==",
"requires": {
"buffer": "^5.4.3",
"punycode": "^2.1.1",
"webidl-conversions": "^5.0.0"
},
"dependencies": {
"buffer": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
}
}
},
"which": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",

View File

@@ -40,11 +40,11 @@
"@svgr/webpack": "4.3.2",
"@tensorflow-models/body-pix": "2.0.4",
"@tensorflow/tfjs": "1.5.1",
"amplitude-js": "7.3.1",
"@webcomponents/url": "0.7.1",
"amplitude-js": "7.1.1",
"base64-js": "1.3.1",
"bc-css-flags": "3.0.0",
"dropbox": "4.0.9",
"focus-visible": "5.1.0",
"i18n-iso-countries": "3.7.8",
"i18next": "17.0.6",
"i18next-browser-languagedetector": "3.0.1",
@@ -56,12 +56,12 @@
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#d2153eb404ddadef6d5b89ae8c499fa144280531",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#0cffc064e644ad87ff381cc6c4df1c1a9f2c73ff",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.19",
"moment": "2.19.4",
"moment-duration-format": "2.2.2",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
"pixelmatch": "5.1.0",
"punycode": "2.1.1",
"react": "16.9",
@@ -69,7 +69,7 @@
"react-emoji-render": "1.2.4",
"react-i18next": "10.11.4",
"react-linkify": "1.0.0-alpha",
"react-native": "github:jitsi/react-native#891986ec5ecaef65d1c8a7fe472f86cf84fe7551",
"react-native": "github:jitsi/react-native#efd2aff5661d75a230e36406b698cfe0ee545be2",
"react-native-background-timer": "2.4.0",
"react-native-calendar-events": "github:jitsi/react-native-calendar-events#df48ecdc4e1e90c5352f803ddbab1fa7269b74a7",
"react-native-callstats": "3.61.0",
@@ -79,12 +79,11 @@
"react-native-keep-awake": "4.0.0",
"react-native-linear-gradient": "2.5.6",
"react-native-sound": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190",
"react-native-splash-screen": "3.2.0",
"react-native-svg": "10.1.0",
"react-native-svg-transformer": "0.14.3",
"react-native-url-polyfill": "1.2.0",
"react-native-svg": "9.7.1",
"react-native-svg-transformer": "0.13.0",
"react-native-swipeout": "2.3.6",
"react-native-watch-connectivity": "0.4.3",
"react-native-webrtc": "1.84.1",
"react-native-webrtc": "1.84.0",
"react-native-webview": "10.9.0",
"react-native-youtube-iframe": "1.2.3",
"react-redux": "7.1.0",
@@ -92,7 +91,7 @@
"react-transition-group": "2.4.0",
"redux": "4.0.4",
"redux-thunk": "2.2.0",
"rnnoise-wasm": "github:jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
"rnnoise-wasm": "github:jitsi/rnnoise-wasm.git#566a16885897704d6e6d67a1d5ac5d39781db2af",
"rtcstats": "github:jitsi/rtcstats#v6.2.0",
"stackblur-canvas": "2.3.0",
"styled-components": "3.4.9",

View File

@@ -1,88 +0,0 @@
/*
Copyright 2015, 2019, 2020 Google LLC. All Rights Reserved.
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.
*/
const CACHE_NAME = 'offline';
// Customize this with a different URL if needed.
const OFFLINE_URL = 'static/offline.html';
self.addEventListener('install', event => {
event.waitUntil(
(async () => {
const cache = await caches.open(CACHE_NAME);
// Setting {cache: 'reload'} in the new request will ensure that the
// response isn't fulfilled from the HTTP cache; i.e., it will be from
// the network.
await cache.add(new Request(OFFLINE_URL, { cache: 'reload' }));
})()
);
// Force the waiting service worker to become the active service worker.
self.skipWaiting();
});
self.addEventListener('activate', event => {
event.waitUntil(
(async () => {
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable();
}
})()
);
// Tell the active service worker to take control of the page immediately.
self.clients.claim();
});
self.addEventListener('fetch', event => {
// We only want to call event.respondWith() if this is a navigation request
// for an HTML page.
if (event.request.mode === 'navigate') {
event.respondWith((async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
// Always try the network first.
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetch() returns a valid HTTP response with a response code in
// the 4xx or 5xx range, the catch() will NOT be called.
console.log('Fetch failed; returning offline page instead.', error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse;
}
})());
}
// If our if() condition is false, then this fetch handler won't intercept the
// request. If there are any other fetch handlers registered, they will get a
// chance to call event.respondWith(). If no fetch handlers call
// event.respondWith(), the request will be handled by the browser as if there
// were no service worker involvement.
});

View File

@@ -772,22 +772,6 @@ export function createTrackMutedEvent(mediaType, reason, muted = true) {
};
}
/**
* Creates an event for joining a vpaas conference.
*
* @param {string} tenant - The conference tenant.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createVpaasConferenceJoinedEvent(tenant) {
return {
action: 'vpaas.conference.joined',
attributes: {
tenant
}
};
}
/**
* Creates an event for an action on the welcome page.
*

View File

@@ -16,7 +16,7 @@ import { connect, disconnect, setLocationURL } from '../base/connection';
import { loadConfig } from '../base/lib-jitsi-meet';
import { MEDIA_TYPE } from '../base/media';
import { toState } from '../base/redux';
import { createDesiredLocalTracks, isLocalCameraTrackMuted, isLocalTrackMuted } from '../base/tracks';
import { createDesiredLocalTracks, isLocalVideoTrackMuted, isLocalTrackMuted } from '../base/tracks';
import {
addHashParamsToURL,
getBackendSafeRoomName,
@@ -232,7 +232,7 @@ export function reloadNow() {
function addTrackStateToURL(url, stateful) {
const state = toState(stateful);
const tracks = state['features/base/tracks'];
const isVideoMuted = isLocalCameraTrackMuted(tracks);
const isVideoMuted = isLocalVideoTrackMuted(tracks);
const isAudioMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO);
return addHashParamsToURL(new URL(url), { // use new URL object in order to not pollute the passed parameter.
@@ -298,13 +298,13 @@ export function maybeRedirectToWelcomePage(options: Object = {}) {
return;
}
const { jwt } = getState()['features/base/jwt'];
const { isGuest, jwt } = getState()['features/base/jwt'];
let hashParam;
// save whether current user is guest or not, and pass auth token,
// before navigating to close page
window.sessionStorage.setItem('guest', !jwt);
window.sessionStorage.setItem('guest', isGuest);
window.sessionStorage.setItem('jwt', jwt);
let path = 'close.html';

View File

@@ -1,7 +1,6 @@
// @flow
import React from 'react';
import SplashScreen from 'react-native-splash-screen';
import { setColorScheme } from '../../base/color-scheme';
import { DialogContainer } from '../../base/dialog';
@@ -85,8 +84,6 @@ export class App extends AbstractApp {
componentDidMount() {
super.componentDidMount();
SplashScreen.hide();
this._init.then(() => {
const { dispatch, getState } = this.state.store;

View File

@@ -37,7 +37,6 @@ import '../overlay/middleware';
import '../recent-list/middleware';
import '../recording/middleware';
import '../rejoin/middleware';
import '../remote-control/middleware';
import '../room-lock/middleware';
import '../rtcstats/middleware';
import '../subtitles/middleware';

View File

@@ -24,7 +24,6 @@ import '../base/sounds/reducer';
import '../base/testing/reducer';
import '../base/tracks/reducer';
import '../base/user-interaction/reducer';
import '../billing-counter/reducer';
import '../blur/reducer';
import '../calendar-sync/reducer';
import '../chat/reducer';
@@ -43,7 +42,6 @@ import '../notifications/reducer';
import '../overlay/reducer';
import '../recent-list/reducer';
import '../recording/reducer';
import '../remote-control/reducer';
import '../settings/reducer';
import '../subtitles/reducer';
import '../toolbox/reducer';

View File

@@ -37,11 +37,6 @@ export type Props = {
*/
displayName?: string,
/**
* Whether or not to update the background color of the avatar
*/
dynamicColor?: Boolean,
/**
* ID of the element, if any.
*/
@@ -83,15 +78,6 @@ export const DEFAULT_SIZE = 65;
* Implements a class to render avatars in the app.
*/
class Avatar<P: Props> extends PureComponent<P, State> {
/**
* Default values for {@code Avatar} component's properties.
*
* @static
*/
static defaultProps = {
dynamicColor: true
};
/**
* Instantiates a new {@code Component}.
*
@@ -137,7 +123,6 @@ class Avatar<P: Props> extends PureComponent<P, State> {
_loadableAvatarUrl,
className,
colorBase,
dynamicColor,
id,
size,
status,
@@ -171,10 +156,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
const initials = getInitials(_initialsBase);
if (initials) {
if (dynamicColor) {
avatarProps.color = getAvatarColor(colorBase || _initialsBase);
}
avatarProps.color = getAvatarColor(colorBase || _initialsBase);
avatarProps.initials = initials;
}

View File

@@ -140,6 +140,18 @@ export const P2P_STATUS_CHANGED = 'P2P_STATUS_CHANGED';
*/
export const SEND_TONES = 'SEND_TONES';
/**
* The type of (redux) action which sets the desktop sharing enabled flag for
* the current conference.
*
* {
* type: SET_DESKTOP_SHARING_ENABLED,
* desktopSharingEnabled: boolean
* }
*/
export const SET_DESKTOP_SHARING_ENABLED
= 'SET_DESKTOP_SHARING_ENABLED';
/**
* The type of (redux) action which updates the current known status of the
* Follow Me feature.

View File

@@ -43,6 +43,7 @@ import {
LOCK_STATE_CHANGED,
P2P_STATUS_CHANGED,
SEND_TONES,
SET_DESKTOP_SHARING_ENABLED,
SET_FOLLOW_ME,
SET_PASSWORD,
SET_PASSWORD_FAILED,
@@ -51,6 +52,7 @@ import {
SET_START_MUTED_POLICY
} from './actionTypes';
import {
AVATAR_ID_COMMAND,
AVATAR_URL_COMMAND,
EMAIL_COMMAND,
JITSI_CONFERENCE_URL_KEY
@@ -196,6 +198,13 @@ function _addConferenceListeners(conference, dispatch) {
botType
})));
conference.addCommandListener(
AVATAR_ID_COMMAND,
(data, id) => dispatch(participantUpdated({
conference,
id,
avatarID: data.value
})));
conference.addCommandListener(
AVATAR_URL_COMMAND,
(data, id) => dispatch(participantUpdated({
@@ -572,6 +581,22 @@ export function sendTones(tones: string, duration: number, pause: number) {
};
}
/**
* Sets the flag for indicating if desktop sharing is enabled.
*
* @param {boolean} desktopSharingEnabled - True if desktop sharing is enabled.
* @returns {{
* type: SET_DESKTOP_SHARING_ENABLED,
* desktopSharingEnabled: boolean
* }}
*/
export function setDesktopSharingEnabled(desktopSharingEnabled: boolean) {
return {
type: SET_DESKTOP_SHARING_ENABLED,
desktopSharingEnabled
};
}
/**
* Enables or disables the Follow Me feature.
*

View File

@@ -1,3 +1,10 @@
/**
* The command type for updating a participant's avatar ID.
*
* @type {string}
*/
export const AVATAR_ID_COMMAND = 'avatar-id';
/**
* The command type for updating a participant's avatar URL.
*

View File

@@ -14,6 +14,7 @@ import { toState } from '../redux';
import { safeDecodeURIComponent } from '../util';
import {
AVATAR_ID_COMMAND,
AVATAR_URL_COMMAND,
EMAIL_COMMAND,
JITSI_CONFERENCE_URL_KEY
@@ -73,7 +74,6 @@ export function commonUserJoinedHandling(
} else {
dispatch(participantJoined({
botType: user.getBotType(),
connectionStatus: user.getConnectionStatus(),
conference,
id,
name: displayName,
@@ -316,12 +316,16 @@ export function sendLocalParticipant(
setDisplayName: Function,
setLocalParticipantProperty: Function }) {
const {
avatarID,
avatarURL,
email,
features,
name
} = getLocalParticipant(stateful);
avatarID && conference.sendCommand(AVATAR_ID_COMMAND, {
value: avatarID
});
avatarURL && conference.sendCommand(AVATAR_URL_COMMAND, {
value: avatarURL
});

Some files were not shown because too many files have changed in this diff Show More