mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-25 09:27:48 +00:00
Compare commits
1 Commits
6951
...
token-veri
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a49b6140e0 |
25
.github/workflows/ci-lua.yml
vendored
25
.github/workflows/ci-lua.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Lua CI
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
luacheck:
|
||||
name: Luacheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install luarocks
|
||||
run: sudo apt-get --install-recommends -y install luarocks
|
||||
|
||||
- name: Install luacheck
|
||||
run: sudo luarocks install luacheck
|
||||
|
||||
- name: Check lua codes
|
||||
run: |
|
||||
set -o pipefail && luacheck . | awk -F: '
|
||||
{
|
||||
print $0
|
||||
printf "::warning file=%s,line=%s,col=%s::%s\n", $1, $2, $3, $4
|
||||
}
|
||||
'
|
||||
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
@@ -20,11 +20,12 @@ jobs:
|
||||
- name: Check if the git repository is clean
|
||||
run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1)
|
||||
- run: npm run lint:ci
|
||||
- run: for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done
|
||||
linux-build:
|
||||
name: Build Frontend (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
@@ -35,7 +36,7 @@ jobs:
|
||||
name: Build Frontend (macOS)
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
global = false
|
||||
unused = false
|
||||
redefined = false
|
||||
ignore = { "581" }
|
||||
max_line_length = false
|
||||
color = false
|
||||
formatter = "plain"
|
||||
quiet = 1
|
||||
@@ -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 android:scaleX="0.75" android:scaleY="0.75" android:translateX="35" android:translateY="35">
|
||||
<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>
|
||||
@@ -2,5 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -2,5 +2,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -81,8 +81,6 @@ public class JitsiMeetView extends FrameLayout {
|
||||
result.putBoolean(key, (Boolean)bValue);
|
||||
} else if (valueType.contentEquals("String")) {
|
||||
result.putString(key, (String)bValue);
|
||||
} else if (valueType.contentEquals("Integer")) {
|
||||
result.putInt(key, (int)bValue);
|
||||
} else if (valueType.contentEquals("Bundle")) {
|
||||
result.putBundle(key, mergeProps((Bundle)aValue, (Bundle)bValue));
|
||||
} else {
|
||||
|
||||
@@ -73,6 +73,7 @@ class ReactInstanceManagerHolder {
|
||||
new SplashScreenModule(reactContext),
|
||||
new PictureInPictureModule(reactContext),
|
||||
new ProximityModule(reactContext),
|
||||
new WiFiStatsModule(reactContext),
|
||||
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
|
||||
|
||||
if (AudioModeModule.useConnectionService()) {
|
||||
@@ -123,7 +124,7 @@ class ReactInstanceManagerHolder {
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
|
||||
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
|
||||
new org.linusu.RNGetRandomValuesPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.swmansion.rnscreens.RNScreensPackage(),
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Module exposing WiFi statistics.
|
||||
*
|
||||
* Gathers rssi, signal in percentage, timestamp and the addresses of the wifi
|
||||
* device.
|
||||
*/
|
||||
@ReactModule(name = WiFiStatsModule.NAME)
|
||||
class WiFiStatsModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "WiFiStats";
|
||||
|
||||
/**
|
||||
* The {@code Log} tag {@code WiFiStatsModule} is to log messages with.
|
||||
*/
|
||||
static final String TAG = NAME;
|
||||
|
||||
/**
|
||||
* The scale used for the signal value. A level of the signal, given in the
|
||||
* range of 0 to SIGNAL_LEVEL_SCALE-1 (both inclusive).
|
||||
*/
|
||||
public final static int SIGNAL_LEVEL_SCALE = 101;
|
||||
|
||||
/**
|
||||
* {@link ExecutorService} for running all operations on a dedicated thread.
|
||||
*/
|
||||
private static final ExecutorService executor
|
||||
= Executors.newSingleThreadExecutor();
|
||||
|
||||
/**
|
||||
* Initializes a new module instance. There shall be a single instance of
|
||||
* this module throughout the lifetime of the application.
|
||||
*
|
||||
* @param reactContext the {@link ReactApplicationContext} where this module
|
||||
* is created.
|
||||
*/
|
||||
public WiFiStatsModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name for this module to be used in the React Native bridge.
|
||||
*
|
||||
* @return a string with the module name.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link InetAddress} represented by this int.
|
||||
*
|
||||
* @param value the int representation of the ip address.
|
||||
* @return the {@link InetAddress}.
|
||||
* @throws UnknownHostException - if IP address is of illegal length.
|
||||
*/
|
||||
public static InetAddress toInetAddress(int value)
|
||||
throws UnknownHostException {
|
||||
return InetAddress.getByAddress(
|
||||
new byte[] {
|
||||
(byte) value,
|
||||
(byte) (value >> 8),
|
||||
(byte) (value >> 16),
|
||||
(byte) (value >> 24)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to retrieve WiFi stats.
|
||||
*
|
||||
* @param promise a {@link Promise} which will be resolved if WiFi stats are
|
||||
* retrieved successfully, and it will be rejected otherwise.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void getWiFiStats(final Promise promise) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Context context
|
||||
= getReactApplicationContext().getApplicationContext();
|
||||
WifiManager wifiManager
|
||||
= (WifiManager) context
|
||||
.getSystemService(Context.WIFI_SERVICE);
|
||||
|
||||
if (!wifiManager.isWifiEnabled()) {
|
||||
promise.reject(new Exception("Wifi not enabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||
|
||||
if (wifiInfo.getNetworkId() == -1) {
|
||||
promise.reject(new Exception("Wifi not connected"));
|
||||
return;
|
||||
}
|
||||
|
||||
int rssi = wifiInfo.getRssi();
|
||||
int signalLevel
|
||||
= WifiManager.calculateSignalLevel(
|
||||
rssi, SIGNAL_LEVEL_SCALE);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("rssi", rssi)
|
||||
.put("signal", signalLevel)
|
||||
.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
JSONArray addresses = new JSONArray();
|
||||
|
||||
InetAddress wifiAddress
|
||||
= toInetAddress(wifiInfo.getIpAddress());
|
||||
|
||||
try {
|
||||
Enumeration<NetworkInterface> e
|
||||
= NetworkInterface.getNetworkInterfaces();
|
||||
while (e.hasMoreElements()) {
|
||||
NetworkInterface networkInterface = e.nextElement();
|
||||
boolean found = false;
|
||||
|
||||
// first check whether this is the desired interface
|
||||
Enumeration<InetAddress> as
|
||||
= networkInterface.getInetAddresses();
|
||||
while (as.hasMoreElements()) {
|
||||
InetAddress a = as.nextElement();
|
||||
if(a.equals(wifiAddress)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
// interface found let's put addresses
|
||||
// to the result object
|
||||
as = networkInterface.getInetAddresses();
|
||||
while (as.hasMoreElements()) {
|
||||
InetAddress a = as.nextElement();
|
||||
if (a.isLinkLocalAddress())
|
||||
continue;
|
||||
|
||||
addresses.put(a.getHostAddress());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
JitsiMeetLogger.e(e, TAG + " Unable to NetworkInterface.getNetworkInterfaces()");
|
||||
}
|
||||
|
||||
result.put("addresses", addresses);
|
||||
promise.resolve(result.toString());
|
||||
|
||||
JitsiMeetLogger.d(TAG + " WiFi stats: " + result.toString());
|
||||
} catch (Throwable e) {
|
||||
JitsiMeetLogger.e(e, TAG + " Failed to obtain wifi stats");
|
||||
promise.reject(
|
||||
new Exception("Failed to obtain wifi stats"));
|
||||
}
|
||||
}
|
||||
};
|
||||
executor.execute(r);
|
||||
}
|
||||
}
|
||||
147
conference.js
147
conference.js
@@ -103,7 +103,6 @@ import {
|
||||
participantMutedUs,
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantSourcesUpdated,
|
||||
participantUpdated,
|
||||
screenshareParticipantDisplayNameChanged,
|
||||
updateRemoteParticipantFeatures
|
||||
@@ -1988,11 +1987,6 @@ export default {
|
||||
APP.store.dispatch(participantKicked(kicker, kicked));
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.PARTICIPANT_SOURCE_UPDATED,
|
||||
jitsiParticipant => {
|
||||
APP.store.dispatch(participantSourcesUpdated(jitsiParticipant));
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.SUSPEND_DETECTED, () => {
|
||||
APP.store.dispatch(suspendDetected());
|
||||
});
|
||||
@@ -2348,7 +2342,7 @@ export default {
|
||||
* @param {MediaDeviceInfo[]} devices
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async _onDeviceListChanged(devices) {
|
||||
_onDeviceListChanged(devices) {
|
||||
const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
@@ -2362,10 +2356,13 @@ export default {
|
||||
const newDevices
|
||||
= mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
|
||||
devices,
|
||||
this.isSharingScreen,
|
||||
localVideo,
|
||||
localAudio,
|
||||
newLabelsOnly);
|
||||
const promises = [];
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
const requestedInput = {
|
||||
audio: Boolean(newDevices.audioinput),
|
||||
video: Boolean(newDevices.videoinput)
|
||||
@@ -2377,6 +2374,7 @@ export default {
|
||||
= setAudioOutputDeviceId(newDevices.audiooutput, dispatch)
|
||||
.catch(); // Just ignore any errors in catch block.
|
||||
|
||||
|
||||
promises.push(setAudioOutputPromise);
|
||||
}
|
||||
|
||||
@@ -2393,7 +2391,8 @@ export default {
|
||||
}
|
||||
|
||||
// Let's handle unknown/non-preferred devices
|
||||
const newAvailDevices = APP.store.getState()['features/base/devices'].availableDevices;
|
||||
const newAvailDevices
|
||||
= APP.store.getState()['features/base/devices'].availableDevices;
|
||||
let newAudioDevices = [];
|
||||
let oldAudioDevices = [];
|
||||
|
||||
@@ -2409,85 +2408,103 @@ export default {
|
||||
|
||||
// check for audio
|
||||
if (newAudioDevices.length > 0) {
|
||||
APP.store.dispatch(checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
|
||||
}
|
||||
|
||||
// check for video
|
||||
if (!requestedInput.video) {
|
||||
APP.store.dispatch(checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
|
||||
}
|
||||
|
||||
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of 'default'
|
||||
// in order to get the correct MediaStreamTrack from chrome because of the following bug.
|
||||
// When the 'default' mic needs to be selected, we need to
|
||||
// pass the real device id to gUM instead of 'default' in order
|
||||
// to get the correct MediaStreamTrack from chrome because of the
|
||||
// following bug.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689
|
||||
const hasDefaultMicChanged = newDevices.audioinput === 'default';
|
||||
|
||||
// When the local video is muted and a preferred device is connected, update the settings and remove the track
|
||||
// from the conference. A new track will be created and replaced when the user unmutes their camera.
|
||||
// This is the case when the local video is muted and a preferred device is connected.
|
||||
if (requestedInput.video && this.isLocalVideoMuted()) {
|
||||
APP.store.dispatch(updateSettings({
|
||||
// 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
|
||||
}));
|
||||
requestedInput.video = false;
|
||||
delete newDevices.videoinput;
|
||||
|
||||
// Remove the track from the conference.
|
||||
if (localVideo) {
|
||||
await this.useVideoStream(null);
|
||||
logger.debug('_onDeviceListChanged: Removed the current video track.');
|
||||
}
|
||||
// Removing the current video track in order to force the unmute to select the preferred device.
|
||||
logger.debug('_onDeviceListChanged: Removing the current video track.');
|
||||
this.useVideoStream(null);
|
||||
|
||||
}
|
||||
|
||||
// When the local audio is muted and a preferred device is connected, update the settings and remove the track
|
||||
// from the conference. A new track will be created and replaced when the user unmutes their mic.
|
||||
if (requestedInput.audio && this.isLocalAudioMuted()) {
|
||||
APP.store.dispatch(updateSettings({
|
||||
micDeviceId: newDevices.audioinput
|
||||
}));
|
||||
requestedInput.audio = false;
|
||||
delete newDevices.audioinput;
|
||||
|
||||
// Remove the track from the conference.
|
||||
if (localAudio) {
|
||||
await this.useAudioStream(null);
|
||||
logger.debug('_onDeviceListChanged: Removed the current audio track.');
|
||||
}
|
||||
}
|
||||
|
||||
// Create the tracks and replace them only if the user is unmuted.
|
||||
if (requestedInput.audio || requestedInput.video) {
|
||||
let tracks = [];
|
||||
|
||||
try {
|
||||
tracks = await mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
|
||||
promises.push(
|
||||
mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
|
||||
createLocalTracksF,
|
||||
newDevices.videoinput,
|
||||
hasDefaultMicChanged
|
||||
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
||||
: newDevices.audioinput);
|
||||
} catch (error) {
|
||||
logger.error(`Track creation failed on device change, ${error}`);
|
||||
: newDevices.audioinput)
|
||||
.then(tracks => {
|
||||
// If audio or video muted before, or we unplugged current
|
||||
// device and selected new one, then mute new track.
|
||||
const muteSyncPromises = tracks.map(track => {
|
||||
if ((track.isVideoTrack() && videoWasMuted)
|
||||
|| (track.isAudioTrack() && audioWasMuted)) {
|
||||
return track.mute();
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
for (const track of tracks) {
|
||||
if (track.isAudioTrack()) {
|
||||
promises.push(
|
||||
this.useAudioStream(track)
|
||||
.then(() => {
|
||||
hasDefaultMicChanged && (track._realDeviceId = track.deviceId = 'default');
|
||||
this._updateAudioDeviceId();
|
||||
}));
|
||||
} else {
|
||||
promises.push(
|
||||
this.useVideoStream(track)
|
||||
.then(() => {
|
||||
this._updateVideoDeviceId();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(muteSyncPromises)
|
||||
.then(() =>
|
||||
Promise.all(Object.keys(requestedInput).map(mediaType => {
|
||||
if (requestedInput[mediaType]) {
|
||||
const useStream
|
||||
= mediaType === 'audio'
|
||||
? this.useAudioStream.bind(this)
|
||||
: this.useVideoStream.bind(this);
|
||||
const track = tracks.find(t => t.getType() === mediaType) || null;
|
||||
|
||||
// Use the new stream or null if we failed to obtain it.
|
||||
return useStream(track)
|
||||
.then(() => {
|
||||
if (track?.isAudioTrack() && hasDefaultMicChanged) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of
|
||||
// the above mentioned chrome bug.
|
||||
track._realDeviceId = track.deviceId = 'default';
|
||||
}
|
||||
mediaType === 'audio'
|
||||
? this._updateAudioDeviceId()
|
||||
: this._updateVideoDeviceId();
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
})));
|
||||
})
|
||||
.then(() => {
|
||||
// Log and sync known mute state.
|
||||
if (audioWasMuted) {
|
||||
sendAnalytics(createTrackMutedEvent(
|
||||
'audio',
|
||||
'device list changed'));
|
||||
logger.log('Audio mute: device list changed');
|
||||
muteLocalAudio(true);
|
||||
}
|
||||
|
||||
if (!this.isSharingScreen && videoWasMuted) {
|
||||
sendAnalytics(createTrackMutedEvent(
|
||||
'video',
|
||||
'device list changed'));
|
||||
logger.log('Video mute: device list changed');
|
||||
muteLocalVideo(true);
|
||||
}
|
||||
}));
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(() => {
|
||||
|
||||
92
config.js
92
config.js
@@ -79,6 +79,11 @@ var config = {
|
||||
// This is useful when the client runs on a host with limited resources.
|
||||
// noAutoPlayVideo: false,
|
||||
|
||||
// Whether to use fake constraints (height: 99999, width: 99999) when calling getDisplayMedia on
|
||||
// Chromium based browsers. This is intended as a workaround for
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1056311
|
||||
// setScreenSharingResolutionConstraints: true,
|
||||
|
||||
// Enable callstats only for a percentage of users.
|
||||
// This takes a value between 0 and 100 which determines the probability for
|
||||
// the callstats to be enabled.
|
||||
@@ -257,6 +262,17 @@ var config = {
|
||||
// applied locally. FIXME: having these 2 options is confusing.
|
||||
// startWithVideoMuted: false,
|
||||
|
||||
// If set to true, prefer to use the H.264 video codec (if supported).
|
||||
// Note that it's not recommended to do this because simulcast is not
|
||||
// supported when using H.264. For 1-to-1 calls this setting is enabled by
|
||||
// default and can be toggled in the p2p section.
|
||||
// This option has been deprecated, use preferredCodec under videoQuality section instead.
|
||||
// preferH264: true,
|
||||
|
||||
// If set to true, disable H.264 video codec by stripping it out of the
|
||||
// SDP.
|
||||
// disableH264: false,
|
||||
|
||||
// Desktop sharing
|
||||
|
||||
// Optional desktop sharing frame rate options. Default value: min:5, max:5.
|
||||
@@ -418,6 +434,12 @@ var config = {
|
||||
// 90: 2,
|
||||
// },
|
||||
|
||||
// Provides a way to translate the legacy bridge signaling messages, 'LastNChangedEvent',
|
||||
// 'SelectedEndpointsChangedEvent' and 'ReceiverVideoConstraint' into the new 'ReceiverVideoConstraints' message
|
||||
// that invokes the new bandwidth allocation algorithm in the bridge which is described here
|
||||
// - https://github.com/jitsi/jitsi-videobridge/blob/master/doc/allocation.md.
|
||||
// useNewBandwidthAllocationStrategy: false,
|
||||
|
||||
// Specify the settings for video quality optimizations on the client.
|
||||
// videoQuality: {
|
||||
// // Provides a way to prevent a video codec from being negotiated on the JVB connection. The codec specified
|
||||
@@ -901,10 +923,18 @@ var config = {
|
||||
// If not set, the effective value is 'all'.
|
||||
// iceTransportPolicy: 'all',
|
||||
|
||||
// If set to true, it will prefer to use H.264 for P2P calls (if H.264
|
||||
// is supported). This setting is deprecated, use preferredCodec instead.
|
||||
// preferH264: true,
|
||||
|
||||
// Provides a way to set the video codec preference on the p2p connection. Acceptable
|
||||
// codec values are 'VP8', 'VP9' and 'H264'.
|
||||
// preferredCodec: 'H264',
|
||||
|
||||
// If set to true, disable H.264 video codec by stripping it out of the
|
||||
// SDP. This setting is deprecated, use disabledCodec instead.
|
||||
// disableH264: false,
|
||||
|
||||
// Provides a way to prevent a video codec from being negotiated on the p2p connection.
|
||||
// disabledCodec: '',
|
||||
|
||||
@@ -1061,64 +1091,10 @@ var config = {
|
||||
// use only.
|
||||
// _desktopSharingSourceDevice: 'sample-id-or-label',
|
||||
|
||||
// DEPRECATED! Use deeplinking.disabled instead.
|
||||
// If true, any checks to handoff to another application will be prevented
|
||||
// and instead the app will continue to display in the current browser.
|
||||
// disableDeepLinking: false,
|
||||
|
||||
// The deeplinking config.
|
||||
// For information about the properties of
|
||||
// deeplinking.[ios/android].dynamicLink check:
|
||||
// https://firebase.google.com/docs/dynamic-links/create-manually
|
||||
// deeplinking: {
|
||||
//
|
||||
// // The desktop deeplinking config.
|
||||
// desktop: {
|
||||
// appName: 'Jitsi Meet'
|
||||
// },
|
||||
// // If true, any checks to handoff to another application will be prevented
|
||||
// // and instead the app will continue to display in the current browser.
|
||||
// disabled: false,
|
||||
|
||||
// // whether to hide the logo on the deep linking pages.
|
||||
// hideLogo: false,
|
||||
|
||||
// // The ios deeplinking config.
|
||||
// ios: {
|
||||
// appName: 'Jitsi Meet',
|
||||
// // Specify mobile app scheme for opening the app from the mobile browser.
|
||||
// appScheme: 'org.jitsi.meet',
|
||||
// // Custom URL for downloading ios mobile app.
|
||||
// downloadLink: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
|
||||
// dynamicLink: {
|
||||
// apn: 'org.jitsi.meet',
|
||||
// appCode: 'w2atb',
|
||||
// customDomain: undefined,
|
||||
// ibi: 'com.atlassian.JitsiMeet.ios',
|
||||
// isi: '1165103905'
|
||||
// }
|
||||
// },
|
||||
|
||||
// // The android deeplinking config.
|
||||
// android: {
|
||||
// appName: 'Jitsi Meet',
|
||||
// // Specify mobile app scheme for opening the app from the mobile browser.
|
||||
// appScheme: 'org.jitsi.meet',
|
||||
// // Custom URL for downloading android mobile app.
|
||||
// downloadLink: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
|
||||
// // Android app package name.
|
||||
// appPackage: 'org.jitsi.meet',
|
||||
// fDroidUrl: 'https://f-droid.org/en/packages/org.jitsi.meet/',
|
||||
// dynamicLink: {
|
||||
// apn: 'org.jitsi.meet',
|
||||
// appCode: 'w2atb',
|
||||
// customDomain: undefined,
|
||||
// ibi: 'com.atlassian.JitsiMeet.ios',
|
||||
// isi: '1165103905'
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
|
||||
// A property to disable the right click context menu for localVideo
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false,
|
||||
@@ -1515,8 +1491,6 @@ var config = {
|
||||
// tileTime: 5000,
|
||||
// // Limit results by rating: g, pg, pg-13, r. Default value: g.
|
||||
// rating: 'pg',
|
||||
// // The proxy server url for giphy requests in the web app.
|
||||
// proxyUrl: 'https://giphy-proxy.example.com',
|
||||
// },
|
||||
|
||||
// Logging
|
||||
@@ -1546,12 +1520,6 @@ var config = {
|
||||
// },
|
||||
};
|
||||
|
||||
// Temporary backwards compatibility with old mobile clients.
|
||||
config.flags = config.flags || {};
|
||||
config.flags.sourceNameSignaling = true;
|
||||
config.flags.sendMultipleVideoStreams = true;
|
||||
config.flags.receiveMultipleVideoStreams = true;
|
||||
|
||||
// Set the default values for JaaS customers
|
||||
if (enableJaaS) {
|
||||
config.dialInNumbersUrl = 'https://conference-mapper.jitsi.net/v1/access/dids';
|
||||
|
||||
@@ -126,12 +126,6 @@ form {
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.leftwatermarknomargin {
|
||||
background-position: center left;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.rightwatermark {
|
||||
right: 32px;
|
||||
top: 32px;
|
||||
|
||||
126
css/_chat.scss
126
css/_chat.scss
@@ -32,7 +32,7 @@
|
||||
|
||||
#chat-conversation-container {
|
||||
// extract message input height
|
||||
height: calc(100% - 64px);
|
||||
height: calc(100% - 68px);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
@@ -76,6 +76,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
#chat-recipient {
|
||||
align-items: center;
|
||||
background-color: $chatPrivateMessageBackgroundColor;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: 100;
|
||||
padding: 10px;
|
||||
|
||||
span {
|
||||
color: white;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
div {
|
||||
svg {
|
||||
cursor: pointer;
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
&.lobby-chat-recipient {
|
||||
background-color: $chatLobbyMessageBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chat-header {
|
||||
height: 70px;
|
||||
@@ -98,12 +124,13 @@
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
padding: 0 16px 24px;
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
|
||||
#chat-input {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -236,6 +263,15 @@
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
@@ -252,11 +288,24 @@
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
margin-top: 3px;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
|
||||
&.localuser {
|
||||
background-color: $chatLocalMessageBackgroundColor;
|
||||
border-radius: 6px 0px 6px 6px;
|
||||
}
|
||||
|
||||
.usermessage {
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-radius: 0px;
|
||||
|
||||
@@ -271,12 +320,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.privatemessagenotice {
|
||||
font-size: 11px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.messagecontent {
|
||||
margin: 8px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
#smileys {
|
||||
font-size: 20pt;
|
||||
margin: auto;
|
||||
@@ -350,9 +409,24 @@
|
||||
}
|
||||
|
||||
.chat-message-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.local {
|
||||
align-items: flex-end;
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatLocalMessageBackgroundColor;
|
||||
border-radius: 6px 0px 6px 6px;
|
||||
|
||||
&.privatemessage {
|
||||
background-color: $chatPrivateMessageBackgroundColor;
|
||||
}
|
||||
&.lobbymessage {
|
||||
background-color: $chatLobbyMessageBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
@@ -363,10 +437,58 @@
|
||||
}
|
||||
|
||||
&.error {
|
||||
.chatmessage {
|
||||
background-color: $defaultWarningColor;
|
||||
border-radius: 0px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chatmessage-wrapper {
|
||||
max-width: 100%;
|
||||
|
||||
.replywrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.messageactions {
|
||||
align-self: stretch;
|
||||
border-left: 1px solid $chatActionsSeparatorColor;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 5px;
|
||||
|
||||
&.lobbychatmessageactions {
|
||||
border-left-color: $chatLobbyActionsSeparatorColor;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
display: inline-block;
|
||||
margin-top: 3px;
|
||||
color: white;
|
||||
|
||||
&.privatemessage {
|
||||
background-color: $chatPrivateMessageBackgroundColor;
|
||||
}
|
||||
&.lobbymessage {
|
||||
background-color: $chatLobbyMessageBackgroundColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-dialog {
|
||||
|
||||
350
css/_polls.scss
350
css/_polls.scss
@@ -1,3 +1,353 @@
|
||||
.poll-dialog {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
|
||||
h1, span, li, strong {
|
||||
color: #bce;
|
||||
}
|
||||
ol {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question-field {
|
||||
padding: 8px 16px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #525252;
|
||||
}
|
||||
|
||||
.poll-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-creator {
|
||||
color: #C2C2C2;
|
||||
font-weight: 600;
|
||||
margin: 4px 0 16px 0;
|
||||
}
|
||||
|
||||
.poll-answer-container {
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
background: #3D3D3D;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@media (max-width: 580px) {
|
||||
&> span {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.poll-answer-field-list, .poll-answer-list, .poll-result-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.poll-answer-field-list {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
ol.poll-result-list {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.poll-result-list > li {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.poll-answer-field {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: 16;
|
||||
|
||||
}
|
||||
|
||||
.poll-answer-field:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.poll-create-option-row {
|
||||
display: flex;
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
// Needed to override atlaskit default blue color
|
||||
.poll-create-container .jsYMHu {
|
||||
background: #292929;
|
||||
border-color: #808090;
|
||||
color: #fff // #808090
|
||||
}
|
||||
|
||||
.poll-add-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.poll-remove-option-button {
|
||||
background: 0 0;
|
||||
border: none;
|
||||
color: #E04757;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.poll-create-add-option {
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.poll-icon-button, .poll-drag-handle {
|
||||
.jitsi-icon svg {
|
||||
fill: #929292;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-drag-handle {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: grab;
|
||||
padding-left: 8;
|
||||
padding-top: 8px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-question {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.poll-answer-voters {
|
||||
font-weight: lighter;
|
||||
list-style-type: none;
|
||||
border: #616161 solid 1px;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
margin: 4px 0px 12px;
|
||||
background-color: #616161;
|
||||
}
|
||||
|
||||
.poll-answer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.poll-answer-vote-name {
|
||||
flex-shrink: 1;
|
||||
overflow-wrap: anywhere
|
||||
}
|
||||
|
||||
.poll-answer-vote-count-container{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-answer-vote-count {
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.poll-answer-short-results{
|
||||
display: flex;
|
||||
min-width: 10em;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.poll-bar-container, .poll-bar {
|
||||
border-radius: 3px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.poll-bar-container {
|
||||
background-color: #616161;
|
||||
max-width: 160px;
|
||||
margin-top: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.poll-bar {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
.poll-message-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.poll-notice {
|
||||
font-weight: 100;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.poll-show-details {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-result-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
a.poll-detail-link, a.poll-change-vote-link {
|
||||
color: #669AEC;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: #669AEC;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: #669AEC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.polls-pane-content {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pane-content{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty-pane-icon {
|
||||
width: 50%;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.empty-pane-icon svg {
|
||||
fill: #3D3D3D;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.empty-pane-message {
|
||||
color: #fff;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
.poll-results, .poll-answer {
|
||||
background: #292929;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #666666;
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.poll-results {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.poll-answer {
|
||||
|
||||
h1, strong ,span {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button > span {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-create-label {
|
||||
color: #C2C2C2;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
.expandable-input{
|
||||
line-height: 18px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border: 1px solid #666666;
|
||||
background-color: #141414;
|
||||
color: #FFF;
|
||||
border-radius: 6px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
#polls-panel {
|
||||
height: calc(100% - 119px);
|
||||
}
|
||||
|
||||
.poll-container {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
height: calc(100% - 88px);
|
||||
line-height: 20px;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
|
||||
& > * + *:not(.ignore-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
}
|
||||
|
||||
.poll-create-header {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin: 20px 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.poll-create-container {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.poll-create-footer {
|
||||
background-color: #141414;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: calc(100% - 32px);
|
||||
}
|
||||
|
||||
.poll-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
|
||||
.poll-answer-footer {
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin-left: 16px;
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
&.space-top {
|
||||
@@ -83,7 +82,7 @@
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
.jitsi-content-recording-icon-container-without-switch {
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-tabs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header-text-title {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -52,6 +56,13 @@
|
||||
.welcome-footer-row-block {
|
||||
display: block;
|
||||
}
|
||||
.welcome-badge {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.welcome-footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
min-width: 34px;
|
||||
padding: 0 8px;
|
||||
height: 28px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
|
||||
@media (max-width: 300px) {
|
||||
display: none;
|
||||
|
||||
@@ -79,6 +79,7 @@ $modalTextColor: #333;
|
||||
$chatActionsSeparatorColor: rgb(173, 105, 112);
|
||||
$chatBackgroundColor: #131519;
|
||||
$chatInputSeparatorColor: #A4B8D1;
|
||||
$chatLobbyMessageBackgroundColor: #6A50D3;
|
||||
$chatLobbyActionsSeparatorColor: #6A50D3;
|
||||
$chatLocalMessageBackgroundColor: #484A4F;
|
||||
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
|
||||
@@ -169,9 +170,8 @@ $welcomePageHeaderPaddingBottom: 0px;
|
||||
$welcomePageHeaderTitleMaxWidth: initial;
|
||||
$welcomePageHeaderTextAlign: center;
|
||||
|
||||
$welcomePageHeaderContainerMarginTop: 104px;
|
||||
$welcomePageHeaderContainerDisplay: flex;
|
||||
$welcomePageHeaderContainerMargin: $welcomePageHeaderContainerMarginTop auto 0;
|
||||
$welcomePageHeaderContainerMargin: 104px 32px 0 32px;
|
||||
|
||||
$welcomePageHeaderTextTitleMarginBottom: 0;
|
||||
$welcomePageHeaderTextTitleFontSize: 42px;
|
||||
|
||||
@@ -29,16 +29,6 @@ body.welcome-page {
|
||||
flex-direction: column;
|
||||
margin: $welcomePageHeaderContainerMargin;
|
||||
z-index: $zindex2;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
max-width: 688px;
|
||||
}
|
||||
|
||||
.header-watermark-container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: calc(20px - #{$welcomePageHeaderContainerMarginTop});
|
||||
}
|
||||
|
||||
.header-text-title {
|
||||
@@ -133,11 +123,16 @@ body.welcome-page {
|
||||
max-width: calc(100% - 40px);
|
||||
padding: 16px 0 39px 0;
|
||||
width: $welcomePageEnterRoomWidth;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 600;
|
||||
p {
|
||||
color: $welcomePageDescriptionColor;
|
||||
float: left;
|
||||
text-align: $welcomePageHeaderTextAlign;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,8 +200,8 @@ body.welcome-page {
|
||||
color: $welcomePageDescriptionColor;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
top: calc(35px - #{$welcomePageHeaderContainerMarginTop});
|
||||
right: 0;
|
||||
top: 32px;
|
||||
right: 32px;
|
||||
z-index: $zindex2;
|
||||
|
||||
* {
|
||||
@@ -229,11 +224,6 @@ body.welcome-page {
|
||||
width: $welcomePageWatermarkWidth;
|
||||
height: $welcomePageWatermarkHeight;
|
||||
}
|
||||
|
||||
.watermark.leftwatermarknomargin {
|
||||
width: $welcomePageWatermarkWidth;
|
||||
height: $welcomePageWatermarkHeight;
|
||||
}
|
||||
}
|
||||
|
||||
&.without-content {
|
||||
@@ -252,17 +242,10 @@ body.welcome-page {
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.welcome-card-column {
|
||||
.welcome-card-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 688px;
|
||||
margin: auto;
|
||||
|
||||
> div {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
.welcome-card-text {
|
||||
@@ -270,7 +253,7 @@ body.welcome-page {
|
||||
}
|
||||
|
||||
.welcome-card {
|
||||
width: 100%;
|
||||
width: 49%;
|
||||
border-radius: 8px;
|
||||
|
||||
&--dark {
|
||||
@@ -285,6 +268,10 @@ body.welcome-page {
|
||||
&--grey {
|
||||
background: #F2F3F4;
|
||||
}
|
||||
|
||||
&--shadow {
|
||||
box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-footer {
|
||||
|
||||
@@ -41,27 +41,20 @@
|
||||
&-dropdown-btns {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
|
||||
&-dropdown-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
|
||||
/**
|
||||
* Override default InlineDialog behaviour, since it does not play nicely with relative widths
|
||||
*/
|
||||
& > div:nth-child(2) {
|
||||
background: #E0E0E0;
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
position: absolute !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-input {
|
||||
margin-bottom: 16px;
|
||||
|
||||
& input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
.premeeting-screen {
|
||||
.premeeting-screen {
|
||||
background: #292929;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
font-size: 1.3em;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.action-btn {
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
@@ -65,38 +75,129 @@
|
||||
}
|
||||
}
|
||||
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
.content {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
margin: 0 30px;
|
||||
padding: 24px 0 16px;
|
||||
position: relative;
|
||||
transition: none;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.toolbox-content {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
&-controls {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015;
|
||||
line-height: 36px;
|
||||
margin-bottom: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input.field {
|
||||
background-color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 16px;
|
||||
color: #1C2025;
|
||||
padding: 10px 16px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&.error {
|
||||
border: 1px solid #E04757;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white;
|
||||
}
|
||||
}
|
||||
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
position: relative;
|
||||
transition: none;
|
||||
|
||||
.toolbox-content {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// mobile phone landscape
|
||||
@media (max-height: 420px) {
|
||||
div.content {
|
||||
padding: 16px 16px 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.content {
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
|
||||
&-controls {
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.device-status-error {
|
||||
border-radius: 0;
|
||||
margin: 0 -16px;
|
||||
}
|
||||
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
7
debian/jitsi-meet-prosody.postinst
vendored
7
debian/jitsi-meet-prosody.postinst
vendored
@@ -44,7 +44,12 @@ case "$1" in
|
||||
fi
|
||||
JVB_SECRET="$RET"
|
||||
|
||||
JICOFO_AUTH_USER="focus"
|
||||
db_get jicofo/jicofo-authuser
|
||||
if [ -z "$RET" ] ; then
|
||||
db_input critical jicofo/jicofo-authuser || true
|
||||
db_go
|
||||
fi
|
||||
JICOFO_AUTH_USER="$RET"
|
||||
|
||||
db_get jicofo/jicofo-authpassword
|
||||
if [ -z "$RET" ] ; then
|
||||
|
||||
6
debian/jitsi-meet-prosody.templates
vendored
6
debian/jitsi-meet-prosody.templates
vendored
@@ -13,6 +13,12 @@ Type: password
|
||||
_Description: Jitsi Videobridge Component secret:
|
||||
The secret used by Jitsi Videobridge to connect to xmpp server as component.
|
||||
|
||||
Template: jicofo/jicofo-authuser
|
||||
Type: string
|
||||
Default: focus
|
||||
_Description: Jicofo username:
|
||||
The jicofo needs an authenticated admin user to connect to xmpp server.
|
||||
|
||||
Template: jicofo/jicofo-authpassword
|
||||
Type: password
|
||||
_Description: Jicofo user password:
|
||||
|
||||
5
debian/jitsi-meet-web-config.postinst
vendored
5
debian/jitsi-meet-web-config.postinst
vendored
@@ -176,10 +176,9 @@ case "$1" in
|
||||
fi
|
||||
|
||||
# Fixes multi-stream flags to workaround problem with mobile joining a multi-stream call with multi-stream disabled
|
||||
FIX_MSG="// Temporary backwards compatibility with old mobile clients."
|
||||
FIX_MSG="//Enables multi-stream, do not delete me"
|
||||
if ! grep -q "^${FIX_MSG}" $JITSI_MEET_CONFIG; then
|
||||
echo $FIX_MSG >> $JITSI_MEET_CONFIG
|
||||
echo "config.flags = config.flags || {};" >> $JITSI_MEET_CONFIG
|
||||
sed -i "s#config.flags.sourceNameSignaling#${FIX_MSG}\nconfig.flags = config.flags || {};\nconfig.flags.sourceNameSignaling#g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
if ! grep -q "^config.flags.sourceNameSignaling*" $JITSI_MEET_CONFIG; then
|
||||
echo "config.flags.sourceNameSignaling = true;" >> $JITSI_MEET_CONFIG
|
||||
|
||||
@@ -76,6 +76,11 @@ var interfaceConfig = {
|
||||
|
||||
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
|
||||
|
||||
/**
|
||||
* Hide the logo on the deep linking pages.
|
||||
*/
|
||||
HIDE_DEEP_LINKING_LOGO: false,
|
||||
|
||||
/**
|
||||
* Hide the invite prompt in the header when alone in the meeting.
|
||||
*/
|
||||
@@ -103,6 +108,23 @@ 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
|
||||
// has a suboptimal experience. Browsers which are not listed as optimal or
|
||||
// unsupported are considered suboptimal. Valid values are:
|
||||
@@ -137,6 +159,7 @@ var interfaceConfig = {
|
||||
*/
|
||||
SHOW_CHROME_EXTENSION_BANNER: false,
|
||||
|
||||
SHOW_DEEP_LINKING_IMAGE: false,
|
||||
SHOW_JITSI_WATERMARK: true,
|
||||
SHOW_POWERED_BY: false,
|
||||
SHOW_PROMOTIONAL_CLOSE_PAGE: false,
|
||||
@@ -177,31 +200,6 @@ var interfaceConfig = {
|
||||
*/
|
||||
// TILE_VIEW_MAX_COLUMNS: 5,
|
||||
|
||||
// List of undocumented settings
|
||||
/**
|
||||
INDICATOR_FONT_SIZES
|
||||
PHONE_NUMBER_REGEX
|
||||
*/
|
||||
|
||||
// -----------------DEPRECATED CONFIGS BELOW THIS LINE-----------------------------
|
||||
|
||||
/**
|
||||
* Specify URL for downloading ios mobile app.
|
||||
*/
|
||||
// MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
|
||||
|
||||
/**
|
||||
* Specify custom URL for downloading android mobile app.
|
||||
*/
|
||||
// MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet',
|
||||
|
||||
/**
|
||||
* Specify mobile app scheme for opening the app from the mobile browser.
|
||||
*/
|
||||
// APP_SCHEME: 'org.jitsi.meet',
|
||||
|
||||
// NATIVE_APP_NAME: 'Jitsi Meet',
|
||||
|
||||
/**
|
||||
* Specify Firebase dynamic link properties for the mobile apps.
|
||||
*/
|
||||
@@ -214,19 +212,22 @@ var interfaceConfig = {
|
||||
// },
|
||||
|
||||
/**
|
||||
* Hide the logo on the deep linking pages.
|
||||
* Specify mobile app scheme for opening the app from the mobile browser.
|
||||
*/
|
||||
// HIDE_DEEP_LINKING_LOGO: false,
|
||||
// APP_SCHEME: 'org.jitsi.meet',
|
||||
|
||||
/**
|
||||
* Specify the Android app package name.
|
||||
*/
|
||||
// ANDROID_APP_PACKAGE: 'org.jitsi.meet',
|
||||
|
||||
// List of undocumented settings
|
||||
/**
|
||||
* Specify custom URL for downloading f droid app.
|
||||
*/
|
||||
// MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/',
|
||||
INDICATOR_FONT_SIZES
|
||||
PHONE_NUMBER_REGEX
|
||||
*/
|
||||
|
||||
// -----------------DEPRECATED CONFIGS BELOW THIS LINE-----------------------------
|
||||
|
||||
// Connection indicators (
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
|
||||
@@ -373,12 +373,8 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-performance (2.1.0):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (4.4.1):
|
||||
- RCT-Folly
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- react-native-safe-area-context (3.3.2):
|
||||
- React-Core
|
||||
- ReactCommon/turbomodule/core
|
||||
- react-native-slider (4.1.12):
|
||||
- React-Core
|
||||
- react-native-splash-screen (3.3.0):
|
||||
@@ -389,7 +385,7 @@ PODS:
|
||||
- react-native-video/Video (6.0.0-alpha.1):
|
||||
- PromisesSwift
|
||||
- React-Core
|
||||
- react-native-webrtc (106.0.4):
|
||||
- react-native-webrtc (106.0.0):
|
||||
- JitsiWebRTC (~> 106.0.0)
|
||||
- React-Core
|
||||
- react-native-webview (11.15.1):
|
||||
@@ -471,7 +467,7 @@ PODS:
|
||||
- React-Core
|
||||
- RNDeviceInfo (8.4.8):
|
||||
- React-Core
|
||||
- RNGestureHandler (2.8.0):
|
||||
- RNGestureHandler (2.1.0):
|
||||
- React-Core
|
||||
- RNGoogleSignin (7.0.4):
|
||||
- GoogleSignIn (~> 6.0.0)
|
||||
@@ -755,11 +751,11 @@ SPEC CHECKSUMS:
|
||||
react-native-orientation-locker: 851f6510d8046ea2f14aa169b1e01fcd309a94ba
|
||||
react-native-pager-view: 3ee7d4c7697fb3ef788346e834a60cca97ed8540
|
||||
react-native-performance: f4b6604a9d5a8a7407e34a82fab6c641d9a3ec12
|
||||
react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
|
||||
react-native-safe-area-context: 584dc04881deb49474363f3be89e4ca0e854c057
|
||||
react-native-slider: 6e9b86e76cce4b9e35b3403193a6432ed07e0c81
|
||||
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
|
||||
react-native-video: bb6f12a7198db53b261fefb5d609dc77417acc8b
|
||||
react-native-webrtc: 4522d420ead45fff83c4ecc7e5a706797857a185
|
||||
react-native-webrtc: 0a407105bf428c9157f2e8d4d6f7c844dc185933
|
||||
react-native-webview: ea4899a1056c782afa96dd082179a66cbebf5504
|
||||
React-perflogger: 0458a87ea9a7342079e7a31b0d32b3734fb8415f
|
||||
React-RCTActionSheet: 22538001ea2926dea001111dd2846c13a0730bc9
|
||||
@@ -779,7 +775,7 @@ SPEC CHECKSUMS:
|
||||
RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd
|
||||
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
|
||||
RNDeviceInfo: 0400a6d0c94186d1120c3cbd97b23abc022187a9
|
||||
RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3
|
||||
RNGestureHandler: e5c7cab5f214503dcefd6b2b0cefb050e1f51c4a
|
||||
RNGoogleSignin: c4381751eefd73c552b923ba347a9bfc6f18771c
|
||||
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
||||
RNSound: 27e8268bdb0a1f191f219a33267f7e0445e8d62f
|
||||
|
||||
@@ -147,7 +147,6 @@
|
||||
"bridgeCount": "Serverzahl: ",
|
||||
"codecs": "Codecs (A/V): ",
|
||||
"connectedTo": "Verbunden mit:",
|
||||
"e2eeVerified": "E2EE verifiziert:",
|
||||
"framerate": "Bildwiederholrate:",
|
||||
"less": "Weniger anzeigen",
|
||||
"localaddress": "Lokale Adresse:",
|
||||
@@ -409,10 +408,6 @@
|
||||
"user": "Anmeldename",
|
||||
"userIdentifier": "Benutzername",
|
||||
"userPassword": "Passwort",
|
||||
"verifyParticipantConfirm": "Sie stimmen überein",
|
||||
"verifyParticipantDismiss": "Sie stimmen nicht überein",
|
||||
"verifyParticipantQuestion": "EXPERIMENTELL: Frage Person {{participantName}} ob sie den selben Inhalt in der selben Reihenfolge sieht.",
|
||||
"verifyParticipantTitle": "Personsverifikation",
|
||||
"videoLink": "Video-Link",
|
||||
"viewUpgradeOptions": "Upgradeoptionen anzeigen",
|
||||
"viewUpgradeOptionsContent": "Sie müssen Ihren Tarif erweitern, um Premium-Features wie Aufnahme, Transkription, RTMP-Streaming und mehr zu nutzen.",
|
||||
@@ -442,6 +437,9 @@
|
||||
"noResults": "Keine Ergebnisse :(",
|
||||
"search": "GIPHY durchsuchen"
|
||||
},
|
||||
"helpView": {
|
||||
"title": "Hilfecenter"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "Antworten",
|
||||
"audioCallTitle": "Eingehender Anruf",
|
||||
@@ -565,6 +563,7 @@
|
||||
"lobby": {
|
||||
"admit": "Zulassen",
|
||||
"admitAll": "Alle zulassen",
|
||||
"allow": "Annehmen",
|
||||
"backToKnockModeButton": "Kein Passwort, stattdessen Beitritt anfragen",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Lobbymodus",
|
||||
@@ -650,8 +649,6 @@
|
||||
"connectedOneMember": "{{name}} nimmt am Meeting teil",
|
||||
"connectedThreePlusMembers": "{{name}} und {{count}} andere Personen nehmen am Meeting teil",
|
||||
"connectedTwoMembers": "{{first}} und {{second}} nehmen am Meeting teil",
|
||||
"dataChannelClosed": "Schlechte Videoqualität",
|
||||
"dataChannelClosedDescription": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher ist die Videoqulität auf die schlechteste Stufe limitiert.",
|
||||
"disconnected": "getrennt",
|
||||
"displayNotifications": "Benachrichtigungen anzeigen für",
|
||||
"focus": "Konferenzleitung",
|
||||
@@ -712,8 +709,6 @@
|
||||
"reactionSoundsForAll": "Interaktionstöne für alle deaktivieren",
|
||||
"screenShareNoAudio": "Die Option \"Audio freigeben\" wurde bei der Auswahl des Fensters nicht ausgewählt.",
|
||||
"screenShareNoAudioTitle": "Share audio was not checked",
|
||||
"screenSharingAudioOnlyDescription": "Durch die Bildschirmfreigabe wird der Modus \"Beste Leistung\" beeinflusst und daher mehr Datenrate benötigt.",
|
||||
"screenSharingAudioOnlyTitle": "Modus \"Beste Leistung\"",
|
||||
"selfViewTitle": "Sie können die eigene Ansicht immer in den Einstellungen reaktivieren",
|
||||
"somebody": "Jemand",
|
||||
"startSilentDescription": "Treten Sie dem Meeting noch einmal bei, um Ihr Audio zu aktivieren",
|
||||
@@ -863,6 +858,9 @@
|
||||
"rejected": "Abgelehnt",
|
||||
"ringing": "Es klingelt …"
|
||||
},
|
||||
"privacyView": {
|
||||
"title": "Datenschutz"
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "Benutzerbild",
|
||||
"setDisplayNameLabel": "Anzeigename festlegen",
|
||||
@@ -1005,7 +1003,6 @@
|
||||
"displayName": "Anzeigename",
|
||||
"displayNamePlaceholderText": "z.B. Erika Musterfrau",
|
||||
"email": "E-Mail",
|
||||
"emailPlaceholderText": "email@beispiel.de",
|
||||
"goTo": "Gehe zu",
|
||||
"header": "Einstellungen",
|
||||
"help": "Hilfe",
|
||||
@@ -1014,7 +1011,6 @@
|
||||
"profileSection": "Profil",
|
||||
"serverURL": "Server-URL",
|
||||
"showAdvanced": "Erweiterte Einstellungen anzeigen",
|
||||
"startCarModeInLowBandwidthMode": "Automodus mit Datensparmodus starten",
|
||||
"startWithAudioMuted": "Stumm beitreten",
|
||||
"startWithVideoMuted": "Ohne Video beitreten",
|
||||
"terms": "Nutzungsbedingungen",
|
||||
@@ -1294,7 +1290,6 @@
|
||||
"show": "Im Vordergrund anzeigen",
|
||||
"showSelfView": "Eigene Ansicht anzeigen",
|
||||
"unpinFromStage": "Lösen",
|
||||
"verify": "Person verifizieren",
|
||||
"videoMuted": "Kamera ausgeschaltet",
|
||||
"videomute": "Person hat die Kamera angehalten"
|
||||
},
|
||||
@@ -1362,7 +1357,6 @@
|
||||
"recentList": "Verlauf",
|
||||
"recentListDelete": "Eintrag löschen",
|
||||
"recentListEmpty": "Ihr Konferenzverlauf ist derzeit leer. Reden Sie mit Ihrem Team und Ihre vergangenen Konferenzen landen hier.",
|
||||
"recentMeetings": "Ihre letzten Konferenzen",
|
||||
"reducedUIText": "Willkommen bei {{app}}!",
|
||||
"roomNameAllowedChars": "Der Konferenzname sollte keines der folgenden Zeichen enthalten: ?, &, :, ', \", %, #.",
|
||||
"roomname": "Konferenzname eingeben",
|
||||
@@ -1371,7 +1365,6 @@
|
||||
"settings": "Einstellungen",
|
||||
"startMeeting": "Meeting starten",
|
||||
"terms": "AGB",
|
||||
"title": "Sichere, voll funktionale und komplett kostenlose Videokonferenzen",
|
||||
"upcomingMeetings": "Ihre zukünftigen Konferenzen"
|
||||
"title": "Sichere, voll funktionale und komplett kostenlose Videokonferenzen"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -442,6 +442,9 @@
|
||||
"noResults": "No results found :(",
|
||||
"search": "Search GIPHY"
|
||||
},
|
||||
"helpView": {
|
||||
"title": "Help center"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "Answer",
|
||||
"audioCallTitle": "Incoming call",
|
||||
@@ -712,8 +715,6 @@
|
||||
"reactionSoundsForAll": "Disable sounds for all",
|
||||
"screenShareNoAudio": "Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudioTitle": "Couldn't share system audio!",
|
||||
"screenSharingAudioOnlyDescription": "Please note that by sharing your screen you're affecting the \"Best performance\" mode and you will use more bandwidth.",
|
||||
"screenSharingAudioOnlyTitle": "\"Best performance\" mode",
|
||||
"selfViewTitle": "You can always un-hide the self-view from settings",
|
||||
"somebody": "Somebody",
|
||||
"startSilentDescription": "Rejoin the meeting to enable audio",
|
||||
@@ -863,6 +864,9 @@
|
||||
"rejected": "Rejected",
|
||||
"ringing": "Ringing..."
|
||||
},
|
||||
"privacyView": {
|
||||
"title": "Privacy"
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "avatar",
|
||||
"setDisplayNameLabel": "Set your display name",
|
||||
@@ -1362,7 +1366,6 @@
|
||||
"recentList": "Recent",
|
||||
"recentListDelete": "Delete entry",
|
||||
"recentListEmpty": "Your recent list is currently empty. Chat with your team and you will find all your recent meetings here.",
|
||||
"recentMeetings": "Your recent meetings",
|
||||
"reducedUIText": "Welcome to {{app}}!",
|
||||
"roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.",
|
||||
"roomname": "Enter room name",
|
||||
@@ -1371,7 +1374,6 @@
|
||||
"settings": "Settings",
|
||||
"startMeeting": "Start meeting",
|
||||
"terms": "Terms",
|
||||
"title": "Secure, fully featured, and completely free video conferencing",
|
||||
"upcomingMeetings": "Your upcoming meetings"
|
||||
"title": "Secure, fully featured, and completely free video conferencing"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,15 +644,15 @@ function initCommands() {
|
||||
}
|
||||
|
||||
let recordingConfig;
|
||||
const { recordingService } = state['features/base/config'];
|
||||
|
||||
if (!recordingService.enabled && !dropboxToken) {
|
||||
logger.error('Failed starting recording: the recording service is not enabled');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === JitsiRecordingConstants.mode.FILE) {
|
||||
const { recordingService } = state['features/base/config'];
|
||||
|
||||
if (!recordingService.enabled && !dropboxToken) {
|
||||
logger.error('Failed starting recording: the recording service is not enabled');
|
||||
|
||||
return;
|
||||
}
|
||||
if (dropboxToken) {
|
||||
recordingConfig = {
|
||||
mode: JitsiRecordingConstants.mode.FILE,
|
||||
|
||||
4
modules/API/external/external_api.js
vendored
4
modules/API/external/external_api.js
vendored
@@ -388,10 +388,10 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
|
||||
this._frame = document.createElement('iframe');
|
||||
this._frame.allow = 'camera; microphone; display-capture; autoplay; clipboard-write';
|
||||
this._frame.src = this._url;
|
||||
this._frame.name = frameName;
|
||||
this._frame.id = frameName;
|
||||
this._setSize(height, width);
|
||||
this._frame.sandbox = 'allow-scripts allow-same-origin allow-popups allow-forms';
|
||||
this._frame.setAttribute('allowFullScreen', 'true');
|
||||
this._frame.style.border = 0;
|
||||
|
||||
@@ -402,8 +402,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
}
|
||||
|
||||
this._frame = this._parentNode.appendChild(this._frame);
|
||||
|
||||
this._frame.src = this._url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,13 +14,11 @@ import { i18next } from '../../../react/features/base/i18n';
|
||||
import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantById,
|
||||
getParticipantDisplayName,
|
||||
isLocalScreenshareParticipant,
|
||||
isScreenShareParticipant
|
||||
} from '../../../react/features/base/participants';
|
||||
import { getHideSelfView } from '../../../react/features/base/settings/functions.any';
|
||||
import {
|
||||
getVideoTrackByParticipant,
|
||||
trackStreamingStatusChanged
|
||||
@@ -234,10 +232,6 @@ export default class LargeVideoManager {
|
||||
|
||||
preUpdate.then(() => {
|
||||
const { id, stream, videoType, resolve } = this.newStreamData;
|
||||
const state = APP.store.getState();
|
||||
const shouldHideSelfView = getHideSelfView(state);
|
||||
const localId = getLocalParticipant(state)?.id;
|
||||
|
||||
|
||||
// FIXME this does not really make sense, because the videoType
|
||||
// (camera or desktop) is a completely different thing than
|
||||
@@ -251,16 +245,13 @@ export default class LargeVideoManager {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const container = this.getCurrentContainer();
|
||||
|
||||
if (shouldHideSelfView && localId === id) {
|
||||
return container.hide();
|
||||
}
|
||||
|
||||
container.setStream(id, stream, videoType);
|
||||
|
||||
// change the avatar url on large
|
||||
this.updateAvatar();
|
||||
|
||||
const isVideoMuted = !stream || stream.isMuted();
|
||||
const state = APP.store.getState();
|
||||
const participant = getParticipantById(state, id);
|
||||
const videoTrack = getVideoTrackByParticipant(state, participant);
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ export default {
|
||||
* Determines if currently selected media devices should be changed after
|
||||
* list of available devices has been changed.
|
||||
* @param {MediaDeviceInfo[]} newDevices
|
||||
* @param {boolean} isSharingScreen
|
||||
* @param {JitsiLocalTrack} localVideo
|
||||
* @param {JitsiLocalTrack} localAudio
|
||||
* @returns {{
|
||||
@@ -168,12 +169,13 @@ export default {
|
||||
*/
|
||||
getNewMediaDevicesAfterDeviceListChanged( // eslint-disable-line max-params
|
||||
newDevices,
|
||||
isSharingScreen,
|
||||
localVideo,
|
||||
localAudio,
|
||||
newLabels) {
|
||||
return {
|
||||
audioinput: getNewAudioInputDevice(newDevices, localAudio, newLabels),
|
||||
videoinput: getNewVideoInputDevice(newDevices, localVideo, newLabels),
|
||||
videoinput: isSharingScreen ? undefined : getNewVideoInputDevice(newDevices, localVideo, newLabels),
|
||||
audiooutput: getNewAudioOutputDevice(newDevices)
|
||||
};
|
||||
},
|
||||
|
||||
578
package-lock.json
generated
578
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -28,7 +28,7 @@
|
||||
"@atlaskit/tooltip": "17.1.2",
|
||||
"@emotion/react": "11.10.0",
|
||||
"@emotion/styled": "11.10.0",
|
||||
"@giphy/js-fetch-api": "4.7.1",
|
||||
"@giphy/js-fetch-api": "4.1.2",
|
||||
"@giphy/react-components": "5.6.0",
|
||||
"@giphy/react-native-sdk": "1.7.0",
|
||||
"@hapi/bourne": "2.0.0",
|
||||
@@ -47,11 +47,11 @@
|
||||
"@react-native-community/slider": "4.1.12",
|
||||
"@react-native-google-signin/google-signin": "7.0.4",
|
||||
"@react-native-masked-view/masked-view": "0.2.6",
|
||||
"@react-navigation/bottom-tabs": "6.5.3",
|
||||
"@react-navigation/elements": "1.3.13",
|
||||
"@react-navigation/material-top-tabs": "6.5.2",
|
||||
"@react-navigation/native": "6.1.2",
|
||||
"@react-navigation/stack": "6.3.11",
|
||||
"@react-navigation/bottom-tabs": "6.0.9",
|
||||
"@react-navigation/elements": "1.2.1",
|
||||
"@react-navigation/material-top-tabs": "6.0.6",
|
||||
"@react-navigation/native": "6.0.6",
|
||||
"@react-navigation/stack": "6.2.2",
|
||||
"@svgr/webpack": "6.3.1",
|
||||
"@tensorflow/tfjs-backend-wasm": "3.13.0",
|
||||
"@tensorflow/tfjs-core": "3.13.0",
|
||||
@@ -79,7 +79,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1578.0.0+5855ca72/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1557.0.0+8df33524/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -101,15 +101,15 @@
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "8.4.8",
|
||||
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
|
||||
"react-native-gesture-handler": "2.8.0",
|
||||
"react-native-gesture-handler": "2.1.0",
|
||||
"react-native-get-random-values": "1.7.2",
|
||||
"react-native-immersive": "2.0.0",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-orientation-locker": "1.5.0",
|
||||
"react-native-pager-view": "5.4.9",
|
||||
"react-native-paper": "5.1.2",
|
||||
"react-native-paper": "4.11.1",
|
||||
"react-native-performance": "2.1.0",
|
||||
"react-native-safe-area-context": "4.4.1",
|
||||
"react-native-safe-area-context": "3.3.2",
|
||||
"react-native-screens": "3.13.1",
|
||||
"react-native-sound": "0.11.1",
|
||||
"react-native-splash-screen": "3.3.0",
|
||||
@@ -119,7 +119,7 @@
|
||||
"react-native-url-polyfill": "1.3.0",
|
||||
"react-native-video": "https://git@github.com/react-native-video/react-native-video#7c48ae7c8544b2b537fb60194e9620b9fcceae52",
|
||||
"react-native-watch-connectivity": "1.0.11",
|
||||
"react-native-webrtc": "106.0.4",
|
||||
"react-native-webrtc": "106.0.0",
|
||||
"react-native-webview": "11.15.1",
|
||||
"react-native-youtube-iframe": "2.2.1",
|
||||
"react-redux": "7.1.0",
|
||||
@@ -152,7 +152,6 @@
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/lodash": "4.14.182",
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-linkify": "1.0.1",
|
||||
"@types/react-native": "0.68.9",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/react-window": "1.8.5",
|
||||
@@ -200,8 +199,7 @@
|
||||
"tsc:web": "tsc --noEmit --project tsconfig.web.json",
|
||||
"tsc:native": "tsc --noEmit --project tsconfig.native.json",
|
||||
"tsc:ci": "npm run tsc:web && npm run tsc:native",
|
||||
"lint:ci": "eslint --ext .js,.ts,.tsx --max-warnings 0 . && npm run tsc:ci && npm run lint:lang",
|
||||
"lint:lang": "for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done",
|
||||
"lint:ci": "eslint --ext .js,.ts,.tsx --max-warnings 0 . && npm run tsc:ci",
|
||||
"lang-sort": "./resources/lang-sort.sh",
|
||||
"lint-fix": "eslint --ext .js,.ts,.tsx --max-warnings 0 --fix .",
|
||||
"postinstall": "patch-package --error-on-fail && jetify",
|
||||
|
||||
13
patches/react-native-gesture-handler+2.1.0.patch
Normal file
13
patches/react-native-gesture-handler+2.1.0.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/node_modules/react-native-gesture-handler/android/build.gradle b/node_modules/react-native-gesture-handler/android/build.gradle
|
||||
index 8afc3d5..4b1f721 100644
|
||||
--- a/node_modules/react-native-gesture-handler/android/build.gradle
|
||||
+++ b/node_modules/react-native-gesture-handler/android/build.gradle
|
||||
@@ -26,7 +26,7 @@ def shouldUseCommonInterfaceFromReanimated() {
|
||||
def json = new JsonSlurper().parseText(inputFile.text)
|
||||
def reanimatedVersion = json.version as String
|
||||
def (major, minor, patch) = reanimatedVersion.tokenize('.')
|
||||
- return Integer.parseInt(minor) >= 3
|
||||
+ return Integer.parseInt(major) >= 2 && Integer.parseInt(minor) >= 3
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import '../mobile/call-integration/reducer';
|
||||
import '../mobile/external-api/reducer';
|
||||
import '../mobile/full-screen/reducer';
|
||||
import '../mobile/watchos/reducer';
|
||||
import '../share-room/reducer';
|
||||
|
||||
import './reducer.native';
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ import { IRecordingState } from '../recording/reducer';
|
||||
import { IRemoteControlState } from '../remote-control/reducer';
|
||||
import { IScreenShareState } from '../screen-share/reducer';
|
||||
import { IScreenshotCaptureState } from '../screenshot-capture/reducer';
|
||||
import { IShareRoomState } from '../share-room/reducer';
|
||||
import { ISharedVideoState } from '../shared-video/reducer';
|
||||
import { ISpeakerStatsState } from '../speaker-stats/reducer';
|
||||
import { ISubtitlesState } from '../subtitles/reducer';
|
||||
@@ -149,7 +148,6 @@ export interface IReduxState {
|
||||
'features/screen-share': IScreenShareState;
|
||||
'features/screenshot-capture': IScreenshotCaptureState;
|
||||
'features/settings': ISettingsState;
|
||||
'features/share-room': IShareRoomState;
|
||||
'features/shared-video': ISharedVideoState;
|
||||
'features/speaker-stats': ISpeakerStatsState;
|
||||
'features/subtitles': ISubtitlesState;
|
||||
|
||||
@@ -253,7 +253,6 @@ class LoginDialog extends Component<IProps, IState> {
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
disableAutoHideOnSubmit = { true }
|
||||
disableBackdropClose = { true }
|
||||
hideCloseButton = { true }
|
||||
ok = {{
|
||||
|
||||
@@ -15,11 +15,9 @@ import {
|
||||
participantMutedUs,
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantSourcesUpdated,
|
||||
participantUpdated
|
||||
} from '../participants/actions';
|
||||
import { getNormalizedDisplayName } from '../participants/functions';
|
||||
import { IJitsiParticipant } from '../participants/types';
|
||||
import { toState } from '../redux/functions';
|
||||
import {
|
||||
destroyLocalTracks,
|
||||
@@ -130,10 +128,6 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[
|
||||
JitsiConferenceEvents.PARTICIPANT_KICKED,
|
||||
(kicker: any, kicked: any) => dispatch(participantKicked(kicker, kicked)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.PARTICIPANT_SOURCE_UPDATED,
|
||||
(jitsiParticipant: IJitsiParticipant) => dispatch(participantSourcesUpdated(jitsiParticipant)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.LOCK_STATE_CHANGED, // @ts-ignore
|
||||
(...args: any[]) => dispatch(lockStateChanged(conference, ...args)));
|
||||
@@ -507,7 +501,7 @@ export function conferenceWillLeave(conference: IJitsiConference) {
|
||||
* from Redux.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function createConference(overrideRoom?: string | String) {
|
||||
export function createConference(overrideRoom?: string) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
const { connection, locationURL } = state['features/base/connection'];
|
||||
|
||||
@@ -14,11 +14,8 @@ import {
|
||||
} from '../participants/actions';
|
||||
import { getLocalParticipant } from '../participants/functions';
|
||||
import { toState } from '../redux/functions';
|
||||
import {
|
||||
appendURLParam,
|
||||
getBackendSafePath,
|
||||
safeDecodeURIComponent
|
||||
} from '../util/uri';
|
||||
import { getJitsiMeetGlobalNS } from '../util/helpers';
|
||||
import { getBackendSafePath, safeDecodeURIComponent } from '../util/uri';
|
||||
|
||||
import { setObfuscatedRoom } from './actions';
|
||||
import {
|
||||
@@ -105,8 +102,7 @@ export function commonUserJoinedHandling(
|
||||
name: displayName,
|
||||
presence: user.getStatus(),
|
||||
role: user.getRole(),
|
||||
isReplacing,
|
||||
sources: user.getSources()
|
||||
isReplacing
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -249,13 +245,15 @@ export function getConferenceOptions(stateful: IStateful) {
|
||||
delete config.analytics?.googleAnalyticsTrackingId;
|
||||
delete options.callStatsID;
|
||||
delete options.callStatsSecret;
|
||||
} else {
|
||||
options.getWiFiStatsMethod = getWiFiStatsMethod;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the global config (that is, window.config) with XMPP configuration required to join as a visitor.
|
||||
* Returns an object aggregating the conference options.
|
||||
*
|
||||
* @param {IStateful} stateful - The redux store state.
|
||||
* @param {Array<string>} params - The received parameters.
|
||||
@@ -276,17 +274,10 @@ export function generateVisitorConfig(stateful: IStateful, params: Array<string>
|
||||
|
||||
config.hosts.domain = `${vnode}.meet.jitsi`;
|
||||
config.hosts.muc = config.hosts.muc.replace(oldDomain, config.hosts.domain);
|
||||
config.focusUserJid = focusJid;
|
||||
config.hosts.visitorFocus = focusJid;
|
||||
|
||||
// This flag disables sending the initial conference request
|
||||
config.disableFocus = true;
|
||||
|
||||
if (config.bosh) {
|
||||
config.bosh = appendURLParam(config.bosh, 'vnode', vnode);
|
||||
}
|
||||
if (config.websocket) {
|
||||
config.websocket = appendURLParam(config.websocket, 'vnode', vnode);
|
||||
}
|
||||
config.bosh += `?vnode=${vnode}`;
|
||||
config.websocket += `?vnode=${vnode}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,6 +371,21 @@ export function getAnalyticsRoomName(state: IReduxState, dispatch: IStore['dispa
|
||||
return getRoomName(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* a JitsiConference, but that method is added to the context later.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
function getWiFiStatsMethod() {
|
||||
const gloabalNS = getJitsiMeetGlobalNS();
|
||||
|
||||
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an error thrown by the backend (i.e. {@code lib-jitsi-meet}) while
|
||||
* manipulating a conference participant (e.g. Pin or select participant).
|
||||
|
||||
@@ -85,7 +85,6 @@ export interface IJitsiConference {
|
||||
sendFaceLandmarks: (faceLandmarks: FaceLandmarks) => void;
|
||||
sendFeedback: Function;
|
||||
sendLobbyMessage: Function;
|
||||
sendMessage: Function;
|
||||
sessionId: string;
|
||||
setDesktopSharingFrameRate: Function;
|
||||
setDisplayName: Function;
|
||||
|
||||
@@ -88,35 +88,6 @@ export type Sounds = 'ASKED_TO_UNMUTE_SOUND' |
|
||||
'RECORDING_ON_SOUND' |
|
||||
'TALK_WHILE_MUTED_SOUND';
|
||||
|
||||
|
||||
export interface IMobileDynamicLink {
|
||||
apn: string;
|
||||
appCode: string;
|
||||
customDomain?: string;
|
||||
ibi: string;
|
||||
isi: string;
|
||||
}
|
||||
|
||||
export interface IDeeplinkingPlatformConfig {
|
||||
appName: string;
|
||||
}
|
||||
|
||||
export interface IDeeplinkingMobileConfig extends IDeeplinkingPlatformConfig {
|
||||
appPackage?: string;
|
||||
appScheme: string;
|
||||
downloadLink: string;
|
||||
dynamicLink?: IMobileDynamicLink;
|
||||
fDroidUrl?: string;
|
||||
}
|
||||
|
||||
export interface IDeeplinkingConfig {
|
||||
android?: IDeeplinkingMobileConfig;
|
||||
desktop?: IDeeplinkingPlatformConfig;
|
||||
disabled: boolean;
|
||||
hideLogo: boolean;
|
||||
ios?: IDeeplinkingMobileConfig;
|
||||
}
|
||||
|
||||
export interface IConfig {
|
||||
_desktopSharingSourceDevice?: string;
|
||||
analytics?: {
|
||||
@@ -188,7 +159,6 @@ export interface IConfig {
|
||||
alwaysVisible?: Array<string>;
|
||||
autoHide?: Array<string>;
|
||||
};
|
||||
conferenceRequestUrl?: string;
|
||||
connectionIndicators?: {
|
||||
autoHide?: boolean;
|
||||
autoHideTimeout?: number;
|
||||
@@ -206,7 +176,6 @@ export interface IConfig {
|
||||
};
|
||||
};
|
||||
corsAvatarURLs?: Array<string>;
|
||||
deeplinking?: IDeeplinkingConfig;
|
||||
defaultLanguage?: string;
|
||||
defaultLocalDisplayName?: string;
|
||||
defaultLogoUrl?: string;
|
||||
@@ -233,7 +202,7 @@ export interface IConfig {
|
||||
disableChatSmileys?: boolean;
|
||||
disableDeepLinking?: boolean;
|
||||
disableFilmstripAutohiding?: boolean;
|
||||
disableFocus?: boolean;
|
||||
disableH264?: boolean;
|
||||
disableIncomingMessageSound?: boolean;
|
||||
disableInitialGUM?: boolean;
|
||||
disableInviteFunctions?: boolean;
|
||||
@@ -336,14 +305,12 @@ export interface IConfig {
|
||||
};
|
||||
firefox_fake_device?: string;
|
||||
flags?: {
|
||||
ssrcRewritingEnabled: boolean;
|
||||
};
|
||||
focusUserJid?: string;
|
||||
gatherStats?: boolean;
|
||||
giphy?: {
|
||||
displayMode?: 'all' | 'tile' | 'chat';
|
||||
enabled?: boolean;
|
||||
proxyUrl?: string;
|
||||
rating?: 'g' | 'pg' | 'pg-13' | 'r';
|
||||
sdkKey?: string;
|
||||
tileTime?: number;
|
||||
@@ -416,10 +383,12 @@ export interface IConfig {
|
||||
opusMaxAverageBitrate?: number;
|
||||
p2p?: {
|
||||
backToP2PDelay?: number;
|
||||
disableH264?: boolean;
|
||||
disabledCodec?: string;
|
||||
enableUnifiedOnChrome?: boolean;
|
||||
enabled?: boolean;
|
||||
iceTransportPolicy?: string;
|
||||
preferH264?: boolean;
|
||||
preferredCodec?: string;
|
||||
stunServers?: Array<{ urls: string; }>;
|
||||
};
|
||||
@@ -430,6 +399,7 @@ export interface IConfig {
|
||||
};
|
||||
pcStatsInterval?: number;
|
||||
peopleSearchUrl?: string;
|
||||
preferH264?: boolean;
|
||||
preferredTranscribeLanguage?: string;
|
||||
prejoinConfig?: {
|
||||
enabled?: boolean;
|
||||
@@ -484,10 +454,12 @@ export interface IConfig {
|
||||
subject?: string;
|
||||
testing?: {
|
||||
callStatsThreshold?: number;
|
||||
capScreenshareBitrate?: number;
|
||||
disableE2EE?: boolean;
|
||||
mobileXmppWsThreshold?: number;
|
||||
noAutoPlayVideo?: boolean;
|
||||
p2pTestMode?: boolean;
|
||||
setScreenSharingResolutionConstraints?: boolean;
|
||||
testMode?: boolean;
|
||||
};
|
||||
tileView?: {
|
||||
@@ -513,6 +485,7 @@ export interface IConfig {
|
||||
useAppLanguage?: boolean;
|
||||
};
|
||||
useHostPageLocalStorage?: boolean;
|
||||
useNewBandwidthAllocationStrategy?: boolean;
|
||||
useTurnUdp?: boolean;
|
||||
videoQuality?: {
|
||||
disabledCodec?: string;
|
||||
|
||||
@@ -81,7 +81,6 @@ export default [
|
||||
'brandingRoomAlias',
|
||||
'debug',
|
||||
'debugAudioLevels',
|
||||
'deeplinking.disabled',
|
||||
'defaultLocalDisplayName',
|
||||
'defaultRemoteDisplayName',
|
||||
'deploymentUrls',
|
||||
@@ -100,6 +99,7 @@ export default [
|
||||
'disabledSounds',
|
||||
'disableFilmstripAutohiding',
|
||||
'disableInitialGUM',
|
||||
'disableH264',
|
||||
'disableHPF',
|
||||
'disableInviteFunctions',
|
||||
'disableIncomingMessageSound',
|
||||
@@ -197,6 +197,7 @@ export default [
|
||||
'p2p',
|
||||
'participantsPane',
|
||||
'pcStatsInterval',
|
||||
'preferH264',
|
||||
'preferredCodec',
|
||||
'prejoinConfig',
|
||||
'prejoinPageEnabled',
|
||||
|
||||
@@ -60,13 +60,3 @@ export const PREMEETING_BUTTONS = [ 'microphone', 'camera', 'select-background',
|
||||
* The toolbar buttons to show on 3rdParty prejoin screen.
|
||||
*/
|
||||
export const THIRD_PARTY_PREJOIN_BUTTONS = [ 'microphone', 'camera', 'select-background' ];
|
||||
|
||||
/**
|
||||
* The set of feature flags.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
|
||||
export const FEATURE_FLAGS = {
|
||||
SSRC_REWRITING: 'ssrcRewritingEnabled'
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
import { IConfig } from './configType';
|
||||
import CONFIG_WHITELIST from './configWhitelist';
|
||||
import { FEATURE_FLAGS, _CONFIG_STORE_PREFIX } from './constants';
|
||||
import { _CONFIG_STORE_PREFIX } from './constants';
|
||||
import INTERFACE_CONFIG_WHITELIST from './interfaceConfigWhitelist';
|
||||
import logger from './logger';
|
||||
|
||||
@@ -63,16 +63,6 @@ export function getMultipleVideoSendingSupportFeatureFlag(state: IReduxState) {
|
||||
return isUnifiedPlanEnabled(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector used to get the SSRC-rewriting feature flag.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getSsrcRewritingFeatureFlag(state: IReduxState) {
|
||||
return getFeatureFlag(state, FEATURE_FLAGS.SSRC_REWRITING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector used to get a feature flag.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IReduxState } from '../../app/types';
|
||||
import { REPLACE_PARTICIPANT } from '../flags/constants';
|
||||
import { getFeatureFlag } from '../flags/functions';
|
||||
|
||||
import { IConfig, IDeeplinkingConfig } from './configType';
|
||||
import { IConfig } from './configType';
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
@@ -15,18 +15,11 @@ export * from './functions.any';
|
||||
* @returns {void}
|
||||
*/
|
||||
export function _cleanupConfig(config: IConfig) {
|
||||
config.analytics = config.analytics ?? {};
|
||||
config.analytics = {};
|
||||
config.analytics.scriptURLs = [];
|
||||
|
||||
if (NativeModules.AppInfo.LIBRE_BUILD) {
|
||||
delete config.analytics?.amplitudeAPPKey;
|
||||
delete config.analytics?.googleAnalyticsTrackingId;
|
||||
delete config.analytics?.rtcstatsEnabled;
|
||||
delete config.analytics?.rtcstatsEndpoint;
|
||||
delete config.analytics?.rtcstatsPollInterval;
|
||||
delete config.analytics?.rtcstatsSendSdp;
|
||||
delete config.analytics?.rtcstatsUseLegacy;
|
||||
delete config.analytics?.obfuscateRoomName;
|
||||
delete config.callStatsID;
|
||||
delete config.callStatsSecret;
|
||||
config.giphy = { enabled: false };
|
||||
@@ -42,14 +35,3 @@ export function _cleanupConfig(config: IConfig) {
|
||||
export function getReplaceParticipant(state: IReduxState): string {
|
||||
return getFeatureFlag(state, REPLACE_PARTICIPANT, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the defaults for deeplinking.
|
||||
*
|
||||
* @param {IDeeplinkingConfig} _deeplinking - The deeplinking config.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function _setDeeplinkingDefaults(_deeplinking: IDeeplinkingConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IReduxState } from '../../app/types';
|
||||
|
||||
import { IConfig, IDeeplinkingConfig, IDeeplinkingMobileConfig, IDeeplinkingPlatformConfig } from './configType';
|
||||
import { IConfig } from './configType';
|
||||
import { TOOLBAR_BUTTONS } from './constants';
|
||||
|
||||
export * from './functions.any';
|
||||
@@ -8,11 +8,10 @@ export * from './functions.any';
|
||||
/**
|
||||
* Removes all analytics related options from the given configuration, in case of a libre build.
|
||||
*
|
||||
* @param {*} _config - The configuration which needs to be cleaned up.
|
||||
* @param {*} config - The configuration which needs to be cleaned up.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function _cleanupConfig(_config: IConfig) {
|
||||
return;
|
||||
export function _cleanupConfig(config: IConfig) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,43 +60,3 @@ export function areAudioLevelsEnabled(state: IReduxState): boolean {
|
||||
// Default to false for React Native as audio levels are of no interest to the mobile app.
|
||||
return navigator.product !== 'ReactNative' && !state['features/base/config'].disableAudioLevels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the defaults for deeplinking.
|
||||
*
|
||||
* @param {IDeeplinkingConfig} deeplinking - The deeplinking config.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function _setDeeplinkingDefaults(deeplinking: IDeeplinkingConfig) {
|
||||
const {
|
||||
desktop = {} as IDeeplinkingPlatformConfig,
|
||||
android = {} as IDeeplinkingMobileConfig,
|
||||
ios = {} as IDeeplinkingMobileConfig
|
||||
} = deeplinking;
|
||||
|
||||
desktop.appName = desktop.appName || 'Jitsi Meet';
|
||||
|
||||
ios.appName = ios.appName || 'Jitsi Meet';
|
||||
ios.appScheme = ios.appScheme || 'org.jitsi.meet';
|
||||
ios.downloadLink = ios.downloadLink
|
||||
|| 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905';
|
||||
if (ios.dynamicLink) {
|
||||
ios.dynamicLink.apn = ios.dynamicLink.apn || 'org.jitsi.meet';
|
||||
ios.dynamicLink.appCode = ios.dynamicLink.appCode || 'w2atb';
|
||||
ios.dynamicLink.ibi = ios.dynamicLink.ibi || 'com.atlassian.JitsiMeet.ios';
|
||||
ios.dynamicLink.isi = ios.dynamicLink.isi || '1165103905';
|
||||
}
|
||||
|
||||
android.appName = android.appName || 'Jitsi Meet';
|
||||
android.appScheme = android.appScheme || 'org.jitsi.meet';
|
||||
android.downloadLink = android.downloadLink
|
||||
|| 'https://play.google.com/store/apps/details?id=org.jitsi.meet';
|
||||
android.appPackage = android.appPackage || 'org.jitsi.meet';
|
||||
android.fDroidUrl = android.fDroidUrl || 'https://f-droid.org/en/packages/org.jitsi.meet/';
|
||||
if (android.dynamicLink) {
|
||||
android.dynamicLink.apn = android.dynamicLink.apn || 'org.jitsi.meet';
|
||||
android.dynamicLink.appCode = android.dynamicLink.appCode || 'w2atb';
|
||||
android.dynamicLink.ibi = android.dynamicLink.ibi || 'com.atlassian.JitsiMeet.ios';
|
||||
android.dynamicLink.isi = android.dynamicLink.isi || '1165103905';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export default [
|
||||
'SETTINGS_SECTIONS',
|
||||
'SHARING_FEATURES',
|
||||
'SHOW_CHROME_EXTENSION_BANNER',
|
||||
'SHOW_DEEP_LINKING_IMAGE',
|
||||
'SHOW_POWERED_BY',
|
||||
'SUPPORT_URL',
|
||||
'TILE_VIEW_MAX_COLUMNS',
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AnyAction } from 'redux';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { getFeatureFlag } from '../flags/functions';
|
||||
import Platform from '../react/Platform';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import { updateSettings } from '../settings/actions';
|
||||
|
||||
@@ -52,9 +53,7 @@ function _setConfig({ dispatch, getState }: IStore, next: Function, action: AnyA
|
||||
const settings = state['features/base/settings'];
|
||||
const config: IConfig = {};
|
||||
|
||||
// FIXME: P2P is currently temporality disabled on mobile.
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false && typeof settings.disableP2P !== 'undefined') {
|
||||
if (Platform.OS !== 'android' && typeof settings.disableP2P !== 'undefined') {
|
||||
config.p2p = { enabled: !settings.disableP2P };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import { CONFERENCE_INFO } from '../../conference/components/constants';
|
||||
import Platform from '../react/Platform';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
import { equals } from '../redux/functions';
|
||||
|
||||
@@ -11,14 +12,8 @@ import {
|
||||
SET_CONFIG,
|
||||
UPDATE_CONFIG
|
||||
} from './actionTypes';
|
||||
import {
|
||||
IConfig,
|
||||
IDeeplinkingConfig,
|
||||
IDeeplinkingMobileConfig,
|
||||
IDeeplinkingPlatformConfig,
|
||||
IMobileDynamicLink
|
||||
} from './configType';
|
||||
import { _cleanupConfig, _setDeeplinkingDefaults } from './functions';
|
||||
import { IConfig } from './configType';
|
||||
import { _cleanupConfig } from './functions';
|
||||
|
||||
/**
|
||||
* The initial state of the feature base/config when executing in a
|
||||
@@ -53,18 +48,16 @@ const INITIAL_RN_STATE: IConfig = {
|
||||
// fastest to merely disable them.
|
||||
disableAudioLevels: true,
|
||||
|
||||
// FIXME: Mobile codecs should probably be configurable separately, rather
|
||||
// than requiring this override here...
|
||||
|
||||
p2p: {
|
||||
// Temporarily disable P2P on mobile while we sort out some (codec?) issues.
|
||||
enabled: false,
|
||||
disabledCodec: 'vp9',
|
||||
// Temporarily disable P2P on Android while we sort out some (codec?) issues.
|
||||
...(Platform.OS === 'android' ? { enabled: false } : {}), // eslint-disable-line no-extra-parens
|
||||
preferredCodec: 'h264'
|
||||
},
|
||||
|
||||
videoQuality: {
|
||||
disabledCodec: 'vp9',
|
||||
// FIXME: Mobile codecs should probably be configurable separately, rather
|
||||
// than requiring this override here...
|
||||
enforcePreferredCodec: true,
|
||||
preferredCodec: 'vp8'
|
||||
}
|
||||
};
|
||||
@@ -298,55 +291,6 @@ function _translateInterfaceConfig(oldValue: IConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
// if we have `deeplinking` defined, ignore deprecated values, except `disableDeepLinking`.
|
||||
// Otherwise, compose the config.
|
||||
if (oldValue.deeplinking && newValue.deeplinking) { // make TS happy
|
||||
newValue.deeplinking.disabled = oldValue.deeplinking.hasOwnProperty('disabled')
|
||||
? oldValue.deeplinking.disabled
|
||||
: Boolean(oldValue.disableDeepLinking);
|
||||
} else {
|
||||
const disabled = Boolean(oldValue.disableDeepLinking);
|
||||
const deeplinking: IDeeplinkingConfig = {
|
||||
desktop: {} as IDeeplinkingPlatformConfig,
|
||||
hideLogo: false,
|
||||
disabled,
|
||||
android: {} as IDeeplinkingMobileConfig,
|
||||
ios: {} as IDeeplinkingMobileConfig
|
||||
};
|
||||
|
||||
if (typeof interfaceConfig === 'object') {
|
||||
const mobileDynamicLink = interfaceConfig.MOBILE_DYNAMIC_LINK;
|
||||
const dynamicLink: IMobileDynamicLink | undefined = mobileDynamicLink ? {
|
||||
apn: mobileDynamicLink.APN,
|
||||
appCode: mobileDynamicLink.APP_CODE,
|
||||
ibi: mobileDynamicLink.IBI,
|
||||
isi: mobileDynamicLink.ISI,
|
||||
customDomain: mobileDynamicLink.CUSTOM_DOMAIN
|
||||
} : undefined;
|
||||
|
||||
if (deeplinking.desktop) {
|
||||
deeplinking.desktop.appName = interfaceConfig.NATIVE_APP_NAME;
|
||||
}
|
||||
|
||||
deeplinking.hideLogo = Boolean(interfaceConfig.HIDE_DEEP_LINKING_LOGO);
|
||||
deeplinking.android = {
|
||||
appName: interfaceConfig.NATIVE_APP_NAME,
|
||||
appScheme: interfaceConfig.APP_SCHEME,
|
||||
downloadLink: interfaceConfig.MOBILE_DOWNLOAD_LINK_ANDROID,
|
||||
appPackage: interfaceConfig.ANDROID_APP_PACKAGE,
|
||||
fDroidUrl: interfaceConfig.MOBILE_DOWNLOAD_LINK_F_DROID,
|
||||
dynamicLink
|
||||
};
|
||||
deeplinking.ios = {
|
||||
appName: interfaceConfig.NATIVE_APP_NAME,
|
||||
appScheme: interfaceConfig.APP_SCHEME,
|
||||
downloadLink: interfaceConfig.MOBILE_DOWNLOAD_LINK_IOS,
|
||||
dynamicLink
|
||||
};
|
||||
}
|
||||
newValue.deeplinking = deeplinking;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@@ -535,8 +479,6 @@ function _translateLegacyConfig(oldValue: IConfig) {
|
||||
};
|
||||
}
|
||||
|
||||
_setDeeplinkingDefaults(newValue.deeplinking as IDeeplinkingConfig);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -189,9 +189,6 @@ export function constructOptions(state: IReduxState) {
|
||||
if (options.websocketKeepAliveUrl) {
|
||||
options.websocketKeepAliveUrl = appendURLParam(options.websocketKeepAliveUrl, 'room', roomName ?? '');
|
||||
}
|
||||
if (options.conferenceRequestUrl) {
|
||||
options.conferenceRequestUrl = appendURLParam(options.conferenceRequestUrl, 'room', roomName ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
|
||||
@@ -88,6 +88,7 @@ export { default as IconSubtitles } from './subtitles.svg';
|
||||
export { default as IconTileView } from './tile-view.svg';
|
||||
export { default as IconTrash } from './trash.svg';
|
||||
export { default as IconUserDeleted } from './user-deleted.svg';
|
||||
export { default as IconUserGroups } from './user-groups.svg';
|
||||
export { default as IconUsers } from './users.svg';
|
||||
export { default as IconVideo } from './video.svg';
|
||||
export { default as IconVideoOff } from './video-off.svg';
|
||||
|
||||
3
react/features/base/icons/svg/user-groups.svg
Normal file
3
react/features/base/icons/svg/user-groups.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.33331 2C6.28101 2 7.09675 2.56499 7.46207 3.37651C7.00766 3.45023 6.58406 3.61583 6.21095 3.85361C6.04111 3.54356 5.71176 3.33333 5.33331 3.33333C4.78103 3.33333 4.33331 3.78105 4.33331 4.33333C4.33331 4.75895 4.59921 5.12246 4.97395 5.26682C4.77672 5.69245 4.66665 6.16671 4.66665 6.66667L4.66678 6.6967C3.12249 6.85332 2.66665 7.65415 2.66665 9.83333C2.66665 9.89666 2.66835 9.95222 2.67088 10H3.13441C2.977 10.3982 2.86114 10.8423 2.7841 11.3333H2.33331C1.66665 11.3333 1.33331 10.8333 1.33331 9.83333C1.33331 7.60559 1.88097 6.20498 3.39417 5.63152C3.14521 5.26038 2.99998 4.81382 2.99998 4.33333C2.99998 3.04467 4.04465 2 5.33331 2ZM9.78901 3.85361C9.4159 3.61583 8.9923 3.45023 8.53788 3.37651C8.90321 2.56499 9.71895 2 10.6666 2C11.9553 2 13 3.04467 13 4.33333C13 4.81382 12.8547 5.26038 12.6058 5.63152C14.119 6.20498 14.6666 7.60559 14.6666 9.83333C14.6666 10.8333 14.3333 11.3333 13.6666 11.3333H13.2159C13.1388 10.8423 13.023 10.3982 12.8656 10H13.3291C13.3316 9.95222 13.3333 9.89666 13.3333 9.83333C13.3333 7.65415 12.8775 6.85332 11.3332 6.6967L11.3333 6.66667C11.3333 6.1667 11.2232 5.69245 11.026 5.26682C11.4008 5.12246 11.6666 4.75895 11.6666 4.33333C11.6666 3.78105 11.2189 3.33333 10.6666 3.33333C10.2882 3.33333 9.95885 3.54356 9.78901 3.85361ZM4.49998 14.6667C3.7222 14.6667 3.33331 14.1111 3.33331 13C3.33331 10.4598 4.0062 8.8875 5.87888 8.28308C5.5366 7.83462 5.33331 7.27438 5.33331 6.66667C5.33331 5.19391 6.52722 4 7.99998 4C9.47274 4 10.6666 5.19391 10.6666 6.66667C10.6666 7.27438 10.4634 7.83462 10.1211 8.28308C11.9938 8.8875 12.6666 10.4598 12.6666 13C12.6666 14.1111 12.2778 14.6667 11.5 14.6667H4.49998ZM9.33331 6.66667C9.33331 7.40305 8.73636 8 7.99998 8C7.2636 8 6.66665 7.40305 6.66665 6.66667C6.66665 5.93029 7.2636 5.33333 7.99998 5.33333C8.73636 5.33333 9.33331 5.93029 9.33331 6.66667ZM11.3333 13C11.3333 13.1426 11.3252 13.2536 11.3152 13.3333H4.68477C4.67476 13.2536 4.66665 13.1426 4.66665 13C4.66665 10.1957 5.42021 9.33333 7.99998 9.33333C10.5797 9.33333 11.3333 10.1957 11.3333 13Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -5,6 +5,7 @@ import { SET_FILMSTRIP_ENABLED } from '../../filmstrip/actionTypes';
|
||||
import { SELECT_LARGE_VIDEO_PARTICIPANT } from '../../large-video/actionTypes';
|
||||
import { APP_STATE_CHANGED } from '../../mobile/background/actionTypes';
|
||||
import {
|
||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED,
|
||||
SET_CAR_MODE,
|
||||
SET_TILE_VIEW,
|
||||
VIRTUAL_SCREENSHARE_REMOTE_PARTICIPANTS_UPDATED
|
||||
@@ -101,6 +102,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
case PARTICIPANT_JOINED:
|
||||
case PARTICIPANT_KICKED:
|
||||
case PARTICIPANT_LEFT:
|
||||
case SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED:
|
||||
case SELECT_LARGE_VIDEO_PARTICIPANT:
|
||||
case SET_AUDIO_ONLY:
|
||||
case SET_CAR_MODE:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import './native';
|
||||
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet.
|
||||
//
|
||||
|
||||
10
react/features/base/lib-jitsi-meet/native/WiFiStats.js
Normal file
10
react/features/base/lib-jitsi-meet/native/WiFiStats.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { getJitsiMeetGlobalNS } from '../../util';
|
||||
|
||||
/**
|
||||
* If WiFiStats native module exist attach it to JitsiMeetGlobalNS.
|
||||
*/
|
||||
if (NativeModules.WiFiStats) {
|
||||
getJitsiMeetGlobalNS().getWiFiStats = NativeModules.WiFiStats.getWiFiStats;
|
||||
}
|
||||
1
react/features/base/lib-jitsi-meet/native/index.js
Normal file
1
react/features/base/lib-jitsi-meet/native/index.js
Normal file
@@ -0,0 +1 @@
|
||||
import './WiFiStats';
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from './actionTypes';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
type MediaType,
|
||||
SCREENSHARE_MUTISM_AUTHORITY,
|
||||
VIDEO_MUTISM_AUTHORITY
|
||||
} from './constants';
|
||||
@@ -94,12 +95,14 @@ export function setCameraFacingMode(cameraFacingMode: string) {
|
||||
* Action to set the muted state of the local screenshare.
|
||||
*
|
||||
* @param {boolean} muted - True if the local screenshare is to be enabled or false otherwise.
|
||||
* @param {MEDIA_TYPE} mediaType - The type of media.
|
||||
* @param {number} authority - The {@link SCREENSHARE_MUTISM_AUTHORITY} which is muting/unmuting the local screenshare.
|
||||
* @param {boolean} ensureTrack - True if we want to ensure that a new track is created if missing.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setScreenshareMuted(
|
||||
muted: boolean,
|
||||
mediaType: MediaType = MEDIA_TYPE.SCREENSHARE,
|
||||
authority: number = SCREENSHARE_MUTISM_AUTHORITY.USER,
|
||||
ensureTrack = false) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
@@ -122,6 +125,7 @@ export function setScreenshareMuted(
|
||||
return dispatch({
|
||||
type: SET_SCREENSHARE_MUTED,
|
||||
authority,
|
||||
mediaType,
|
||||
ensureTrack,
|
||||
muted: newValue
|
||||
});
|
||||
@@ -150,6 +154,7 @@ export function setVideoAvailable(available: boolean) {
|
||||
*
|
||||
* @param {boolean} muted - True if the local video is to be muted or false if
|
||||
* the local video is to be unmuted.
|
||||
* @param {MEDIA_TYPE} mediaType - The type of media.
|
||||
* @param {number} authority - The {@link VIDEO_MUTISM_AUTHORITY} which is
|
||||
* muting/unmuting the local video.
|
||||
* @param {boolean} ensureTrack - True if we want to ensure that a new track is
|
||||
@@ -158,6 +163,7 @@ export function setVideoAvailable(available: boolean) {
|
||||
*/
|
||||
export function setVideoMuted(
|
||||
muted: boolean,
|
||||
mediaType: string = MEDIA_TYPE.VIDEO,
|
||||
authority: number = VIDEO_MUTISM_AUTHORITY.USER,
|
||||
ensureTrack = false) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
@@ -180,6 +186,7 @@ export function setVideoMuted(
|
||||
return dispatch({
|
||||
type: SET_VIDEO_MUTED,
|
||||
authority,
|
||||
mediaType,
|
||||
ensureTrack,
|
||||
muted: newValue
|
||||
});
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { PanResponder, PixelRatio, View } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { connect } from '../../../redux/functions';
|
||||
import { ASPECT_RATIO_WIDE } from '../../../responsive-ui/constants';
|
||||
import { connect } from '../../../redux';
|
||||
import { ASPECT_RATIO_WIDE } from '../../../responsive-ui';
|
||||
import { storeVideoTransform } from '../../actions';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* The default/initial transform (= no transform).
|
||||
*/
|
||||
@@ -57,9 +55,9 @@ const TAP_TIMEOUT_MS = 400;
|
||||
* Type of a transform object this component is capable of handling.
|
||||
*/
|
||||
type Transform = {
|
||||
scale: number;
|
||||
translateX: number;
|
||||
translateY: number;
|
||||
scale: number,
|
||||
translateX: number,
|
||||
translateY: number
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -67,43 +65,43 @@ type Props = {
|
||||
/**
|
||||
* The current aspect ratio of the screen.
|
||||
*/
|
||||
_aspectRatio: Symbol;
|
||||
_aspectRatio: Symbol,
|
||||
|
||||
/**
|
||||
* Action to dispatch when the component is unmounted.
|
||||
* The children components of this view.
|
||||
*/
|
||||
_onUnmount: Function;
|
||||
children: Object,
|
||||
|
||||
/**
|
||||
* Transformation is only enabled when this flag is true.
|
||||
*/
|
||||
enabled: boolean,
|
||||
|
||||
/**
|
||||
* Function to invoke when a press event is detected.
|
||||
*/
|
||||
onPress?: Function,
|
||||
|
||||
/**
|
||||
* The id of the current stream that is displayed.
|
||||
*/
|
||||
streamId: string,
|
||||
|
||||
/**
|
||||
* Style of the top level transformable view.
|
||||
*/
|
||||
style: Object,
|
||||
|
||||
/**
|
||||
* The stored transforms retrieved from Redux to be initially applied
|
||||
* to different streams.
|
||||
*/
|
||||
_transforms: Object;
|
||||
_transforms: Object,
|
||||
|
||||
/**
|
||||
* The children components of this view.
|
||||
* Action to dispatch when the component is unmounted.
|
||||
*/
|
||||
children: Object;
|
||||
|
||||
/**
|
||||
* Transformation is only enabled when this flag is true.
|
||||
*/
|
||||
enabled: boolean;
|
||||
|
||||
/**
|
||||
* Function to invoke when a press event is detected.
|
||||
*/
|
||||
onPress?: Function;
|
||||
|
||||
/**
|
||||
* The id of the current stream that is displayed.
|
||||
*/
|
||||
streamId: string;
|
||||
|
||||
/**
|
||||
* Style of the top level transformable view.
|
||||
*/
|
||||
style: Object;
|
||||
_onUnmount: Function
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -111,12 +109,12 @@ type State = {
|
||||
/**
|
||||
* The current (non-transformed) layout of the View.
|
||||
*/
|
||||
layout: any;
|
||||
layout: ?Object,
|
||||
|
||||
/**
|
||||
* The current transform that is applied.
|
||||
*/
|
||||
transform: Transform;
|
||||
transform: Transform
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -131,14 +129,14 @@ class VideoTransform extends Component<Props, State> {
|
||||
/**
|
||||
* The initial distance of the fingers on pinch start.
|
||||
*/
|
||||
initialDistance: number;
|
||||
initialDistance: ?number;
|
||||
|
||||
/**
|
||||
* The initial position of the finger on touch start.
|
||||
*/
|
||||
initialPosition: {
|
||||
x: number;
|
||||
y: number;
|
||||
x: number,
|
||||
y: number
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -235,17 +233,14 @@ class VideoTransform extends Component<Props, State> {
|
||||
videoTransformedViewContainerStyles,
|
||||
style
|
||||
] }
|
||||
|
||||
// @ts-ignore
|
||||
{ ...this.gestureHandlers.panHandlers }>
|
||||
<SafeAreaView
|
||||
edges = { [ 'bottom', 'left' ] }
|
||||
<View
|
||||
style = { [
|
||||
styles.videoTranformedView,
|
||||
this._getTransformStyle()
|
||||
] }>
|
||||
{ children }
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -286,13 +281,15 @@ class VideoTransform extends Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
_didMove: Object => boolean;
|
||||
|
||||
/**
|
||||
* Determines if there was large enough movement to be handled.
|
||||
*
|
||||
* @param {Object} gestureState - The gesture state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_didMove({ dx, dy }: any) {
|
||||
_didMove({ dx, dy }) {
|
||||
return Math.abs(dx) > this.moveThreshold
|
||||
|| Math.abs(dy) > this.moveThreshold;
|
||||
}
|
||||
@@ -305,13 +302,14 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {Object | null}
|
||||
*/
|
||||
_getSavedTransform(streamId: string) {
|
||||
_getSavedTransform(streamId) {
|
||||
const { enabled, _transforms } = this.props;
|
||||
|
||||
// @ts-ignore
|
||||
return (enabled && _transforms[streamId]) || null;
|
||||
}
|
||||
|
||||
_getTouchDistance: Object => number;
|
||||
|
||||
/**
|
||||
* Calculates the touch distance on a pinch event.
|
||||
*
|
||||
@@ -319,13 +317,15 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {number}
|
||||
*/
|
||||
_getTouchDistance({ nativeEvent: { touches } }: any) {
|
||||
_getTouchDistance({ nativeEvent: { touches } }) {
|
||||
const dx = Math.abs(touches[0].pageX - touches[1].pageX);
|
||||
const dy = Math.abs(touches[0].pageY - touches[1].pageY);
|
||||
|
||||
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
|
||||
}
|
||||
|
||||
_getTouchPosition: Object => Object;
|
||||
|
||||
/**
|
||||
* Calculates the position of the touch event.
|
||||
*
|
||||
@@ -333,13 +333,15 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getTouchPosition({ nativeEvent: { touches } }: any) {
|
||||
_getTouchPosition({ nativeEvent: { touches } }) {
|
||||
return {
|
||||
x: touches[0].pageX,
|
||||
y: touches[0].pageY
|
||||
};
|
||||
}
|
||||
|
||||
_getTransformStyle: () => Object;
|
||||
|
||||
/**
|
||||
* Generates a transform style object to be used on the component.
|
||||
*
|
||||
@@ -471,6 +473,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onGesture: (string, ?Object | number) => void;
|
||||
|
||||
/**
|
||||
* Handles gestures and converts them to transforms.
|
||||
*
|
||||
@@ -490,7 +494,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @param {?Object | number} value - The value of the gesture, if any.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onGesture(type: string, value: any) {
|
||||
_onGesture(type, value) {
|
||||
let transform;
|
||||
|
||||
switch (type) {
|
||||
@@ -524,6 +528,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
this.lastTap = 0;
|
||||
}
|
||||
|
||||
_onLayout: Object => void;
|
||||
|
||||
/**
|
||||
* Callback for the onLayout of the component.
|
||||
*
|
||||
@@ -531,7 +537,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onLayout({ nativeEvent: { layout: { x, y, width, height } } }: any) {
|
||||
_onLayout({ nativeEvent: { layout: { x, y, width, height } } }) {
|
||||
this.setState({
|
||||
layout: {
|
||||
x,
|
||||
@@ -542,6 +548,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
_onMoveShouldSetPanResponder: (Object, Object) => boolean;
|
||||
|
||||
/**
|
||||
* Function to decide whether the responder should respond to a move event.
|
||||
*
|
||||
@@ -550,12 +558,14 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onMoveShouldSetPanResponder(evt: Object, gestureState: any) {
|
||||
_onMoveShouldSetPanResponder(evt, gestureState) {
|
||||
return this.props.enabled
|
||||
&& (this._didMove(gestureState)
|
||||
|| gestureState.numberActiveTouches === 2);
|
||||
}
|
||||
|
||||
_onPanResponderGrant: (Object, Object) => void;
|
||||
|
||||
/**
|
||||
* Calculates the initial touch distance.
|
||||
*
|
||||
@@ -564,7 +574,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onPanResponderGrant(evt: Object, { numberActiveTouches }: any) {
|
||||
_onPanResponderGrant(evt, { numberActiveTouches }) {
|
||||
if (numberActiveTouches === 1) {
|
||||
this.initialPosition = this._getTouchPosition(evt);
|
||||
this.lastTap = Date.now();
|
||||
@@ -574,6 +584,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onPanResponderMove: (Object, Object) => void;
|
||||
|
||||
/**
|
||||
* Handles the PanResponder move (touch move) event.
|
||||
*
|
||||
@@ -582,7 +594,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onPanResponderMove(evt: Object, gestureState: any) {
|
||||
_onPanResponderMove(evt, gestureState) {
|
||||
if (gestureState.numberActiveTouches === 2) {
|
||||
// this is a zoom event
|
||||
if (
|
||||
@@ -616,6 +628,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onPanResponderRelease: () => void;
|
||||
|
||||
/**
|
||||
* Handles the PanResponder gesture end event.
|
||||
*
|
||||
@@ -624,11 +638,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
*/
|
||||
_onPanResponderRelease() {
|
||||
if (this.lastTap && Date.now() - this.lastTap < TAP_TIMEOUT_MS) {
|
||||
// @ts-ignore
|
||||
this._onGesture('press');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
delete this.initialDistance;
|
||||
this.initialPosition = {
|
||||
x: 0,
|
||||
@@ -636,6 +647,8 @@ class VideoTransform extends Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
_onStartShouldSetPanResponder: () => boolean;
|
||||
|
||||
/**
|
||||
* Function to decide whether the responder should respond to a start
|
||||
* (touch) event.
|
||||
@@ -655,7 +668,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_restoreTransform(streamId: string) {
|
||||
_restoreTransform(streamId) {
|
||||
const savedTransform = this._getSavedTransform(streamId);
|
||||
|
||||
if (savedTransform) {
|
||||
@@ -674,7 +687,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_storeTransform(streamId: string, transform: Transform) {
|
||||
_storeTransform(streamId, transform) {
|
||||
const { _onUnmount, enabled } = this.props;
|
||||
|
||||
if (enabled) {
|
||||
@@ -702,7 +715,7 @@ function _mapDispatchToProps(dispatch: Dispatch<any>) {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onUnmount(streamId: string, transform: Transform) {
|
||||
_onUnmount(streamId, transform) {
|
||||
dispatch(storeVideoTransform(streamId, transform));
|
||||
}
|
||||
};
|
||||
@@ -717,7 +730,7 @@ function _mapDispatchToProps(dispatch: Dispatch<any>) {
|
||||
* _transforms: Object
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
|
||||
|
||||
@@ -23,7 +23,7 @@ export default StyleSheet.create({
|
||||
|
||||
videoTransformedViewContainerWide: {
|
||||
overflow: 'hidden',
|
||||
paddingRight: '16%'
|
||||
paddingRight: '14%'
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,7 +166,7 @@ function _appStateChanged({ dispatch, getState }, next, action) {
|
||||
|
||||
sendAnalytics(createTrackMutedEvent('video', 'background mode', mute));
|
||||
|
||||
dispatch(setVideoMuted(mute, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
||||
dispatch(setVideoMuted(mute, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
@@ -191,9 +191,9 @@ function _setAudioOnly({ dispatch, getState }, next, action) {
|
||||
sendAnalytics(createTrackMutedEvent('video', 'audio-only mode', audioOnly));
|
||||
|
||||
// Make sure we mute both the desktop and video tracks.
|
||||
dispatch(setVideoMuted(audioOnly, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||
dispatch(setVideoMuted(audioOnly, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||
if (getMultipleVideoSendingSupportFeatureFlag(state)) {
|
||||
dispatch(setScreenshareMuted(audioOnly, SCREENSHARE_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||
dispatch(setScreenshareMuted(audioOnly, MEDIA_TYPE.SCREENSHARE, SCREENSHARE_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
@@ -273,10 +273,7 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||
// The following don't have complications around whether
|
||||
// they are defined or not:
|
||||
jwt: false,
|
||||
|
||||
// We need to look for 'startAudioOnly' in settings only for react native clients. Otherwise, the
|
||||
// default value from ISettingsState (false) will override the value set in config for web clients.
|
||||
settings: typeof APP === 'undefined'
|
||||
settings: true
|
||||
}));
|
||||
|
||||
sendAnalytics(createStartAudioOnlyEvent(audioOnly));
|
||||
|
||||
29
react/features/base/modal/components/JitsiScreenWebView.js
Normal file
29
react/features/base/modal/components/JitsiScreenWebView.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import WebView from 'react-native-webview';
|
||||
|
||||
import JitsiScreen from './JitsiScreen';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The URL to display.
|
||||
*/
|
||||
source: string,
|
||||
|
||||
/**
|
||||
* The component's external style.
|
||||
*/
|
||||
style: Object
|
||||
}
|
||||
|
||||
const JitsiScreenWebView = ({ source, style }: Props) => (
|
||||
<JitsiScreen
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
style = { style }>
|
||||
<WebView source = {{ uri: source }} />
|
||||
</JitsiScreen>
|
||||
);
|
||||
|
||||
export default JitsiScreenWebView;
|
||||
@@ -117,18 +117,6 @@ export const PARTICIPANT_KICKED = 'PARTICIPANT_KICKED';
|
||||
*/
|
||||
export const PARTICIPANT_LEFT = 'PARTICIPANT_LEFT';
|
||||
|
||||
/**
|
||||
* Action to handle case when the sources attached to a participant are updated.
|
||||
*
|
||||
* {
|
||||
* type: PARTICIPANT_SOURCES_UPDATED,
|
||||
* participant: {
|
||||
* id: string
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export const PARTICIPANT_SOURCES_UPDATED = 'PARTICIPANT_SOURCES_UPDATED';
|
||||
|
||||
/**
|
||||
* Action to handle case when info about participant changes.
|
||||
*
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
PARTICIPANT_JOINED,
|
||||
PARTICIPANT_KICKED,
|
||||
PARTICIPANT_LEFT,
|
||||
PARTICIPANT_SOURCES_UPDATED,
|
||||
PARTICIPANT_UPDATED,
|
||||
PIN_PARTICIPANT,
|
||||
RAISE_HAND_UPDATED,
|
||||
@@ -37,7 +36,7 @@ import {
|
||||
getVirtualScreenshareParticipantOwnerId
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
import { FakeParticipant, IJitsiParticipant, IParticipant } from './types';
|
||||
import { FakeParticipant, IParticipant } from './types';
|
||||
|
||||
/**
|
||||
* Create an action for when dominant speaker changes.
|
||||
@@ -254,39 +253,6 @@ export function participantJoined(participant: IParticipant) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sources of a remote participant.
|
||||
*
|
||||
* @param {IJitsiParticipant} jitsiParticipant - The IJitsiParticipant instance.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_SOURCES_UPDATED,
|
||||
* participant: IParticipant
|
||||
* }}
|
||||
*/
|
||||
export function participantSourcesUpdated(jitsiParticipant: IJitsiParticipant) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const id = jitsiParticipant.getId();
|
||||
const participant = getParticipantById(getState(), id);
|
||||
|
||||
if (participant?.local) {
|
||||
return;
|
||||
}
|
||||
const sources = jitsiParticipant.getSources();
|
||||
|
||||
if (!sources?.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
return dispatch({
|
||||
type: PARTICIPANT_SOURCES_UPDATED,
|
||||
participant: {
|
||||
id,
|
||||
sources
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the features of a remote participant.
|
||||
*
|
||||
|
||||
@@ -6,9 +6,8 @@ import { isStageFilmstripAvailable } from '../../filmstrip/functions';
|
||||
import { IStateful } from '../app/types';
|
||||
import { GRAVATAR_BASE_URL } from '../avatar/constants';
|
||||
import { isCORSAvatarURL } from '../avatar/functions';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import i18next from '../i18n/i18next';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
|
||||
import { VIDEO_TYPE } from '../media/constants';
|
||||
import { toState } from '../redux/functions';
|
||||
import { getScreenShareTrack } from '../tracks/functions';
|
||||
import { createDeferred } from '../util/helpers';
|
||||
@@ -22,7 +21,7 @@ import {
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { preloadImage } from './preloadImage';
|
||||
import { FakeParticipant, IJitsiParticipant, IParticipant, ISourceInfo } from './types';
|
||||
import { FakeParticipant, IParticipant } from './types';
|
||||
|
||||
/**
|
||||
* Temp structures for avatar urls to be checked/preloaded.
|
||||
@@ -352,11 +351,11 @@ export function isWhiteboardParticipant(participant?: IParticipant): boolean {
|
||||
* features/base/participants.
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getRemoteParticipantCountWithFake(stateful: IStateful) {
|
||||
export function getRemoteParticipantCount(stateful: IStateful) {
|
||||
const state = toState(stateful);
|
||||
const participantsState = state['features/base/participants'];
|
||||
|
||||
return participantsState.remote.size;
|
||||
return participantsState.remote.size - participantsState.sortedRemoteVirtualScreenshareParticipants.size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,35 +407,6 @@ export function getParticipantDisplayName(stateful: IStateful, id: string): stri
|
||||
return defaultRemoteDisplayName ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source names of the screenshare sources in the conference based on the presence shared by the remote
|
||||
* endpoints. This should be only used for creating/removing virtual screenshare participant tiles when ssrc-rewriting
|
||||
* is enabled. Once the tile is created, the source-name gets added to the receiver constraints based on which the
|
||||
* JVB will add the source to the video sources map and signal it to the local endpoint. Only then, a remote track is
|
||||
* created/remapped and the tracks in redux will be updated. Once the track is updated in redux, the client will
|
||||
* will continue to use the other track based getter functions for other operations related to screenshare.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's {@code getState} function to be used to
|
||||
* retrieve the state.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function getRemoteScreensharesBasedOnPresence(stateful: IStateful): string[] {
|
||||
const conference = getCurrentConference(stateful);
|
||||
|
||||
return conference?.getParticipants()?.reduce((screenshares: string[], participant: IJitsiParticipant) => {
|
||||
const sources: Map<string, Map<string, ISourceInfo>> = participant.getSources();
|
||||
const videoSources = sources.get(MEDIA_TYPE.VIDEO);
|
||||
const screenshareSources = Array.from(videoSources ?? new Map())
|
||||
.filter(source => source[1].videoType === VIDEO_TYPE.DESKTOP && !source[1].muted)
|
||||
.map(source => source[0]);
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
screenshares = [ ...screenshares, ...screenshareSources ];
|
||||
|
||||
return screenshares;
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns screenshare participant's display name.
|
||||
*
|
||||
@@ -464,36 +434,6 @@ export function getScreenshareParticipantIds(stateful: IStateful): Array<string>
|
||||
.map(t => t.participantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list source name associated with a given remote participant and for the given media type.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's {@code getState} function to be used to
|
||||
* retrieve the state.
|
||||
* @param {string} id - The id of the participant whose source names are to be retrieved.
|
||||
* @param {string} mediaType - The type of source, audio or video.
|
||||
* @returns {Array<string>|undefined}
|
||||
*/
|
||||
export function getSourceNamesByMediaType(
|
||||
stateful: IStateful,
|
||||
id: string,
|
||||
mediaType: string): Array<string> | undefined {
|
||||
const participant: IParticipant | undefined = getParticipantById(stateful, id);
|
||||
|
||||
if (!participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sources = participant.sources;
|
||||
|
||||
if (!sources) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Array.from(sources.get(mediaType) ?? new Map())
|
||||
.filter(source => source[1].videoType !== VIDEO_TYPE.DESKTOP || !source[1].muted)
|
||||
.map(s => s[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presence status of a participant associated with the passed id.
|
||||
*
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import {
|
||||
SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED
|
||||
} from '../../video-layout/actionTypes';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
import { set } from '../redux/functions';
|
||||
|
||||
@@ -8,7 +10,6 @@ import {
|
||||
PARTICIPANT_ID_CHANGED,
|
||||
PARTICIPANT_JOINED,
|
||||
PARTICIPANT_LEFT,
|
||||
PARTICIPANT_SOURCES_UPDATED,
|
||||
PARTICIPANT_UPDATED,
|
||||
PIN_PARTICIPANT,
|
||||
RAISE_HAND_UPDATED,
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
isRemoteScreenshareParticipant,
|
||||
isScreenShareParticipant
|
||||
} from './functions';
|
||||
import { FakeParticipant, ILocalParticipant, IParticipant, ISourceInfo } from './types';
|
||||
import { ILocalParticipant, IParticipant } from './types';
|
||||
|
||||
/**
|
||||
* Participant object.
|
||||
@@ -71,9 +72,9 @@ const DEFAULT_STATE = {
|
||||
pinnedParticipant: undefined,
|
||||
raisedHandsQueue: [],
|
||||
remote: new Map(),
|
||||
remoteVideoSources: new Set<string>(),
|
||||
sortedRemoteVirtualScreenshareParticipants: new Map(),
|
||||
sortedRemoteParticipants: new Map(),
|
||||
sortedRemoteScreenshares: new Map(),
|
||||
speakersList: new Map()
|
||||
};
|
||||
|
||||
@@ -87,8 +88,8 @@ export interface IParticipantsState {
|
||||
pinnedParticipant?: string;
|
||||
raisedHandsQueue: Array<{ id: string; raisedHandTimestamp: number; }>;
|
||||
remote: Map<string, IParticipant>;
|
||||
remoteVideoSources: Set<string>;
|
||||
sortedRemoteParticipants: Map<string, string>;
|
||||
sortedRemoteScreenshares: Map<string, string>;
|
||||
sortedRemoteVirtualScreenshareParticipants: Map<string, string>;
|
||||
speakersList: Map<string, string>;
|
||||
}
|
||||
@@ -247,8 +248,7 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
fakeParticipant,
|
||||
id,
|
||||
name,
|
||||
pinned,
|
||||
sources
|
||||
pinned
|
||||
} = participant;
|
||||
const { pinnedParticipant, dominantSpeaker } = state;
|
||||
|
||||
@@ -292,19 +292,6 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
|
||||
state.remote.set(id, participant);
|
||||
|
||||
if (sources?.size) {
|
||||
const videoSources: Map<string, ISourceInfo> | undefined = sources.get(MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (videoSources?.size) {
|
||||
const newRemoteVideoSources = new Set(state.remoteVideoSources);
|
||||
|
||||
for (const source of videoSources.keys()) {
|
||||
newRemoteVideoSources.add(source);
|
||||
}
|
||||
state.remoteVideoSources = newRemoteVideoSources;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the new participant.
|
||||
const displayName = _getDisplayName(state, name);
|
||||
const sortedRemoteParticipants = Array.from(state.sortedRemoteParticipants);
|
||||
@@ -350,23 +337,6 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
} = state;
|
||||
let oldParticipant = remote.get(id);
|
||||
|
||||
if (oldParticipant?.sources?.size) {
|
||||
const videoSources: Map<string, ISourceInfo> | undefined = oldParticipant.sources.get(MEDIA_TYPE.VIDEO);
|
||||
const newRemoteVideoSources = new Set(state.remoteVideoSources);
|
||||
|
||||
if (videoSources?.size) {
|
||||
for (const source of videoSources.keys()) {
|
||||
newRemoteVideoSources.delete(source);
|
||||
}
|
||||
}
|
||||
state.remoteVideoSources = newRemoteVideoSources;
|
||||
} else if (oldParticipant?.fakeParticipant === FakeParticipant.RemoteScreenShare) {
|
||||
const newRemoteVideoSources = new Set(state.remoteVideoSources);
|
||||
|
||||
newRemoteVideoSources.delete(id);
|
||||
state.remoteVideoSources = newRemoteVideoSources;
|
||||
}
|
||||
|
||||
if (oldParticipant && oldParticipant.conference === conference) {
|
||||
remote.delete(id);
|
||||
} else if (local?.id === id) {
|
||||
@@ -409,32 +379,35 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
|
||||
return { ...state };
|
||||
}
|
||||
case PARTICIPANT_SOURCES_UPDATED: {
|
||||
const { id, sources } = action.participant;
|
||||
const participant = state.remote.get(id);
|
||||
|
||||
if (participant) {
|
||||
participant.sources = sources;
|
||||
const videoSources: Map<string, ISourceInfo> = sources.get(MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (videoSources?.size) {
|
||||
const newRemoteVideoSources = new Set(state.remoteVideoSources);
|
||||
|
||||
for (const source of videoSources.keys()) {
|
||||
newRemoteVideoSources.add(source);
|
||||
}
|
||||
state.remoteVideoSources = newRemoteVideoSources;
|
||||
}
|
||||
}
|
||||
|
||||
return { ...state };
|
||||
}
|
||||
case RAISE_HAND_UPDATED: {
|
||||
return {
|
||||
...state,
|
||||
raisedHandsQueue: action.queue
|
||||
};
|
||||
}
|
||||
case SCREEN_SHARE_REMOTE_PARTICIPANTS_UPDATED: {
|
||||
const { participantIds } = action;
|
||||
const sortedSharesList = [];
|
||||
|
||||
for (const participant of participantIds) {
|
||||
const remoteParticipant = state.remote.get(participant);
|
||||
|
||||
if (remoteParticipant) {
|
||||
const displayName
|
||||
= _getDisplayName(state, remoteParticipant.name);
|
||||
|
||||
sortedSharesList.push([ participant, displayName ]);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the remote screen share list sorted alphabetically.
|
||||
sortedSharesList.length && sortedSharesList.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
// @ts-ignore
|
||||
state.sortedRemoteScreenshares = new Map(sortedSharesList);
|
||||
|
||||
return { ...state };
|
||||
}
|
||||
case OVERWRITE_PARTICIPANT_NAME: {
|
||||
const { id, name } = action;
|
||||
|
||||
@@ -548,8 +521,7 @@ function _participantJoined({ participant }: { participant: IParticipant; }) {
|
||||
name,
|
||||
pinned,
|
||||
presence,
|
||||
role,
|
||||
sources
|
||||
role
|
||||
} = participant;
|
||||
let { conference, id } = participant;
|
||||
|
||||
@@ -579,8 +551,7 @@ function _participantJoined({ participant }: { participant: IParticipant; }) {
|
||||
name,
|
||||
pinned: pinned || false,
|
||||
presence,
|
||||
role: role || PARTICIPANT_ROLE.NONE,
|
||||
sources
|
||||
role: role || PARTICIPANT_ROLE.NONE
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@ import _ from 'lodash';
|
||||
import { IStore } from '../../app/types';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import {
|
||||
getMultipleVideoSendingSupportFeatureFlag,
|
||||
getSsrcRewritingFeatureFlag
|
||||
getMultipleVideoSendingSupportFeatureFlag
|
||||
} from '../config/functions.any';
|
||||
import { VIDEO_TYPE } from '../media/constants';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
|
||||
import { createVirtualScreenshareParticipant, participantLeft } from './actions';
|
||||
import { getRemoteScreensharesBasedOnPresence } from './functions';
|
||||
import { FakeParticipant } from './types';
|
||||
|
||||
StateListenerRegistry.register(
|
||||
@@ -18,50 +15,13 @@ StateListenerRegistry.register(
|
||||
/* listener */(tracks, store) => _updateScreenshareParticipants(store)
|
||||
);
|
||||
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].remoteVideoSources,
|
||||
/* listener */(remoteVideoSources, store) => getSsrcRewritingFeatureFlag(store.getState())
|
||||
&& _updateScreenshareParticipantsBasedOnPresence(store)
|
||||
);
|
||||
|
||||
/**
|
||||
* Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
|
||||
* tiles accodingly.
|
||||
*
|
||||
* @param {Array<string>} oldScreenshareSourceNames - List of old screenshare source names.
|
||||
* @param {Array<string>} newScreenshareSourceNames - Current list of screenshare source names.
|
||||
* @param {Object} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _createOrRemoveVirtualParticipants(
|
||||
oldScreenshareSourceNames: string[],
|
||||
newScreenshareSourceNames: string[],
|
||||
store: IStore): void {
|
||||
const { dispatch, getState } = store;
|
||||
const conference = getCurrentConference(getState());
|
||||
const removedScreenshareSourceNames = _.difference(oldScreenshareSourceNames, newScreenshareSourceNames);
|
||||
const addedScreenshareSourceNames = _.difference(newScreenshareSourceNames, oldScreenshareSourceNames);
|
||||
|
||||
if (removedScreenshareSourceNames.length) {
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, {
|
||||
fakeParticipant: FakeParticipant.RemoteScreenShare
|
||||
})));
|
||||
}
|
||||
|
||||
if (addedScreenshareSourceNames.length) {
|
||||
addedScreenshareSourceNames.forEach(id => dispatch(
|
||||
createVirtualScreenshareParticipant(id, false, conference)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles creating and removing virtual screenshare participants.
|
||||
*
|
||||
* @param {*} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _updateScreenshareParticipants(store: IStore): void {
|
||||
const { dispatch, getState } = store;
|
||||
function _updateScreenshareParticipants({ getState, dispatch }: IStore) {
|
||||
const state = getState();
|
||||
const conference = getCurrentConference(state);
|
||||
const tracks = state['features/base/tracks'];
|
||||
@@ -71,7 +31,7 @@ function _updateScreenshareParticipants(store: IStore): void {
|
||||
let newLocalSceenshareSourceName;
|
||||
|
||||
const currentScreenshareSourceNames = tracks.reduce((acc: string[], track) => {
|
||||
if (track.videoType === VIDEO_TYPE.DESKTOP && !track.jitsiTrack.isMuted()) {
|
||||
if (track.videoType === 'desktop' && !track.jitsiTrack.isMuted()) {
|
||||
const sourceName: string = track.jitsiTrack.getSourceName();
|
||||
|
||||
if (track.local) {
|
||||
@@ -91,30 +51,24 @@ function _updateScreenshareParticipants(store: IStore): void {
|
||||
|
||||
if (localScreenShare && !newLocalSceenshareSourceName) {
|
||||
dispatch(participantLeft(localScreenShare.id, conference, {
|
||||
fakeParticipant: FakeParticipant.LocalScreenShare
|
||||
fakeParticipant: FakeParticipant.LocalScreenShare,
|
||||
isReplaced: undefined
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (getSsrcRewritingFeatureFlag(state)) {
|
||||
return;
|
||||
const removedScreenshareSourceNames = _.difference(previousScreenshareSourceNames, currentScreenshareSourceNames);
|
||||
const addedScreenshareSourceNames = _.difference(currentScreenshareSourceNames, previousScreenshareSourceNames);
|
||||
|
||||
if (removedScreenshareSourceNames.length) {
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, {
|
||||
fakeParticipant: FakeParticipant.RemoteScreenShare,
|
||||
isReplaced: undefined
|
||||
})));
|
||||
}
|
||||
|
||||
_createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the creation and removal of remote virtual screenshare participants when ssrc-rewriting is enabled.
|
||||
*
|
||||
* @param {Object} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _updateScreenshareParticipantsBasedOnPresence(store: IStore): void {
|
||||
const { getState } = store;
|
||||
const state = getState();
|
||||
const { sortedRemoteVirtualScreenshareParticipants } = state['features/base/participants'];
|
||||
const previousScreenshareSourceNames = [ ...sortedRemoteVirtualScreenshareParticipants.keys() ];
|
||||
const currentScreenshareSourceNames = getRemoteScreensharesBasedOnPresence(state);
|
||||
|
||||
_createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
|
||||
if (addedScreenshareSourceNames.length) {
|
||||
addedScreenshareSourceNames.forEach(id => dispatch(
|
||||
createVirtualScreenshareParticipant(id, false, conference)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ export interface IParticipant {
|
||||
region?: string;
|
||||
remoteControlSessionStatus?: boolean;
|
||||
role?: string;
|
||||
sources?: Map<string, Map<string, ISourceInfo>>;
|
||||
supportsRemoteControl?: boolean;
|
||||
}
|
||||
|
||||
@@ -52,16 +51,10 @@ export interface ILocalParticipant extends IParticipant {
|
||||
userSelectedMicDeviceLabel?: string;
|
||||
}
|
||||
|
||||
export interface ISourceInfo {
|
||||
muted: boolean;
|
||||
videoType: string;
|
||||
}
|
||||
|
||||
export interface IJitsiParticipant {
|
||||
getDisplayName: () => string;
|
||||
getId: () => string;
|
||||
getJid: () => string;
|
||||
getRole: () => string;
|
||||
getSources: () => Map<string, Map<string, ISourceInfo>>;
|
||||
isHidden: () => boolean;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { translate } from '../../../i18n/functions';
|
||||
import Icon from '../../../icons/components/Icon';
|
||||
import { IconArrowDown, IconWifi1Bar, IconWifi2Bars, IconWifi3Bars } from '../../../icons/svg';
|
||||
import { connect } from '../../../redux/functions';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
import { PREJOIN_DEFAULT_CONTENT_WIDTH } from '../../../ui/components/variables';
|
||||
import { CONNECTION_TYPE } from '../../constants';
|
||||
import { getConnectionData } from '../../functions';
|
||||
@@ -28,8 +27,11 @@ interface IProps extends WithTranslation {
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
connectionStatus: {
|
||||
borderRadius: '6px',
|
||||
color: '#fff',
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
fontSize: '12px',
|
||||
letterSpacing: '0.16px',
|
||||
lineHeight: '16px',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
|
||||
@@ -54,15 +56,14 @@ const useStyles = makeStyles()(theme => {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
padding: '12px 16px',
|
||||
borderRadius: theme.shape.borderRadius
|
||||
padding: '14px 16px'
|
||||
},
|
||||
|
||||
'& .con-status-circle': {
|
||||
borderRadius: '50%',
|
||||
display: 'inline-block',
|
||||
padding: theme.spacing(1),
|
||||
marginRight: theme.spacing(2)
|
||||
marginRight: theme.spacing(3)
|
||||
},
|
||||
|
||||
'& .con-status--good': {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import clsx from 'clsx';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
@@ -11,12 +9,12 @@ import { Toolbox } from '../../../../toolbox/components/web';
|
||||
import { getConferenceName } from '../../../conference/functions';
|
||||
import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from '../../../config/constants';
|
||||
import { getToolbarButtons, isToolbarButtonEnabled } from '../../../config/functions.web';
|
||||
import { connect } from '../../../redux/functions';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
|
||||
import ConnectionStatus from './ConnectionStatus';
|
||||
// @ts-ignore
|
||||
import Preview from './Preview';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
interface IProps {
|
||||
|
||||
@@ -53,7 +51,7 @@ interface IProps {
|
||||
/**
|
||||
* Indicates whether the copy url button should be shown.
|
||||
*/
|
||||
showCopyUrlButton?: boolean;
|
||||
showCopyUrlButton: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the device status should be shown.
|
||||
@@ -88,64 +86,7 @@ interface IProps {
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
container: {
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
inset: '0 0 0 0',
|
||||
display: 'flex',
|
||||
backgroundColor: theme.palette.ui01,
|
||||
zIndex: 252,
|
||||
|
||||
'@media (max-width: 720px)': {
|
||||
flexDirection: 'column-reverse'
|
||||
}
|
||||
},
|
||||
content: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
flexShrink: 0,
|
||||
boxSizing: 'border-box',
|
||||
margin: '0 48px',
|
||||
padding: '24px 0 16px',
|
||||
position: 'relative',
|
||||
width: '300px',
|
||||
height: '100%',
|
||||
zIndex: 252,
|
||||
|
||||
'@media (max-width: 720px)': {
|
||||
height: 'auto',
|
||||
margin: '0 auto'
|
||||
},
|
||||
|
||||
// mobile phone landscape
|
||||
'@media (max-width: 420px)': {
|
||||
padding: '16px 16px 0 16px',
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
'@media (max-width: 400px)': {
|
||||
padding: '16px'
|
||||
}
|
||||
},
|
||||
contentControls: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
margin: 'auto',
|
||||
width: '100%'
|
||||
},
|
||||
title: {
|
||||
...withPixelLineHeight(theme.typography.heading4),
|
||||
color: `${theme.palette.text01}!important`,
|
||||
marginBottom: theme.spacing(3),
|
||||
textAlign: 'center',
|
||||
|
||||
'@media (max-width: 400px)': {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
roomName: {
|
||||
subtitle: {
|
||||
...withPixelLineHeight(theme.typography.heading5),
|
||||
color: theme.palette.text01,
|
||||
marginBottom: theme.spacing(4),
|
||||
@@ -171,6 +112,7 @@ const PreMeetingScreen = ({
|
||||
videoTrack
|
||||
}: IProps) => {
|
||||
const { classes } = useStyles();
|
||||
const containerClassName = `premeeting-screen ${className ? className : ''}`;
|
||||
const style = _premeetingBackground ? {
|
||||
background: _premeetingBackground,
|
||||
backgroundPosition: 'center',
|
||||
@@ -178,17 +120,17 @@ const PreMeetingScreen = ({
|
||||
} : {};
|
||||
|
||||
return (
|
||||
<div className = { clsx('premeeting-screen', classes.container, className) }>
|
||||
<div className = { containerClassName }>
|
||||
<div style = { style }>
|
||||
<div className = { classes.content }>
|
||||
<div className = 'content'>
|
||||
<ConnectionStatus />
|
||||
|
||||
<div className = { classes.contentControls }>
|
||||
<h1 className = { classes.title }>
|
||||
<div className = 'content-controls'>
|
||||
<h1 className = 'title'>
|
||||
{title}
|
||||
</h1>
|
||||
{_roomName && (
|
||||
<span className = { classes.roomName }>
|
||||
<span className = { classes.subtitle }>
|
||||
{_roomName}
|
||||
</span>
|
||||
)}
|
||||
@@ -233,7 +175,7 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
|
||||
? premeetingButtons
|
||||
: premeetingButtons.filter(b => isToolbarButtonEnabled(b, toolbarButtons)),
|
||||
_premeetingBackground: premeetingBackground,
|
||||
_roomName: (hideConferenceSubject ? undefined : getConferenceName(state)) ?? ''
|
||||
_roomName: hideConferenceSubject ? undefined : getConferenceName(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
// @flow
|
||||
|
||||
import punycode from 'punycode';
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import ReactLinkify from 'react-linkify';
|
||||
|
||||
interface IProps {
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The children of the component.
|
||||
*/
|
||||
children: ReactNode;
|
||||
}
|
||||
children: React$Node
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a react wrapper for the react-linkify component.
|
||||
*/
|
||||
export default class Linkify extends Component<IProps> {
|
||||
export default class Linkify extends Component<Props> {
|
||||
/**
|
||||
* Implements {@Component#render}.
|
||||
*
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { toArray } from 'react-emoji-render';
|
||||
|
||||
import GifMessage from '../../../../chat/components/web/GifMessage';
|
||||
import { GIF_PREFIX } from '../../../../gifs/constants';
|
||||
import { isGifMessage } from '../../../../gifs/functions.web';
|
||||
import { isGifMessage } from '../../../../gifs/functions';
|
||||
|
||||
import Linkify from './Linkify';
|
||||
|
||||
@@ -12,7 +14,7 @@ type Props = {
|
||||
/**
|
||||
* The body of the message.
|
||||
*/
|
||||
text: string;
|
||||
text: string
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -39,7 +41,7 @@ class Message extends Component<Props> {
|
||||
*/
|
||||
_processMessage() {
|
||||
const { text } = this.props;
|
||||
const message: (string | ReactNode)[] = [];
|
||||
const message = [];
|
||||
|
||||
// Tokenize the text in order to avoid emoji substitution for URLs
|
||||
const tokens = text ? text.split(' ') : [];
|
||||
@@ -79,6 +81,8 @@ class Message extends Component<Props> {
|
||||
return message;
|
||||
}
|
||||
|
||||
_processMessage: () => Array<string | React$Element<*>>;
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -38,11 +38,6 @@ type Props = {
|
||||
*/
|
||||
_showJitsiWatermark: boolean,
|
||||
|
||||
/**
|
||||
* Whether the watermark should have a `top` and `left` value.
|
||||
*/
|
||||
noMargins: boolean;
|
||||
|
||||
/**
|
||||
* The default value for the Jitsi logo URL.
|
||||
*/
|
||||
@@ -165,9 +160,7 @@ class Watermarks extends Component<Props, State> {
|
||||
_logoUrl,
|
||||
_showJitsiWatermark
|
||||
} = this.props;
|
||||
const { noMargins, t } = this.props;
|
||||
const className = `watermark ${noMargins ? 'leftwatermarknomargin' : 'leftwatermark'}`;
|
||||
|
||||
const { t } = this.props;
|
||||
let reactElement = null;
|
||||
|
||||
if (_showJitsiWatermark) {
|
||||
@@ -179,14 +172,14 @@ class Watermarks extends Component<Props, State> {
|
||||
};
|
||||
|
||||
reactElement = (<div
|
||||
className = { className }
|
||||
className = 'watermark leftwatermark'
|
||||
style = { style } />);
|
||||
|
||||
if (_logoLink) {
|
||||
reactElement = (
|
||||
<a
|
||||
aria-label = { t('jitsiHome', { logo: interfaceConfig.APP_NAME }) }
|
||||
className = { className }
|
||||
className = 'watermark leftwatermark'
|
||||
href = { _logoLink }
|
||||
target = '_new'>
|
||||
{ reactElement }
|
||||
|
||||
@@ -52,15 +52,3 @@ export const SET_REDUCED_UI = 'SET_REDUCED_UI';
|
||||
*/
|
||||
export const SET_CONTEXT_MENU_OPEN = 'SET_CONTEXT_MENU_OPEN';
|
||||
|
||||
/**
|
||||
* The type of redux action which signals whether we are in narrow layout.
|
||||
*
|
||||
* {
|
||||
* type: SET_NARROW_LAYOUT,
|
||||
* isNarrow: boolean
|
||||
* }
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SET_NARROW_LAYOUT = 'SET_NARROW_LAYOUT';
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
SAFE_AREA_INSETS_CHANGED,
|
||||
SET_ASPECT_RATIO,
|
||||
SET_CONTEXT_MENU_OPEN,
|
||||
SET_NARROW_LAYOUT,
|
||||
SET_REDUCED_UI
|
||||
} from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW, ASPECT_RATIO_WIDE } from './constants';
|
||||
@@ -144,19 +143,3 @@ export function setSafeAreaInsets(insets: Object) {
|
||||
insets
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets narrow layout.
|
||||
*
|
||||
* @param {boolean} isNarrow - Whether is narrow layout.
|
||||
* @returns {{
|
||||
* type: SET_NARROW_LAYOUT,
|
||||
* isNarrow: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setNarrowLayout(isNarrow: boolean) {
|
||||
return {
|
||||
type: SET_NARROW_LAYOUT,
|
||||
isNarrow
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
SAFE_AREA_INSETS_CHANGED,
|
||||
SET_ASPECT_RATIO,
|
||||
SET_CONTEXT_MENU_OPEN,
|
||||
SET_NARROW_LAYOUT,
|
||||
SET_REDUCED_UI
|
||||
} from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW } from './constants';
|
||||
@@ -23,7 +22,6 @@ const DEFAULT_STATE = {
|
||||
aspectRatio: ASPECT_RATIO_NARROW,
|
||||
clientHeight: innerHeight,
|
||||
clientWidth: innerWidth,
|
||||
isNarrowLayout: false,
|
||||
reducedUI: false,
|
||||
contextMenuOpened: false
|
||||
};
|
||||
@@ -33,7 +31,6 @@ export interface IResponsiveUIState {
|
||||
clientHeight: number;
|
||||
clientWidth: number;
|
||||
contextMenuOpened: boolean;
|
||||
isNarrowLayout: boolean;
|
||||
reducedUI: boolean;
|
||||
safeAreaInsets?: {
|
||||
bottom: number;
|
||||
@@ -68,9 +65,6 @@ ReducerRegistry.register<IResponsiveUIState>('features/base/responsive-ui',
|
||||
|
||||
case SET_CONTEXT_MENU_OPEN:
|
||||
return set(state, 'contextMenuOpened', action.isOpen);
|
||||
|
||||
case SET_NARROW_LAYOUT:
|
||||
return set(state, 'isNarrowLayout', action.isNarrow);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { IStateful } from '../app/types';
|
||||
import CONFIG_WHITELIST from '../config/configWhitelist';
|
||||
import { IConfigState } from '../config/reducer';
|
||||
import { IJwtState } from '../jwt/reducer';
|
||||
import { getParticipantCount } from '../participants/functions';
|
||||
import { toState } from '../redux/functions';
|
||||
import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
@@ -113,6 +114,16 @@ export function shouldHideShareAudioHelper(state: IReduxState): boolean | undefi
|
||||
return state['features/base/settings'].hideShareAudioHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we should hide self view.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function shouldHideSelfView(state: IReduxState) {
|
||||
return getParticipantCount(state) === 1 ? false : getHideSelfView(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the disable self view setting.
|
||||
*
|
||||
|
||||
@@ -66,16 +66,6 @@ export const TRACK_MUTE_UNMUTE_FAILED = 'TRACK_MUTE_UNMUTE_FAILED';
|
||||
*/
|
||||
export const TRACK_NO_DATA_FROM_SOURCE = 'TRACK_NO_DATA_FROM_SOURCE';
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when the owner of a track changes due to ssrc remapping.
|
||||
*
|
||||
* {
|
||||
* type: TRACK_OWNER_CHANGED,
|
||||
* track: Track
|
||||
* }
|
||||
*/
|
||||
export const TRACK_OWNER_CHANGED = 'TRACK_OWNER_CHANGED';
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when a track has been (locally or
|
||||
* remotely) removed from the conference.
|
||||
@@ -106,7 +96,7 @@ export const TRACK_STOPPED = 'TRACK_STOPPED';
|
||||
* }
|
||||
*/
|
||||
export const TRACK_UPDATED = 'TRACK_UPDATED';
|
||||
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when a local track starts being created
|
||||
* via a WebRTC {@code getUserMedia} call. The action's payload includes an
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
TRACK_CREATE_ERROR,
|
||||
TRACK_MUTE_UNMUTE_FAILED,
|
||||
TRACK_NO_DATA_FROM_SOURCE,
|
||||
TRACK_OWNER_CHANGED,
|
||||
TRACK_REMOVED,
|
||||
TRACK_STOPPED,
|
||||
TRACK_UPDATED,
|
||||
@@ -378,9 +377,7 @@ export function trackAdded(track: any) {
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,
|
||||
(type: VideoType) => dispatch(trackVideoTypeChanged(track, type)));
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_OWNER_CHANGED,
|
||||
(owner: string) => dispatch(trackOwnerChanged(track, owner)));
|
||||
|
||||
const local = track.isLocal();
|
||||
const isVirtualScreenshareParticipantCreated = !local || getMultipleVideoSendingSupportFeatureFlag(getState());
|
||||
const mediaType = track.getVideoType() === VIDEO_TYPE.DESKTOP && isVirtualScreenshareParticipantCreated
|
||||
@@ -585,14 +582,11 @@ export function trackVideoStarted(track: any): {
|
||||
* }}
|
||||
*/
|
||||
export function trackVideoTypeChanged(track: any, videoType: VideoType) {
|
||||
const mediaType = videoType === VIDEO_TYPE.CAMERA ? MEDIA_TYPE.VIDEO : MEDIA_TYPE.SCREENSHARE;
|
||||
|
||||
return {
|
||||
type: TRACK_UPDATED,
|
||||
track: {
|
||||
jitsiTrack: track,
|
||||
videoType,
|
||||
mediaType
|
||||
videoType
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -623,32 +617,6 @@ export function trackStreamingStatusChanged(track: any, streamingStatus: string)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action for when the owner of the track changes due to ssrc remapping.
|
||||
*
|
||||
* @param {(JitsiRemoteTrack)} track - JitsiTrack instance.
|
||||
* @param {string} participantId - New owner's participant ID.
|
||||
* @returns {{
|
||||
* type: TRACK_OWNER_CHANGED,
|
||||
* track: Track
|
||||
* }}
|
||||
*/
|
||||
export function trackOwnerChanged(track: any, participantId: string): {
|
||||
track: {
|
||||
jitsiTrack: any;
|
||||
participantId: string;
|
||||
};
|
||||
type: 'TRACK_OWNER_CHANGED';
|
||||
} {
|
||||
return {
|
||||
type: TRACK_OWNER_CHANGED,
|
||||
track: {
|
||||
jitsiTrack: track,
|
||||
participantId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals passed tracks to be added.
|
||||
*
|
||||
|
||||
@@ -2,14 +2,16 @@ import { IReduxState, IStore } from '../../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
|
||||
import { showNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
import { setAudioOnly } from '../audio-only/actions';
|
||||
import JitsiMeetJS from '../lib-jitsi-meet';
|
||||
import {
|
||||
setScreenshareMuted,
|
||||
setVideoMuted
|
||||
} from '../media/actions';
|
||||
import { VIDEO_MUTISM_AUTHORITY } from '../media/constants';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
VIDEO_MUTISM_AUTHORITY
|
||||
} from '../media/constants';
|
||||
|
||||
import { addLocalTrack, replaceLocalTrack } from './actions.any';
|
||||
import { getLocalDesktopTrack, getTrackState, isLocalVideoTrackDesktop } from './functions.native';
|
||||
@@ -38,7 +40,7 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
|
||||
}
|
||||
} else {
|
||||
dispatch(setScreenshareMuted(true));
|
||||
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
|
||||
dispatch(setVideoMuted(false, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
|
||||
setPictureInPictureEnabled(true);
|
||||
}
|
||||
};
|
||||
@@ -71,16 +73,12 @@ async function _startScreenSharing(dispatch: Function, state: IReduxState) {
|
||||
dispatch(addLocalTrack(track));
|
||||
}
|
||||
|
||||
dispatch(setVideoMuted(true, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
|
||||
dispatch(setVideoMuted(true, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
|
||||
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
|
||||
if (audioOnly) {
|
||||
dispatch(showNotification({
|
||||
titleKey: 'notify.screenSharingAudioOnlyTitle',
|
||||
descriptionKey: 'notify.screenSharingAudioOnlyDescription',
|
||||
maxLines: 3
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
dispatch(setAudioOnly(false));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('ERROR creating ScreeSharing stream ', error);
|
||||
|
||||
@@ -14,6 +14,7 @@ import { isAudioOnlySharing, isScreenVideoShared } from '../../screen-share/func
|
||||
import { isScreenshotCaptureEnabled, toggleScreenshotCaptureSummary } from '../../screenshot-capture';
|
||||
// @ts-ignore
|
||||
import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { setAudioOnly } from '../audio-only/actions';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
|
||||
import { setScreenshareMuted } from '../media/actions';
|
||||
@@ -226,15 +227,12 @@ async function _toggleScreenSharing(
|
||||
}
|
||||
}
|
||||
|
||||
// Show notification about more bandwidth usage in audio-only mode if the user starts screensharing. This
|
||||
// doesn't apply to audio-only screensharing.
|
||||
// Disable audio-only or best performance mode if the user starts screensharing. This doesn't apply to
|
||||
// audio-only screensharing.
|
||||
const { enabled: bestPerformanceMode } = state['features/base/audio-only'];
|
||||
|
||||
if (bestPerformanceMode && !audioOnly) {
|
||||
dispatch(showNotification({
|
||||
titleKey: 'notify.screenSharingAudioOnlyTitle',
|
||||
descriptionKey: 'notify.screenSharingAudioOnlyDescription'
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
dispatch(setAudioOnly(false));
|
||||
}
|
||||
} else {
|
||||
const { desktopAudioTrack } = state['features/screen-share'];
|
||||
|
||||
@@ -79,7 +79,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
|
||||
case SET_SCREENSHARE_MUTED:
|
||||
_setMuted(store, action, MEDIA_TYPE.SCREENSHARE);
|
||||
_setMuted(store, action, action.mediaType);
|
||||
break;
|
||||
|
||||
case SET_VIDEO_MUTED:
|
||||
@@ -88,7 +88,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
return;
|
||||
}
|
||||
|
||||
_setMuted(store, action, MEDIA_TYPE.VIDEO);
|
||||
_setMuted(store, action, action.mediaType);
|
||||
break;
|
||||
|
||||
case TOGGLE_CAMERA_FACING_MODE: {
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
TRACK_CREATE_CANCELED,
|
||||
TRACK_CREATE_ERROR,
|
||||
TRACK_NO_DATA_FROM_SOURCE,
|
||||
TRACK_OWNER_CHANGED,
|
||||
TRACK_REMOVED,
|
||||
TRACK_UPDATED,
|
||||
TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
|
||||
@@ -42,18 +41,6 @@ function track(state: ITrack, action: any) {
|
||||
}
|
||||
break;
|
||||
|
||||
case TRACK_OWNER_CHANGED: {
|
||||
const t = action.track;
|
||||
|
||||
if (state.jitsiTrack === t.jitsiTrack) {
|
||||
return {
|
||||
...state,
|
||||
participantId: t.participantId
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TRACK_UPDATED: {
|
||||
const t = action.track;
|
||||
|
||||
@@ -116,10 +103,10 @@ ReducerRegistry.register<ITracksState>('features/base/tracks', (state = [], acti
|
||||
switch (action.type) {
|
||||
case PARTICIPANT_ID_CHANGED:
|
||||
case TRACK_NO_DATA_FROM_SOURCE:
|
||||
case TRACK_OWNER_CHANGED:
|
||||
case TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT:
|
||||
case TRACK_UPDATED:
|
||||
return state.map((t: ITrack) => track(t, action));
|
||||
|
||||
case TRACK_ADDED: {
|
||||
let withoutTrackStub = state;
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ export const colors = {
|
||||
// after we replace them in the components.
|
||||
primary10: '#17A0DB',
|
||||
primary11: '#1081B2',
|
||||
primary12: '#B8C7E0',
|
||||
surface00: '#111111',
|
||||
surface12: '#AAAAAA',
|
||||
surface13: '#495258',
|
||||
@@ -198,6 +199,7 @@ export const colorMap = {
|
||||
border01: 'surface08',
|
||||
border02: 'surface06',
|
||||
border03: 'surface04',
|
||||
border04: 'primary12',
|
||||
border05: 'surface07',
|
||||
borderError: 'error06',
|
||||
warning03: 'warning07',
|
||||
|
||||
@@ -90,8 +90,8 @@ const Button: React.FC<IProps> = ({
|
||||
return (
|
||||
<NativePaperButton
|
||||
accessibilityLabel = { t(accessibilityLabel ?? '') }
|
||||
buttonColor = { color }
|
||||
children = { t(labelKey ?? '') }
|
||||
color = { color }
|
||||
contentStyle = { [
|
||||
styles.buttonContent,
|
||||
contentStyle
|
||||
|
||||
@@ -55,7 +55,6 @@ export default {
|
||||
buttonLabelTertiary: {
|
||||
...buttonLabel,
|
||||
color: BaseTheme.palette.text01,
|
||||
margin: BaseTheme.spacing[3],
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
|
||||
@@ -34,11 +34,6 @@ export interface IButtonProps {
|
||||
*/
|
||||
onClick?: (e?: React.MouseEvent<HTMLButtonElement> | GestureResponderEvent) => void;
|
||||
|
||||
/**
|
||||
* Key press callback.
|
||||
*/
|
||||
onKeyPress?: (e?: React.KeyboardEvent<HTMLButtonElement>) => void;
|
||||
|
||||
/**
|
||||
* The type of button to be displayed.
|
||||
*/
|
||||
|
||||
@@ -187,7 +187,6 @@ const Button = React.forwardRef<any, any>(({
|
||||
label,
|
||||
labelKey,
|
||||
onClick = () => null,
|
||||
onKeyPress = () => null,
|
||||
size = 'medium',
|
||||
testId,
|
||||
type = BUTTON_TYPES.PRIMARY
|
||||
@@ -207,7 +206,6 @@ const Button = React.forwardRef<any, any>(({
|
||||
disabled = { disabled }
|
||||
{ ...(id ? { id } : {}) }
|
||||
onClick = { onClick }
|
||||
onKeyPress = { onKeyPress }
|
||||
ref = { ref }
|
||||
title = { accessibilityLabel }
|
||||
type = { isSubmit ? 'submit' : 'button' }>
|
||||
|
||||
@@ -68,7 +68,7 @@ const useStyles = makeStyles()(theme => {
|
||||
flexDirection: 'column',
|
||||
height: 'auto',
|
||||
minHeight: '200px',
|
||||
maxHeight: '80vh',
|
||||
maxHeight: '760px',
|
||||
marginTop: '64px',
|
||||
animation: `${keyframes`
|
||||
0% {
|
||||
|
||||
@@ -10,7 +10,6 @@ import { IInputProps } from '../types';
|
||||
|
||||
interface IProps extends IInputProps {
|
||||
accessibilityLabel?: string;
|
||||
autoComplete?: string;
|
||||
autoFocus?: boolean;
|
||||
bottomLabel?: string;
|
||||
className?: string;
|
||||
@@ -22,7 +21,6 @@ interface IProps extends IInputProps {
|
||||
name?: string;
|
||||
onKeyPress?: (e: React.KeyboardEvent) => void;
|
||||
readOnly?: boolean;
|
||||
required?: boolean;
|
||||
textarea?: boolean;
|
||||
type?: 'text' | 'email' | 'number' | 'password';
|
||||
}
|
||||
@@ -132,7 +130,6 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
const Input = React.forwardRef<any, IProps>(({
|
||||
accessibilityLabel,
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
bottomLabel,
|
||||
className,
|
||||
@@ -151,7 +148,6 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
onKeyPress,
|
||||
placeholder,
|
||||
readOnly = false,
|
||||
required,
|
||||
textarea = false,
|
||||
type = 'text',
|
||||
value
|
||||
@@ -177,13 +173,11 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
{textarea ? (
|
||||
<TextareaAutosize
|
||||
aria-label = { accessibilityLabel }
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { cx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
|
||||
disabled = { disabled }
|
||||
{ ...(id ? { id } : {}) }
|
||||
maxLength = { maxLength }
|
||||
maxRows = { maxRows }
|
||||
minRows = { minRows }
|
||||
name = { name }
|
||||
@@ -192,12 +186,10 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
placeholder = { placeholder }
|
||||
readOnly = { readOnly }
|
||||
ref = { ref }
|
||||
required = { required }
|
||||
value = { value } />
|
||||
) : (
|
||||
<input
|
||||
aria-label = { accessibilityLabel }
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { cx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
|
||||
@@ -210,7 +202,6 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
placeholder = { placeholder }
|
||||
readOnly = { readOnly }
|
||||
ref = { ref }
|
||||
required = { required }
|
||||
type = { type }
|
||||
value = { value } />
|
||||
)}
|
||||
|
||||
@@ -265,7 +265,15 @@ export const commonStyles = (theme: Theme) => {
|
||||
padding: 6,
|
||||
textAlign: 'center' as const,
|
||||
pointerEvents: 'all' as const,
|
||||
boxShadow: '0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15)'
|
||||
boxShadow: '0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15)',
|
||||
|
||||
'& > div': {
|
||||
marginLeft: 8,
|
||||
|
||||
'&:first-child': {
|
||||
marginLeft: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { parseURLParams } from './parseURLParams';
|
||||
import { normalizeNFKC } from './strings';
|
||||
|
||||
/**
|
||||
* Http status codes.
|
||||
*/
|
||||
export enum StatusCode {
|
||||
PaymentRequired = 402
|
||||
}
|
||||
|
||||
/**
|
||||
* The app linking scheme.
|
||||
* TODO: This should be read from the manifest files later.
|
||||
|
||||
@@ -215,7 +215,9 @@ export function moveToRoom(roomId?: string) {
|
||||
}
|
||||
|
||||
dispatch(clearNotifications());
|
||||
dispatch(createConference(_roomId));
|
||||
|
||||
// dispatch(setRoom(_roomId));
|
||||
dispatch(createConference(_roomId?.toString()));
|
||||
dispatch(setAudioMuted(audio.muted));
|
||||
dispatch(setVideoMuted(Boolean(video.muted)));
|
||||
dispatch(createDesiredLocalTracks());
|
||||
|
||||
@@ -120,7 +120,7 @@ export function sendMessage(message: string, ignorePrivacy = false) {
|
||||
* type: SET_PRIVATE_MESSAGE_RECIPIENT
|
||||
* }}
|
||||
*/
|
||||
export function setPrivateMessageRecipient(participant?: Object) {
|
||||
export function setPrivateMessageRecipient(participant: Object) {
|
||||
return {
|
||||
participant,
|
||||
type: SET_PRIVATE_MESSAGE_RECIPIENT
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
// @flow
|
||||
|
||||
import { getLocalizedDateFormatter } from '../../base/i18n/dateUtil';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { getLocalizedDateFormatter } from '../../base/i18n';
|
||||
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../constants';
|
||||
import { IMessage } from '../reducer';
|
||||
|
||||
/**
|
||||
* Formatter string to display the message timestamp.
|
||||
@@ -13,41 +13,46 @@ const TIMESTAMP_FORMAT = 'H:mm';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@code AbstractChatMessage}.
|
||||
*/
|
||||
export interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Whether current participant is currently knocking in the lobby room.
|
||||
*/
|
||||
knocking: boolean;
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* The representation of a chat message.
|
||||
*/
|
||||
message: IMessage;
|
||||
message: Object,
|
||||
|
||||
/**
|
||||
* Whether or not the avatar image of the participant which sent the message
|
||||
* should be displayed.
|
||||
*/
|
||||
showAvatar?: boolean;
|
||||
showAvatar: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the name of the participant which sent the message should
|
||||
* be displayed.
|
||||
*/
|
||||
showDisplayName: boolean;
|
||||
showDisplayName: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the time at which the message was sent should be
|
||||
* displayed.
|
||||
*/
|
||||
showTimestamp: boolean;
|
||||
}
|
||||
showTimestamp: boolean,
|
||||
|
||||
/**
|
||||
* Whether current participant is currently knocking in the lobby room.
|
||||
*/
|
||||
knocking: boolean,
|
||||
|
||||
/**
|
||||
* Invoked to receive translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract component to display a chat message.
|
||||
*/
|
||||
export default class AbstractChatMessage<P extends IProps> extends PureComponent<P> {
|
||||
export default class AbstractChatMessage<P: Props> extends PureComponent<P> {
|
||||
/**
|
||||
* Returns the timestamp to display for the message.
|
||||
*
|
||||
@@ -1,50 +1,53 @@
|
||||
// @flow
|
||||
|
||||
import { PureComponent } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { getParticipantDisplayName, isLocalParticipantModerator } from '../../base/participants/functions';
|
||||
import { getParticipantDisplayName, isLocalParticipantModerator } from '../../base/participants';
|
||||
import { setPrivateMessageRecipient } from '../actions';
|
||||
import { setLobbyChatActiveState } from '../actions.any';
|
||||
import { setPrivateMessageRecipient } from '../actions.web';
|
||||
|
||||
export interface IProps extends WithTranslation {
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* Is lobby messaging active.
|
||||
*/
|
||||
_isLobbyChatActive: boolean;
|
||||
* Function used to translate i18n labels.
|
||||
*/
|
||||
t: Function,
|
||||
|
||||
/**
|
||||
* The name of the lobby message recipient, if any.
|
||||
*/
|
||||
_lobbyMessageRecipient?: string;
|
||||
* Function to remove the recipent setting of the chat window.
|
||||
*/
|
||||
_onRemovePrivateMessageRecipient: Function,
|
||||
|
||||
/**
|
||||
/**
|
||||
* Function to make the lobby message recipient inactive.
|
||||
*/
|
||||
_onHideLobbyChatRecipient: () => void;
|
||||
|
||||
/**
|
||||
* Function to remove the recipient setting of the chat window.
|
||||
*/
|
||||
_onRemovePrivateMessageRecipient: () => void;
|
||||
_onHideLobbyChatRecipient: Function,
|
||||
|
||||
/**
|
||||
* The name of the message recipient, if any.
|
||||
*/
|
||||
_privateMessageRecipient?: string;
|
||||
_privateMessageRecipient: ?string,
|
||||
|
||||
/**
|
||||
/**
|
||||
* Is lobby messaging active.
|
||||
*/
|
||||
_isLobbyChatActive: boolean,
|
||||
|
||||
/**
|
||||
* The name of the lobby message recipient, if any.
|
||||
*/
|
||||
_lobbyMessageRecipient: ?string,
|
||||
|
||||
/**
|
||||
* Shows widget if it is necessary.
|
||||
*/
|
||||
_visible: boolean;
|
||||
|
||||
classes?: any;
|
||||
}
|
||||
_visible: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class for the {@code MessageRecipient} component.
|
||||
*/
|
||||
export default class AbstractMessageRecipient<P extends IProps> extends PureComponent<P> {
|
||||
export default class AbstractMessageRecipient<P: Props> extends PureComponent<P> {
|
||||
|
||||
}
|
||||
|
||||
@@ -52,9 +55,9 @@ export default class AbstractMessageRecipient<P extends IProps> extends PureComp
|
||||
* Maps part of the props of this component to Redux actions.
|
||||
*
|
||||
* @param {Function} dispatch - The Redux dispatch function.
|
||||
* @returns {IProps}
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function _mapDispatchToProps(dispatch: Function) {
|
||||
export function _mapDispatchToProps(dispatch: Function): $Shape<Props> {
|
||||
return {
|
||||
_onRemovePrivateMessageRecipient: () => {
|
||||
dispatch(setPrivateMessageRecipient());
|
||||
@@ -69,9 +72,9 @@ export function _mapDispatchToProps(dispatch: Function) {
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {IProps}
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function _mapStateToProps(state: IReduxState) {
|
||||
export function _mapStateToProps(state: Object): $Shape<Props> {
|
||||
const { privateMessageRecipient, lobbyMessageRecipient, isLobbyChatActive } = state['features/chat'];
|
||||
|
||||
return {
|
||||
131
react/features/chat/components/web/ChatMessage.js
Normal file
131
react/features/chat/components/web/ChatMessage.js
Normal file
@@ -0,0 +1,131 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import Message from '../../../base/react/components/web/Message';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import AbstractChatMessage, { type Props } from '../AbstractChatMessage';
|
||||
|
||||
import PrivateMessageButton from './PrivateMessageButton';
|
||||
|
||||
/**
|
||||
* Renders a single chat message.
|
||||
*/
|
||||
class ChatMessage extends AbstractChatMessage<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { message, t, knocking } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = 'chatmessage-wrapper'
|
||||
id = { this.props.message.messageId }
|
||||
tabIndex = { -1 }>
|
||||
<div
|
||||
className = { `chatmessage ${message.privateMessage ? 'privatemessage' : ''} ${
|
||||
message.lobbyChat && !knocking ? 'lobbymessage' : ''}` }>
|
||||
<div className = 'replywrapper'>
|
||||
<div className = 'messagecontent'>
|
||||
{ this.props.showDisplayName && this._renderDisplayName() }
|
||||
<div className = 'usermessage'>
|
||||
<span className = 'sr-only'>
|
||||
{ this.props.message.displayName === this.props.message.recipient
|
||||
? t('chat.messageAccessibleTitleMe')
|
||||
: t('chat.messageAccessibleTitle',
|
||||
{ user: this.props.message.displayName }) }
|
||||
</span>
|
||||
<Message text = { this._getMessageText() } />
|
||||
</div>
|
||||
{ (message.privateMessage || (message.lobbyChat && !knocking))
|
||||
&& this._renderPrivateNotice() }
|
||||
</div>
|
||||
{ (message.privateMessage || (message.lobbyChat && !knocking))
|
||||
&& message.messageType !== MESSAGE_TYPE_LOCAL
|
||||
&& (
|
||||
<div
|
||||
className = { `messageactions ${
|
||||
message.lobbyChat ? 'lobbychatmessageactions' : ''}` }>
|
||||
<PrivateMessageButton
|
||||
isLobbyMessage = { message.lobbyChat }
|
||||
participantID = { message.id }
|
||||
reply = { true }
|
||||
showLabel = { false } />
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
{ this.props.showTimestamp && this._renderTimestamp() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_getFormattedTimestamp: () => string;
|
||||
|
||||
_getMessageText: () => string;
|
||||
|
||||
_getPrivateNoticeMessage: () => string;
|
||||
|
||||
/**
|
||||
* Renders the display name of the sender.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderDisplayName() {
|
||||
return (
|
||||
<div
|
||||
aria-hidden = { true }
|
||||
className = 'display-name'>
|
||||
{ this.props.message.displayName }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the message privacy notice.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderPrivateNotice() {
|
||||
return (
|
||||
<div className = 'privatemessagenotice'>
|
||||
{ this._getPrivateNoticeMessage() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the time at which the message was sent.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderTimestamp() {
|
||||
return (
|
||||
<div className = 'timestamp'>
|
||||
{ this._getFormattedTimestamp() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state: Object): $Shape<Props> {
|
||||
const { knocking } = state['features/lobby'];
|
||||
|
||||
return {
|
||||
knocking
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(ChatMessage));
|
||||
@@ -1,224 +0,0 @@
|
||||
import { Theme } from '@mui/material';
|
||||
import { withStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Message from '../../../base/react/components/web/Message';
|
||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||
import { MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import AbstractChatMessage, { IProps as AbstractProps } from '../AbstractChatMessage';
|
||||
|
||||
import PrivateMessageButton from './PrivateMessageButton';
|
||||
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
classes: any;
|
||||
|
||||
type: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) => {
|
||||
return {
|
||||
chatMessageWrapper: {
|
||||
maxWidth: '100%'
|
||||
},
|
||||
|
||||
chatMessage: {
|
||||
display: 'inline-flex',
|
||||
padding: '12px',
|
||||
backgroundColor: theme.palette.ui02,
|
||||
borderRadius: '4px 12px 12px 12px',
|
||||
boxSizing: 'border-box' as const,
|
||||
maxWidth: '100%',
|
||||
marginTop: '4px',
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.support05
|
||||
},
|
||||
|
||||
'&.local': {
|
||||
backgroundColor: theme.palette.ui04,
|
||||
borderRadius: '12px 4px 12px 12px',
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.support05
|
||||
}
|
||||
},
|
||||
|
||||
'&.error': {
|
||||
backgroundColor: 'rgb(215, 121, 118)',
|
||||
borderRadius: 0,
|
||||
fontWeight: 100
|
||||
},
|
||||
|
||||
'&.lobbymessage': {
|
||||
backgroundColor: theme.palette.support05
|
||||
}
|
||||
},
|
||||
|
||||
replyWrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center'
|
||||
},
|
||||
|
||||
messageContent: {
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden',
|
||||
flex: 1
|
||||
},
|
||||
|
||||
replyButtonContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
height: '100%'
|
||||
},
|
||||
|
||||
replyButton: {
|
||||
padding: '2px'
|
||||
},
|
||||
|
||||
displayName: {
|
||||
...withPixelLineHeight(theme.typography.labelBold),
|
||||
color: theme.palette.text02,
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
marginBottom: theme.spacing(1)
|
||||
},
|
||||
|
||||
userMessage: {
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
color: theme.palette.text01,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word'
|
||||
},
|
||||
|
||||
privateMessageNotice: {
|
||||
...withPixelLineHeight(theme.typography.labelRegular),
|
||||
color: theme.palette.text02,
|
||||
marginTop: theme.spacing(1)
|
||||
},
|
||||
|
||||
timestamp: {
|
||||
...withPixelLineHeight(theme.typography.labelRegular),
|
||||
color: theme.palette.text03,
|
||||
marginTop: theme.spacing(1)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a single chat message.
|
||||
*/
|
||||
class ChatMessage extends AbstractChatMessage<IProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { message, t, knocking, classes, type } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { classes.chatMessageWrapper }
|
||||
id = { this.props.message.messageId }
|
||||
tabIndex = { -1 }>
|
||||
<div
|
||||
className = { clsx('chatmessage', classes.chatMessage, type,
|
||||
message.privateMessage && 'privatemessage',
|
||||
message.lobbyChat && !knocking && 'lobbymessage') }>
|
||||
<div className = { classes.replyWrapper }>
|
||||
<div className = { clsx('messagecontent', classes.messageContent) }>
|
||||
{ this.props.showDisplayName && this._renderDisplayName() }
|
||||
<div className = { clsx('usermessage', classes.userMessage) }>
|
||||
<span className = 'sr-only'>
|
||||
{ this.props.message.displayName === this.props.message.recipient
|
||||
? t('chat.messageAccessibleTitleMe')
|
||||
: t('chat.messageAccessibleTitle',
|
||||
{ user: this.props.message.displayName }) }
|
||||
</span>
|
||||
<Message text = { this._getMessageText() } />
|
||||
</div>
|
||||
{ (message.privateMessage || (message.lobbyChat && !knocking))
|
||||
&& this._renderPrivateNotice() }
|
||||
</div>
|
||||
{ (message.privateMessage || (message.lobbyChat && !knocking))
|
||||
&& message.messageType !== MESSAGE_TYPE_LOCAL
|
||||
&& (
|
||||
<div
|
||||
className = { classes.replyButtonContainer }>
|
||||
<PrivateMessageButton
|
||||
isLobbyMessage = { message.lobbyChat }
|
||||
participantID = { message.id } />
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
{ this.props.showTimestamp && this._renderTimestamp() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the display name of the sender.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderDisplayName() {
|
||||
return (
|
||||
<div
|
||||
aria-hidden = { true }
|
||||
className = { clsx('display-name', this.props.classes.displayName) }>
|
||||
{ this.props.message.displayName }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the message privacy notice.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderPrivateNotice() {
|
||||
return (
|
||||
<div className = { this.props.classes.privateMessageNotice }>
|
||||
{ this._getPrivateNoticeMessage() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the time at which the message was sent.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
_renderTimestamp() {
|
||||
return (
|
||||
<div className = { clsx('timestamp', this.props.classes.timestamp) }>
|
||||
{ this._getFormattedTimestamp() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { knocking } = state['features/lobby'];
|
||||
|
||||
return {
|
||||
knocking
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(withStyles(styles)(ChatMessage)));
|
||||
59
react/features/chat/components/web/ChatMessageGroup.js
Normal file
59
react/features/chat/components/web/ChatMessageGroup.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import ChatMessage from './ChatMessage';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Additional CSS classes to apply to the root element.
|
||||
*/
|
||||
className: string,
|
||||
|
||||
/**
|
||||
* The messages to display as a group.
|
||||
*/
|
||||
messages: Array<Object>,
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a list of chat messages. Will show only the display name for the
|
||||
* first chat message and the timestamp for the last chat message.
|
||||
*
|
||||
* @augments React.Component
|
||||
*/
|
||||
class ChatMessageGroup extends Component<Props> {
|
||||
static defaultProps = {
|
||||
className: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { className, messages } = this.props;
|
||||
|
||||
const messagesLength = messages.length;
|
||||
|
||||
if (!messagesLength) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = { `chat-message-group ${className}` }>
|
||||
{ messages.map((message, i) => (
|
||||
<ChatMessage
|
||||
key = { i }
|
||||
message = { message }
|
||||
showDisplayName = { i === 0 }
|
||||
showTimestamp = { i === messages.length - 1 } />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatMessageGroup;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user