mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-04 22:02:28 +00:00
Compare commits
95 Commits
7160
...
android-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2521bc67a | ||
|
|
38a293f8f6 | ||
|
|
13e8f992b5 | ||
|
|
c384d0d3a9 | ||
|
|
2b71fa512b | ||
|
|
d381ceb040 | ||
|
|
e18c428f52 | ||
|
|
9fc32dc59b | ||
|
|
6f45622ef1 | ||
|
|
e56c7070c2 | ||
|
|
0aef7a36aa | ||
|
|
c030cf941e | ||
|
|
f6760e4ac7 | ||
|
|
9060c77307 | ||
|
|
a1d018eef4 | ||
|
|
3f724d8fb7 | ||
|
|
49d69a5a02 | ||
|
|
6db9e42876 | ||
|
|
ad3e8f9f53 | ||
|
|
9f39caa247 | ||
|
|
1402a63324 | ||
|
|
a9863e65c3 | ||
|
|
646c58f7d1 | ||
|
|
a78ea7ca9c | ||
|
|
8b8565bf60 | ||
|
|
b9e30f3c1b | ||
|
|
96b6edccf8 | ||
|
|
2af9dc88e6 | ||
|
|
eda25ca3c9 | ||
|
|
c99da17973 | ||
|
|
9e147d7842 | ||
|
|
d7afaf871f | ||
|
|
c4f6d37aa1 | ||
|
|
a5663872d9 | ||
|
|
007283aab3 | ||
|
|
3b612376f2 | ||
|
|
1a312e2140 | ||
|
|
6f5d0400b8 | ||
|
|
aec86cecc0 | ||
|
|
bf1dde7cd1 | ||
|
|
91e9005f08 | ||
|
|
57f9ea2865 | ||
|
|
1f6425fbfd | ||
|
|
e169979bab | ||
|
|
6e9e9c9a6a | ||
|
|
102a369bca | ||
|
|
b318b987a7 | ||
|
|
78ce68160a | ||
|
|
6aff616af4 | ||
|
|
0140a49641 | ||
|
|
732754c566 | ||
|
|
b360a9e572 | ||
|
|
f88fa81616 | ||
|
|
e0e66119f5 | ||
|
|
ba4784f149 | ||
|
|
7fb7c3de9c | ||
|
|
3d2d449d31 | ||
|
|
ca60c33dda | ||
|
|
162512496a | ||
|
|
7819c97839 | ||
|
|
e9c8603c3c | ||
|
|
9e165c337a | ||
|
|
58af1b98c0 | ||
|
|
6a077333c6 | ||
|
|
cb234e6b1b | ||
|
|
67a9f35176 | ||
|
|
9396e8b0c0 | ||
|
|
200d857012 | ||
|
|
1a22b7d0dd | ||
|
|
035cccb97b | ||
|
|
ca1c00acb0 | ||
|
|
8836669c9f | ||
|
|
13e818e135 | ||
|
|
fc0fd2d08c | ||
|
|
cc91cfe7b5 | ||
|
|
33564a311b | ||
|
|
2de416c1fa | ||
|
|
64838df712 | ||
|
|
84ad0200a8 | ||
|
|
046f9c53ab | ||
|
|
62f1139193 | ||
|
|
373be54b04 | ||
|
|
00c3ea07e7 | ||
|
|
5a64bd76fb | ||
|
|
57dbd3cf54 | ||
|
|
e772831f7c | ||
|
|
9363b79454 | ||
|
|
cf97ff724c | ||
|
|
c1f1c0d341 | ||
|
|
fd47225d30 | ||
|
|
0e9e884ab4 | ||
|
|
85d13ddfdf | ||
|
|
deadd8ad07 | ||
|
|
b9e5e5f114 | ||
|
|
e5d948af44 |
5
Makefile
5
Makefile
@@ -63,10 +63,7 @@ deploy-appbundle:
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
cp \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \
|
||||
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
|
||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.* \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-olm:
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:name=".MainApplication"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiReactNativeHost;
|
||||
|
||||
/**
|
||||
* Application class for Jitsi Meet. The only reason why this exists is for Detox
|
||||
* to believe our app is a "greenfield" app. SDK users need not use this.
|
||||
*/
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
private final ReactNativeHost mReactNativeHost = new JitsiReactNativeHost(this);
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// Initialize RN
|
||||
Log.d(this.getClass().getCanonicalName(), "app onCreate");
|
||||
getReactNativeHost().getReactInstanceManager();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
compileSdkVersion = 32
|
||||
minSdkVersion = 23
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 32
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import org.webrtc.VideoCodecInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/** Container for static helper functions related to dealing with H264 codecs. */
|
||||
class H264Utils {
|
||||
public static final String H264_FMTP_PROFILE_LEVEL_ID = "profile-level-id";
|
||||
public static final String H264_FMTP_LEVEL_ASYMMETRY_ALLOWED = "level-asymmetry-allowed";
|
||||
public static final String H264_FMTP_PACKETIZATION_MODE = "packetization-mode";
|
||||
|
||||
public static final String H264_PROFILE_CONSTRAINED_BASELINE = "42e0";
|
||||
public static final String H264_PROFILE_CONSTRAINED_HIGH = "640c";
|
||||
public static final String H264_LEVEL_3_1 = "1f"; // 31 in hex.
|
||||
public static final String H264_CONSTRAINED_HIGH_3_1 =
|
||||
H264_PROFILE_CONSTRAINED_HIGH + H264_LEVEL_3_1;
|
||||
public static final String H264_CONSTRAINED_BASELINE_3_1 =
|
||||
H264_PROFILE_CONSTRAINED_BASELINE + H264_LEVEL_3_1;
|
||||
|
||||
public static Map<String, String> getDefaultH264Params(boolean isHighProfile) {
|
||||
final Map<String, String> params = new HashMap<>();
|
||||
params.put(VideoCodecInfo.H264_FMTP_LEVEL_ASYMMETRY_ALLOWED, "1");
|
||||
params.put(VideoCodecInfo.H264_FMTP_PACKETIZATION_MODE, "1");
|
||||
params.put(VideoCodecInfo.H264_FMTP_PROFILE_LEVEL_ID,
|
||||
isHighProfile ? VideoCodecInfo.H264_CONSTRAINED_HIGH_3_1
|
||||
: VideoCodecInfo.H264_CONSTRAINED_BASELINE_3_1);
|
||||
return params;
|
||||
}
|
||||
|
||||
public static VideoCodecInfo DEFAULT_H264_BASELINE_PROFILE_CODEC =
|
||||
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ false));
|
||||
public static VideoCodecInfo DEFAULT_H264_HIGH_PROFILE_CODEC =
|
||||
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ true));
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -177,8 +178,11 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
protected void leave() {
|
||||
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView.abort();
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot leave, view is null");
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable
|
||||
@@ -295,6 +299,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingSuperCall")
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
@@ -157,6 +157,14 @@ public class JitsiMeetView extends FrameLayout {
|
||||
setProps(options != null ? options.asProps() : new Bundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method which aborts running RN by passing empty props.
|
||||
* This is only meant to be used from the enclosing Activity's onDestroy.
|
||||
*/
|
||||
public void abort() {
|
||||
setProps(new Bundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@code ReactRootView} for the given app name with the given
|
||||
* props. Once created it's set as the view of this {@code FrameLayout}.
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the minimal implementation of ReactNativeHost that will make things like the
|
||||
* Detox testing framework believe we are a "greenfield" app.
|
||||
*
|
||||
* Generally speaking, apps using the SDK (other than the Jitsi Meet app itself) should not
|
||||
* need to use this because the
|
||||
*/
|
||||
public class JitsiReactNativeHost extends ReactNativeHost {
|
||||
public JitsiReactNativeHost(Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
// Unused since we override `createReactInstanceManager`.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
// Unused since we override `createReactInstanceManager`.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactInstanceManager createReactInstanceManager() {
|
||||
ReactInstanceManagerHolder.initReactInstanceManager(this.getApplication());
|
||||
|
||||
return ReactInstanceManagerHolder.getReactInstanceManager();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -32,17 +31,17 @@ import com.facebook.react.jscexecutor.JSCExecutorFactory;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.oney.WebRTCModule.EglUtils;
|
||||
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
import com.oney.WebRTCModule.WebRTCModuleOptions;
|
||||
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoDecoderFactory;
|
||||
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
|
||||
|
||||
import org.devio.rn.splashscreen.SplashScreenModule;
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class ReactInstanceManagerHolder {
|
||||
@@ -79,30 +78,11 @@ class ReactInstanceManagerHolder {
|
||||
nativeModules.add(new RNConnectionService(reactContext));
|
||||
}
|
||||
|
||||
// Initialize the WebRTC module by hand, since we want to override some
|
||||
// initialization options.
|
||||
WebRTCModule.Options options = new WebRTCModule.Options();
|
||||
|
||||
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
|
||||
.setEnableVolumeLogger(false)
|
||||
.createAudioDeviceModule();
|
||||
options.setAudioDeviceModule(adm);
|
||||
|
||||
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
|
||||
|
||||
options.setVideoDecoderFactory(new WebRTCVideoDecoderFactory(eglContext));
|
||||
options.setVideoEncoderFactory(new WebRTCVideoEncoderFactory(eglContext));
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
return nativeModules;
|
||||
}
|
||||
|
||||
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Arrays.<ViewManager>asList(
|
||||
// WebRTC, see createNativeModules for details.
|
||||
new RTCVideoViewManager()
|
||||
);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
static List<ReactPackage> getReactNativePackages() {
|
||||
@@ -122,6 +102,7 @@ class ReactInstanceManagerHolder {
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
|
||||
new org.linusu.RNGetRandomValuesPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
@@ -239,35 +220,6 @@ class ReactInstanceManagerHolder {
|
||||
return reactInstanceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to initialize the React Native instance manager. We
|
||||
* create a single instance in order to load the JavaScript bundle a single
|
||||
* time. All {@code ReactRootView} instances will be tied to the one and
|
||||
* only {@code ReactInstanceManager}.
|
||||
*
|
||||
* This method is only meant to be called when integrating with {@code JitsiReactNativeHost}.
|
||||
*
|
||||
* @param app {@code Application} current running Application.
|
||||
*/
|
||||
static void initReactInstanceManager(Application app) {
|
||||
if (reactInstanceManager != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "initializing RN with Application");
|
||||
|
||||
reactInstanceManager
|
||||
= ReactInstanceManager.builder()
|
||||
.setApplication(app)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModulePath("index.android")
|
||||
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
|
||||
.addPackages(getReactNativePackages())
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to initialize the React Native instance manager. We
|
||||
* create a single instance in order to load the JavaScript bundle a single
|
||||
@@ -281,7 +233,15 @@ class ReactInstanceManagerHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Activity");
|
||||
// Initialize the WebRTC module options.
|
||||
WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();
|
||||
|
||||
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
|
||||
|
||||
options.videoDecoderFactory = new H264AndSoftwareVideoDecoderFactory(eglContext);
|
||||
options.videoEncoderFactory = new H264AndSoftwareVideoEncoderFactory(eglContext);
|
||||
|
||||
Log.d(TAG, "initializing RN with Activity");
|
||||
|
||||
reactInstanceManager
|
||||
= ReactInstanceManager.builder()
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
/** Enumeration of supported video codec types. */
|
||||
public enum VideoCodecMimeType {
|
||||
VP8("video/x-vnd.on2.vp8"),
|
||||
VP9("video/x-vnd.on2.vp9"),
|
||||
H264("video/avc"),
|
||||
AV1("video/av01");
|
||||
|
||||
private final String mimeType;
|
||||
|
||||
private VideoCodecMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
String mimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.HardwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.VideoCodecInfo;
|
||||
import org.webrtc.VideoDecoder;
|
||||
import org.webrtc.VideoDecoderFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a custom video decoder factory for WebRTC which behaves similarly
|
||||
* to the default one in iOS. It supports the following codecs:
|
||||
*
|
||||
* - In hardware: H.264 (baseline)
|
||||
* - In software: VP8, VP9, AV1
|
||||
*/
|
||||
public class WebRTCVideoDecoderFactory implements VideoDecoderFactory {
|
||||
private final VideoDecoderFactory hardwareVideoDecoderFactory;
|
||||
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory();
|
||||
|
||||
public WebRTCVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
|
||||
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VideoDecoder createDecoder(VideoCodecInfo codecInfo) {
|
||||
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
|
||||
return this.hardwareVideoDecoderFactory.createDecoder(codecInfo);
|
||||
}
|
||||
|
||||
return this.softwareVideoDecoderFactory.createDecoder(codecInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoCodecInfo[] getSupportedCodecs() {
|
||||
List<VideoCodecInfo> codecs = new ArrayList<>();
|
||||
|
||||
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
|
||||
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
|
||||
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
|
||||
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
|
||||
|
||||
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.HardwareVideoEncoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.VideoCodecInfo;
|
||||
import org.webrtc.VideoEncoder;
|
||||
import org.webrtc.VideoEncoderFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a custom video encoder factory for WebRTC which behaves similarly
|
||||
* to the default one in iOS. It supports the following codecs:
|
||||
*
|
||||
* - In hardware: H.264 (baseline)
|
||||
* - In software: VP8, VP9, AV1
|
||||
*/
|
||||
public class WebRTCVideoEncoderFactory implements VideoEncoderFactory {
|
||||
private final VideoEncoderFactory hardwareVideoEncoderFactory;
|
||||
private final VideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();
|
||||
|
||||
public WebRTCVideoEncoderFactory(@Nullable EglBase.Context eglContext) {
|
||||
this.hardwareVideoEncoderFactory =
|
||||
new HardwareVideoEncoderFactory(eglContext, false, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VideoEncoder createEncoder(VideoCodecInfo codecInfo) {
|
||||
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
|
||||
return this.hardwareVideoEncoderFactory.createEncoder(codecInfo);
|
||||
}
|
||||
|
||||
return this.softwareVideoEncoderFactory.createEncoder(codecInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoCodecInfo[] getSupportedCodecs() {
|
||||
List<VideoCodecInfo> codecs = new ArrayList<>();
|
||||
|
||||
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
|
||||
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
|
||||
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
|
||||
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
|
||||
|
||||
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
|
||||
}
|
||||
}
|
||||
2
app.js
2
app.js
@@ -18,7 +18,6 @@ import './react/features/base/jitsi-local-storage/setup';
|
||||
import conference from './conference';
|
||||
import API from './modules/API';
|
||||
import UI from './modules/UI/UI';
|
||||
import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
|
||||
import translation from './modules/translation/translation';
|
||||
|
||||
// Initialize Olm as early as possible.
|
||||
@@ -38,7 +37,6 @@ window.APP = {
|
||||
'index.loaded': window.indexLoadedTime
|
||||
},
|
||||
|
||||
keyboardshortcut,
|
||||
translation,
|
||||
UI
|
||||
};
|
||||
|
||||
@@ -140,6 +140,7 @@ import { downloadJSON } from './react/features/base/util/downloadJSON';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker/actions';
|
||||
import { appendSuffix } from './react/features/display-name/functions';
|
||||
import { maybeOpenFeedbackDialog, submitFeedback } from './react/features/feedback/actions';
|
||||
import { initKeyboardShortcuts } from './react/features/keyboard-shortcuts/actions';
|
||||
import { maybeSetLobbyChatMessageListener } from './react/features/lobby/actions.any';
|
||||
import { setNoiseSuppressionEnabled } from './react/features/noise-suppression/actions';
|
||||
import { hideNotification, showNotification, showWarningNotification } from './react/features/notifications/actions';
|
||||
@@ -162,6 +163,7 @@ import { endpointMessageReceived } from './react/features/subtitles/actions.any'
|
||||
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
|
||||
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||
import { setIAmVisitor } from './react/features/visitors/actions';
|
||||
import { iAmVisitor } from './react/features/visitors/functions';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
@@ -365,6 +367,10 @@ class ConferenceConnector {
|
||||
// we do not clear local tracks on error, so we need to manually clear them
|
||||
.then(APP.store.dispatch(destroyLocalTracks()))
|
||||
.then(() => {
|
||||
// Reset VideoLayout. It's destroyed in features/video-layout/middleware.web.js so re-initialize it.
|
||||
VideoLayout.initLargeVideo();
|
||||
VideoLayout.resizeVideoArea();
|
||||
|
||||
connect(this._conference.roomName).then(con => {
|
||||
this._conference.startConference(con, []);
|
||||
});
|
||||
@@ -800,13 +806,14 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async init({ roomName }) {
|
||||
const state = APP.store.getState();
|
||||
const initialOptions = {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: getStartWithAudioMuted(APP.store.getState())
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|
||||
startWithVideoMuted: getStartWithVideoMuted(APP.store.getState())
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|
||||
startWithAudioMuted: getStartWithAudioMuted(state)
|
||||
|| isUserInteractionRequiredForUnmute(state),
|
||||
startWithVideoMuted: getStartWithVideoMuted(state)
|
||||
|| isUserInteractionRequiredForUnmute(state)
|
||||
};
|
||||
|
||||
this.roomName = roomName;
|
||||
@@ -836,7 +843,7 @@ export default {
|
||||
return tracks;
|
||||
};
|
||||
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
_connectionPromise = connect(roomName).then(c => {
|
||||
// we want to initialize it early, in case of errors to be able
|
||||
// to gather logs
|
||||
@@ -859,7 +866,7 @@ export default {
|
||||
// they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
return APP.store.dispatch(initPrejoin(tracks, errors));
|
||||
}
|
||||
|
||||
@@ -869,7 +876,7 @@ export default {
|
||||
|
||||
let localTracks = handleStartAudioMuted(initialOptions, tracks);
|
||||
|
||||
// in case where gum is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
|
||||
// In case where gUM is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
|
||||
// join unmuted even though jicofo had instruct us to mute, so let's respect that before passing the tracks
|
||||
if (!browser.isWebKitBased()) {
|
||||
if (room?.isStartAudioMuted()) {
|
||||
@@ -881,6 +888,11 @@ export default {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
// Do not add the tracks if the user has joined the call as a visitor.
|
||||
if (iAmVisitor(state)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._setLocalAudioVideoStreams(localTracks);
|
||||
}
|
||||
|
||||
@@ -2297,10 +2309,7 @@ export default {
|
||||
|
||||
APP.UI.initConference();
|
||||
|
||||
if (!config.disableShortcuts) {
|
||||
APP.keyboardshortcut.init();
|
||||
}
|
||||
|
||||
dispatch(initKeyboardShortcuts());
|
||||
dispatch(conferenceJoined(room));
|
||||
|
||||
const jwt = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
@@ -1406,6 +1406,7 @@ var config = {
|
||||
disableAGC
|
||||
disableAP
|
||||
disableHPF
|
||||
disableLocalStats
|
||||
disableNS
|
||||
enableTalkWhileMuted
|
||||
forceJVB121Ratio
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
.audio-preview {
|
||||
display: inline-block;
|
||||
|
||||
&-content {
|
||||
position: relative;
|
||||
right: auto;
|
||||
margin-bottom: 4px;
|
||||
max-height: calc(100vh - 100px);
|
||||
overflow: auto;
|
||||
width: 300px;
|
||||
|
||||
&-ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-header:hover {
|
||||
background-color: initial;
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
&-entry-text {
|
||||
max-width: 213px;
|
||||
|
||||
&.left-margin {
|
||||
margin-left: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
&-speaker {
|
||||
position: relative;
|
||||
|
||||
&:hover, &:focus-within, &:focus {
|
||||
.audio-preview-test-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 178px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 238px;
|
||||
}
|
||||
}
|
||||
|
||||
&-microphone {
|
||||
position: relative;
|
||||
|
||||
&--nometer {
|
||||
.audio-preview-entry-text {
|
||||
max-width: 238px;
|
||||
}
|
||||
}
|
||||
|
||||
&--withmeter {
|
||||
.audio-preview-entry-text {
|
||||
max-width: 178px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&-icon {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
|
||||
&--exclamation {
|
||||
margin-left: 6px;
|
||||
|
||||
& svg {
|
||||
fill: #E54B4B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-test-button {
|
||||
display: none;
|
||||
padding: 4px 10px;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
&-meter-mic {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,10 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
.popover {
|
||||
margin: -16px -24px;
|
||||
z-index: $popoverZ;
|
||||
|
||||
.popover-content {
|
||||
margin: 16px 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.top {
|
||||
bottom: 8px;
|
||||
}
|
||||
&.hover {
|
||||
margin: -16px -24px;
|
||||
|
||||
&.bottom {
|
||||
top: 4px;
|
||||
}
|
||||
.popover-content {
|
||||
margin: 16px 24px;
|
||||
|
||||
&.left {
|
||||
right: 4px;
|
||||
}
|
||||
&.top {
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
&.right {
|
||||
left: 4px;
|
||||
&.bottom {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
&.left {
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
&.right {
|
||||
left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +197,6 @@
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.helper-link {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
color:#FFD740;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
.video-preview {
|
||||
background: none;
|
||||
display: inline-block;
|
||||
|
||||
&-container {
|
||||
max-height: 456px;
|
||||
overflow: auto;
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
&-entry {
|
||||
cursor: pointer;
|
||||
height: 138px;
|
||||
width: 244px;
|
||||
position: relative;
|
||||
margin: 0 7px 4px;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
border: 2px solid #4687ED;
|
||||
}
|
||||
}
|
||||
|
||||
&-video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-error {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-label {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: 100%;
|
||||
padding: 8px;
|
||||
z-index: 2;
|
||||
|
||||
&-text {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 600;
|
||||
max-width: calc(100% - 16px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,10 @@
|
||||
#largeVideoContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#largeVideoWrapper {
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
.button-control {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
border: 1px solid $buttonBorder;
|
||||
vertical-align: baseline;
|
||||
height: 30px;
|
||||
min-width: 60px;
|
||||
padding: 4px 10px;
|
||||
margin: 0;
|
||||
line-height: 1.5em;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
color: $buttonColor;
|
||||
font-weight: $buttonFontWeight;
|
||||
@include transition(background-color .1s ease-out);
|
||||
|
||||
&[disabled] {
|
||||
color: #666;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&_full-width {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $buttonHoverBorder;
|
||||
background-color: $buttonHoverBackground;
|
||||
@include transition(background-color .1s ease-in);
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include box-shadow(0, 0, 1px, $buttonShadowColor, true);
|
||||
}
|
||||
|
||||
&_light {
|
||||
color: $defaultDarkColor;
|
||||
background-color: $buttonLightBackground;
|
||||
border: 1px solid $buttonLightBorder;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $buttonLightHoverBorder;
|
||||
background-color: $buttonLightHoverBackground;
|
||||
}
|
||||
}
|
||||
|
||||
&_link {
|
||||
color: $buttonLinkColor;
|
||||
background-color: $buttonLinkBackground;
|
||||
|
||||
&:hover {
|
||||
background-color: $buttonLinkBackground;
|
||||
}
|
||||
}
|
||||
|
||||
&_overlay {
|
||||
color: $primaryButtonColor;
|
||||
background-color: $overlayButtonBg;
|
||||
border-radius: 2px;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $primaryButtonBackground;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&_primary {
|
||||
background-color: $primaryButtonBackground;
|
||||
border: 1px solid $primaryButtonBackground;
|
||||
color: $primaryButtonColor !important;
|
||||
font-weight: $primaryButtonFontWeight;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $primaryButtonHoverBackground;
|
||||
background-color: $primaryButtonHoverBackground;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
color: $primaryButtonColor;
|
||||
}
|
||||
}
|
||||
|
||||
&_close {
|
||||
color: $defaultFontColor;
|
||||
}
|
||||
&_submit {
|
||||
color: $linkFontColor;
|
||||
&:hover {
|
||||
color: $linkHoverFontColor;
|
||||
}
|
||||
}
|
||||
|
||||
&_center {
|
||||
float: none !important;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
.form-control {
|
||||
padding: $formPadding 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&__text {
|
||||
margin: 8px 0;
|
||||
font-size: 1em
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 1em;
|
||||
font-weight: $labelFontWeight;
|
||||
}
|
||||
|
||||
&__em {
|
||||
color: $inputControlEmColor;
|
||||
}
|
||||
|
||||
&__container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
@include flex();
|
||||
|
||||
.button-control {
|
||||
margin: 1px 0 1px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific color for read only style.
|
||||
*/
|
||||
input:read-only {
|
||||
color: $readOnlyInputColor;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
.input-control {
|
||||
@include transition(all .2s ease-in);
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 5px 7px;
|
||||
border-radius: $borderRadius;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
text-align: left;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: inherit;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
background-color: $defaultDarkSelectionColor;
|
||||
}
|
||||
|
||||
|
||||
&.error {
|
||||
color: $errorColor;
|
||||
border-color: $errorColor;
|
||||
}
|
||||
}
|
||||
|
||||
@include placeholder {
|
||||
color: $placeHolderColor;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
.link {
|
||||
cursor: pointer;
|
||||
color: $linkFontColor;
|
||||
@include transition(color .1s ease-out);
|
||||
|
||||
&:hover {
|
||||
color: $linkHoverFontColor;
|
||||
text-decoration: underline;
|
||||
@include transition(color .1s ease-in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper links are links that are meant to open a documentation page or more
|
||||
* detailed info.
|
||||
*/
|
||||
.helper-link {
|
||||
@extend .link;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -33,8 +33,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'mini_toolbox';
|
||||
@import 'modals/desktop-picker/desktop-picker';
|
||||
@import 'modals/dialog';
|
||||
@import 'modals/embed-meeting/embed-meeting';
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'modals/invite/info';
|
||||
@import 'modals/screen-share/share-audio';
|
||||
@import 'modals/screen-share/share-screen-warning';
|
||||
@@ -51,10 +49,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'welcome_page_settings_toolbar';
|
||||
@import 'toolbars';
|
||||
@import 'redirect_page';
|
||||
@import 'components/form-control';
|
||||
@import 'components/link';
|
||||
@import 'components/button-control';
|
||||
@import 'components/input-control';
|
||||
@import 'components/input-slider';
|
||||
@import '404';
|
||||
@import 'policy';
|
||||
@@ -66,7 +60,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'filmstrip/vertical_filmstrip';
|
||||
@import 'filmstrip/vertical_filmstrip_overrides';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'modals/invite/add-people';
|
||||
@import 'deep-linking/main';
|
||||
@import 'transcription-subtitles';
|
||||
@import '_meetings_list.scss';
|
||||
@@ -77,8 +70,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'chrome-extension-banner';
|
||||
@import 'settings-button';
|
||||
@import 'meter';
|
||||
@import 'audio-preview';
|
||||
@import 'video-preview';
|
||||
@import 'premeeting/main';
|
||||
@import 'modals/invite/invite_more';
|
||||
@import 'modals/security/security';
|
||||
|
||||
@@ -12,24 +12,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styling inline dialog errors.
|
||||
*/
|
||||
.inline-dialog-error {
|
||||
margin-top: 16px;
|
||||
|
||||
&-text {
|
||||
color: $dialogErrorText;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: block;
|
||||
margin: 16px auto 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styling shared video dialog errors.
|
||||
*/
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
.embed-meeting {
|
||||
&-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-copy {
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
margin-left: auto;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&-code {
|
||||
background: transparent;
|
||||
border: 1px solid #A4B8D1;
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
height: 165px;
|
||||
line-height: 24px;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
&-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 24px;
|
||||
width: calc(100% - 24px);
|
||||
height: 24px;
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.jitsi-icon {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
@-webkit-keyframes shake-rotate {
|
||||
0% {
|
||||
-webkit-transform:scale(1) rotate(0deg);
|
||||
transform:scale(1) rotate(0deg)
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform:scale(.8) rotate(-5deg);
|
||||
transform:scale(.8) rotate(-5deg)
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform:scale(1) rotate(3deg);
|
||||
transform:scale(1) rotate(3deg)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake-rotate {
|
||||
0% {
|
||||
-webkit-transform:scale(1) rotate(0deg);
|
||||
transform:scale(1) rotate(0deg)
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform:scale(.8) rotate(-5deg);
|
||||
transform:scale(.8) rotate(-5deg)
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform:scale(1) rotate(3deg);
|
||||
transform:scale(1) rotate(3deg)
|
||||
}
|
||||
}
|
||||
|
||||
.shake-rotate {
|
||||
display: inline-block;
|
||||
|
||||
-webkit-animation-duration: .4s;
|
||||
animation-duration: .4s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
animation-iteration-count: infinite;
|
||||
-webkit-animation-name: shake-rotate;
|
||||
animation-name: shake-rotate;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out
|
||||
}
|
||||
@@ -41,10 +41,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles errors in the MultiSelectAutocomplete.
|
||||
*/
|
||||
.autocomplete-error {
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
line-height: 24px;
|
||||
border-collapse: collapse;
|
||||
|
||||
* {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
thead {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -1,57 +1,4 @@
|
||||
.invite-more {
|
||||
&-container {
|
||||
margin-bottom: 8px;
|
||||
transition: margin-bottom 0.3s;
|
||||
|
||||
&.elevated {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-header {
|
||||
max-width: 100%;
|
||||
margin-bottom: 16px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 16px;
|
||||
background: #0376DA;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: #278ADF;
|
||||
}
|
||||
}
|
||||
|
||||
&-text {
|
||||
margin-left: 8px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
&-dialog {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
@@ -65,59 +12,6 @@
|
||||
background: #5E6D7A;
|
||||
}
|
||||
|
||||
&.email-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 24px;
|
||||
width: calc(100% - 26px);
|
||||
height: 22px;
|
||||
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.invite-buttons {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
margin-top: 8px;
|
||||
|
||||
& > a {
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
min-width: 48px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-cancel {
|
||||
margin-right: 16px;
|
||||
padding: 7px 15px;
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
}
|
||||
|
||||
&-add {
|
||||
padding: 8px 16px;
|
||||
background: #0376DA;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
& > a {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.stream {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -158,14 +52,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser {
|
||||
.invite-more-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.invite-more-button {
|
||||
height: 48px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,3 +208,26 @@
|
||||
.lobby-button-margin {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.lobby-prejoin-error {
|
||||
background-color: #E04757;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-top: -8px;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lobby-prejoin-input {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
|
||||
& input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@import 'lobby';
|
||||
@import 'premeeting-screens';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-third-party';
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
.prejoin {
|
||||
&-input-area {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
margin: 8px auto 16px;
|
||||
|
||||
&-name {
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
&-error {
|
||||
background-color: #E04757;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-top: -8px;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
&-dropdown-btns {
|
||||
padding: 8px 0;
|
||||
width: 300px;
|
||||
background-color: #E0E0E0;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
top: -16px;
|
||||
}
|
||||
|
||||
&-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;
|
||||
padding: 0;
|
||||
position: absolute !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-input {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
|
||||
& input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -78,13 +78,18 @@ Component "conference.jitmeet.example.com" "muc"
|
||||
restrict_room_creation = true
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
"polls";
|
||||
--"token_verification";
|
||||
"muc_rate_limit";
|
||||
"muc_password_whitelist";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
muc_password_whitelist = {
|
||||
"focusUser@auth.jitmeet.example.com"
|
||||
}
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
@@ -92,6 +97,7 @@ Component "breakout.jitmeet.example.com" "muc"
|
||||
restrict_room_creation = true
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
"muc_rate_limit";
|
||||
@@ -105,6 +111,7 @@ Component "breakout.jitmeet.example.com" "muc"
|
||||
Component "internal.auth.jitmeet.example.com" "muc"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"ping";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com", "jvb@auth.jitmeet.example.com" }
|
||||
@@ -139,6 +146,7 @@ Component "lobby.jitmeet.example.com" "muc"
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"muc_rate_limit";
|
||||
"polls";
|
||||
}
|
||||
|
||||
6
globals.d.ts
vendored
6
globals.d.ts
vendored
@@ -10,12 +10,6 @@ declare global {
|
||||
API: any;
|
||||
conference: any;
|
||||
debugLogs: any;
|
||||
keyboardshortcut: {
|
||||
registerShortcut: Function;
|
||||
unregisterShortcut: Function;
|
||||
openDialog: Function;
|
||||
enable: Function;
|
||||
}
|
||||
};
|
||||
const interfaceConfig: any;
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ var interfaceConfig = {
|
||||
|
||||
ENABLE_DIAL_OUT: true,
|
||||
|
||||
ENABLE_FEEDBACK_ANIMATION: false, // Enables feedback star animation.
|
||||
// DEPRECATED. Animation no longer supported.
|
||||
// ENABLE_FEEDBACK_ANIMATION: false,
|
||||
|
||||
FILM_STRIP_MAX_HEIGHT: 120,
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ target 'JitsiMeetSDK' do
|
||||
|
||||
pod 'CocoaLumberjack', '3.7.2'
|
||||
pod 'ObjectiveDropboxOfficial', '6.2.3'
|
||||
pod 'JitsiWebRTC', '~> 106.0.0'
|
||||
pod 'JitsiWebRTC', '~> 111.0.0'
|
||||
end
|
||||
|
||||
target 'JitsiMeetSDKLite' do
|
||||
|
||||
103
ios/Podfile.lock
103
ios/Podfile.lock
@@ -3,11 +3,12 @@ PODS:
|
||||
- amplitude-react-native (2.7.0):
|
||||
- Amplitude (= 8.7.1)
|
||||
- React-Core
|
||||
- AppAuth (1.4.0):
|
||||
- AppAuth/Core (= 1.4.0)
|
||||
- AppAuth/ExternalUserAgent (= 1.4.0)
|
||||
- AppAuth/Core (1.4.0)
|
||||
- AppAuth/ExternalUserAgent (1.4.0)
|
||||
- AppAuth (1.6.1):
|
||||
- AppAuth/Core (= 1.6.1)
|
||||
- AppAuth/ExternalUserAgent (= 1.6.1)
|
||||
- AppAuth/Core (1.6.1)
|
||||
- AppAuth/ExternalUserAgent (1.6.1):
|
||||
- AppAuth/Core
|
||||
- boost (1.76.0)
|
||||
- CocoaLumberjack (3.7.2):
|
||||
- CocoaLumberjack/Core (= 3.7.2)
|
||||
@@ -102,56 +103,56 @@ PODS:
|
||||
- GoogleUtilities/Network (~> 7.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||
- nanopb (~> 2.30908.0)
|
||||
- GoogleDataTransport (9.1.4):
|
||||
- GoogleDataTransport (9.2.2):
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- GoogleSignIn (6.0.2):
|
||||
- AppAuth (~> 1.4)
|
||||
- GTMAppAuth (~> 1.0)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
- GoogleUtilities/AppDelegateSwizzler (7.7.0):
|
||||
- GoogleSignIn (6.2.4):
|
||||
- AppAuth (~> 1.5)
|
||||
- GTMAppAuth (~> 1.3)
|
||||
- GTMSessionFetcher/Core (< 3.0, >= 1.1)
|
||||
- GoogleUtilities/AppDelegateSwizzler (7.11.1):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network
|
||||
- GoogleUtilities/Environment (7.7.0):
|
||||
- GoogleUtilities/Environment (7.11.1):
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- GoogleUtilities/Logger (7.7.0):
|
||||
- GoogleUtilities/Logger (7.11.1):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/MethodSwizzler (7.7.0):
|
||||
- GoogleUtilities/MethodSwizzler (7.11.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network (7.7.0):
|
||||
- GoogleUtilities/Network (7.11.1):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
- GoogleUtilities/Reachability
|
||||
- "GoogleUtilities/NSData+zlib (7.7.0)"
|
||||
- GoogleUtilities/Reachability (7.7.0):
|
||||
- "GoogleUtilities/NSData+zlib (7.11.1)"
|
||||
- GoogleUtilities/Reachability (7.11.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (7.7.0):
|
||||
- GoogleUtilities/UserDefaults (7.11.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GTMAppAuth (1.2.2):
|
||||
- AppAuth/Core (~> 1.4)
|
||||
- GTMSessionFetcher/Core (~> 1.5)
|
||||
- GTMSessionFetcher/Core (1.7.0)
|
||||
- JitsiWebRTC (106.0.0)
|
||||
- libwebp (1.2.1):
|
||||
- libwebp/demux (= 1.2.1)
|
||||
- libwebp/mux (= 1.2.1)
|
||||
- libwebp/webp (= 1.2.1)
|
||||
- libwebp/demux (1.2.1):
|
||||
- GTMAppAuth (1.3.1):
|
||||
- AppAuth/Core (~> 1.6)
|
||||
- GTMSessionFetcher/Core (< 3.0, >= 1.5)
|
||||
- GTMSessionFetcher/Core (2.3.0)
|
||||
- JitsiWebRTC (111.0.1)
|
||||
- libwebp (1.2.4):
|
||||
- libwebp/demux (= 1.2.4)
|
||||
- libwebp/mux (= 1.2.4)
|
||||
- libwebp/webp (= 1.2.4)
|
||||
- libwebp/demux (1.2.4):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.2.1):
|
||||
- libwebp/mux (1.2.4):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.2.1)
|
||||
- libwebp/webp (1.2.4)
|
||||
- nanopb (2.30908.0):
|
||||
- nanopb/decode (= 2.30908.0)
|
||||
- nanopb/encode (= 2.30908.0)
|
||||
- nanopb/decode (2.30908.0)
|
||||
- nanopb/encode (2.30908.0)
|
||||
- ObjectiveDropboxOfficial (6.2.3)
|
||||
- PromisesObjC (2.1.1)
|
||||
- PromisesSwift (2.1.1):
|
||||
- PromisesObjC (= 2.1.1)
|
||||
- PromisesObjC (2.2.0)
|
||||
- PromisesSwift (2.2.0):
|
||||
- PromisesObjC (= 2.2.0)
|
||||
- RCT-Folly (2021.06.28.00-v2):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -389,8 +390,8 @@ PODS:
|
||||
- react-native-video/Video (6.0.0-alpha.1):
|
||||
- PromisesSwift
|
||||
- React-Core
|
||||
- react-native-webrtc (106.0.7):
|
||||
- JitsiWebRTC (~> 106.0.0)
|
||||
- react-native-webrtc (111.0.0):
|
||||
- JitsiWebRTC (~> 111.0.0)
|
||||
- React-Core
|
||||
- react-native-webview (11.15.1):
|
||||
- React-Core
|
||||
@@ -471,8 +472,8 @@ PODS:
|
||||
- React-Core
|
||||
- RNGestureHandler (2.9.0):
|
||||
- React-Core
|
||||
- RNGoogleSignin (7.0.4):
|
||||
- GoogleSignIn (~> 6.0.0)
|
||||
- RNGoogleSignin (9.0.2):
|
||||
- GoogleSignIn (~> 6.2)
|
||||
- React-Core
|
||||
- RNScreens (3.13.1):
|
||||
- React-Core
|
||||
@@ -500,7 +501,7 @@ DEPENDENCIES:
|
||||
- Firebase/DynamicLinks (~> 8.0)
|
||||
- "giphy-react-native-sdk (from `../node_modules/@giphy/react-native-sdk`)"
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- JitsiWebRTC (~> 106.0.0)
|
||||
- JitsiWebRTC (~> 111.0.0)
|
||||
- ObjectiveDropboxOfficial (= 6.2.3)
|
||||
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||
@@ -701,7 +702,7 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
Amplitude: 834c7332dfb9640a751e21c13efb22a07c0c12d4
|
||||
amplitude-react-native: 0ed8cab759aafaa94961b82122bf56297da607ad
|
||||
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
|
||||
AppAuth: e48b432bb4ba88b10cb2bcc50d7f3af21e78b9c2
|
||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||
CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
@@ -719,17 +720,17 @@ SPEC CHECKSUMS:
|
||||
giphy-react-native-sdk: 7abccf2b52123a0f30ce99da895ab6288023680c
|
||||
glog: 476ee3e89abb49e07f822b48323c51c57124b572
|
||||
GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e
|
||||
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
|
||||
GoogleSignIn: fd381840dbe7c1137aa6dc30849a5c3e070c034a
|
||||
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
|
||||
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
|
||||
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
|
||||
JitsiWebRTC: f441eb0e2d67f0588bf24e21c5162e97342714fb
|
||||
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
||||
GoogleDataTransport: 8378d1fa8ac49753ea6ce70d65a7cb70ce5f66e6
|
||||
GoogleSignIn: 5651ce3a61e56ca864160e79b484cd9ed3f49b7a
|
||||
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
|
||||
GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd
|
||||
GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2
|
||||
JitsiWebRTC: 9619c1f71cc16eeca76df68aa2d213c6d63274a8
|
||||
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
|
||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
||||
ObjectiveDropboxOfficial: fe206ce8c0bc49976c249d472db7fdbc53ebbd53
|
||||
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
|
||||
PromisesSwift: 99fddfe4a0ec88a56486644c0da106694c92a604
|
||||
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
|
||||
PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959
|
||||
RCT-Folly: 4d8508a426467c48885f1151029bc15fa5d7b3b8
|
||||
RCTRequired: 92cbd71369a2de6add25fd2403ac39838f1b694f
|
||||
RCTTypeSafety: 494e8af41d7410ed0b877210859ee3984f37e6b4
|
||||
@@ -754,7 +755,7 @@ SPEC CHECKSUMS:
|
||||
react-native-slider: 6e9b86e76cce4b9e35b3403193a6432ed07e0c81
|
||||
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
|
||||
react-native-video: bb6f12a7198db53b261fefb5d609dc77417acc8b
|
||||
react-native-webrtc: 0df36747802476e758af6b6dceccdeaed8c826c2
|
||||
react-native-webrtc: a9d4d8ef61adb634e006ffd956c494ad8318d95c
|
||||
react-native-webview: ea4899a1056c782afa96dd082179a66cbebf5504
|
||||
React-perflogger: 46620fc6d1c3157b60ed28434e08f7fd7f3f3353
|
||||
React-RCTActionSheet: b1f7e72a0ba760ec684df335c61f730b5179f5ff
|
||||
@@ -774,13 +775,13 @@ SPEC CHECKSUMS:
|
||||
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
|
||||
RNDeviceInfo: 0400a6d0c94186d1120c3cbd97b23abc022187a9
|
||||
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
|
||||
RNGoogleSignin: c4381751eefd73c552b923ba347a9bfc6f18771c
|
||||
RNGoogleSignin: 22e468a9474dbcb8618d8847205ad4f0b2575d13
|
||||
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
||||
RNSound: 27e8268bdb0a1f191f219a33267f7e0445e8d62f
|
||||
RNSVG: f3b60aeeaa81960e2e0536c3a9eef50b667ef3a9
|
||||
RNWatch: dae6c858a2051dbdcfb00b9a86cf4d90400263b4
|
||||
Yoga: 7929b92b1828675c1bebeb114dae8cb8fa7ef6a3
|
||||
|
||||
PODFILE CHECKSUM: e671cdcdb80fab67e305861c36bfae8ed5a5b0ef
|
||||
PODFILE CHECKSUM: d9116cb59cd7e921956e45de7cbbd75bef3862c1
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
|
||||
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
// CallKit has started to create problems starting with the iOS 16 simulator.
|
||||
// Disable it since it never worked in the simulator anyway.
|
||||
[builder setFeatureFlag:@"call-integration.enabled" withBoolean:NO];
|
||||
#endif
|
||||
}];
|
||||
|
||||
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#import <Intents/Intents.h>
|
||||
#import <WebRTC/RTCLogging.h>
|
||||
|
||||
#import "Orientation.h"
|
||||
|
||||
#import "JitsiMeet+Private.h"
|
||||
@@ -26,6 +26,8 @@
|
||||
#import "RNSplashScreen.h"
|
||||
#import "ScheenshareEventEmiter.h"
|
||||
|
||||
#import <react-native-webrtc/WebRTCModuleOptions.h>
|
||||
|
||||
#if !defined(JITSI_MEET_SDK_LITE)
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
#import "Dropbox.h"
|
||||
@@ -52,6 +54,12 @@
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
#if 0
|
||||
// Initialize WebRTC options.
|
||||
WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance];
|
||||
options.loggingSeverity = RTCLoggingSeverityInfo;
|
||||
#endif
|
||||
|
||||
// Initialize the one and only bridge for interfacing with React Native.
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
|
||||
@@ -63,11 +71,6 @@
|
||||
|
||||
// Register a log handler for React.
|
||||
registerReactLogHandler();
|
||||
|
||||
#if 0
|
||||
// Enable WebRTC logs
|
||||
RTCSetMinDebugLogLevel(RTCLoggingSeverityInfo);
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
1440
lang/main-el.json
1440
lang/main-el.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"add": "Mời",
|
||||
"copyInvite": "Sao chép lời mời",
|
||||
"countryNotSupported": "Chúng tôi chưa hỗ trợ đích đến này.",
|
||||
"countryReminder": "Đang gọi ra ngoài Mỹ? Đảm bảo bắt đầu bằng mã quốc gia!",
|
||||
"countryReminder": "Nhớ đảm bảo bắt đầu bằng mã quốc gia!",
|
||||
"disabled": "Bạn không thể mời thêm người.",
|
||||
"failedToAdd": "",
|
||||
"footerText": "Quay số bị tắt.",
|
||||
@@ -18,7 +18,7 @@
|
||||
"searchPeopleAndNumbers": "Tìm người và thêm số",
|
||||
"shareInvite": "Chia sẻ lời mời tham dự cuộc họp",
|
||||
"shareLink": "Chia sẻ đường dẫn để mời người khác tham dự cuộc họp",
|
||||
"telephone": "Số:{{number}}",
|
||||
"telephone": "Số: {{number}}",
|
||||
"title": "Mời người tham dự cuộc họp này"
|
||||
},
|
||||
"audioDevices": {
|
||||
@@ -28,24 +28,24 @@
|
||||
"speaker": "Diễn giả"
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "Chỉ âm thanh"
|
||||
"audioOnly": "Chỉ nghe âm thanh"
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "Thêm một liên kết họp",
|
||||
"confirmAddLink": "Bạn có muốn thêm một liên kiết tới sự kiện này?",
|
||||
"error": {
|
||||
"appConfiguration": "Tích hợp lịch chưa được cấu hình đúng.",
|
||||
"generic": "Một lỗi xuất hiện. Vui lòng kiểm tra cấu hình lịch hoặc thử làm tươi lịch.",
|
||||
"generic": "Có lỗi xảy ra. Vui lòng kiểm tra cấu hình lịch hoặc thử làm mới lịch.",
|
||||
"notSignedIn": "Một lỗi xảy ra khi xác thực để xem lịch sự kiện. Vui lòng kiểm tra cấu hình lịch và thử đăng nhập lại."
|
||||
},
|
||||
"join": "Tham gia",
|
||||
"joinTooltip": "Tham gia cuọc họp",
|
||||
"joinTooltip": "Tham gia cuộc họp",
|
||||
"nextMeeting": "Cuộc họp tiếp theo",
|
||||
"noEvents": "Không có sự kiện được lên lịch nào tiếp theo.",
|
||||
"ongoingMeeting": "cuộc họp đang diễn ra",
|
||||
"ongoingMeeting": "Cuộc họp đang diễn ra",
|
||||
"permissionButton": "Mở cấu hình",
|
||||
"permissionMessage": "Yêu cầu quyền truy cập Lịch để thấy cuộc họp của bạn trên ứng dụng.",
|
||||
"refresh": "Làm tươi lịch",
|
||||
"permissionMessage": "Yêu cầu quyền truy cập lịch để thấy cuộc họp của bạn trên ứng dụng.",
|
||||
"refresh": "Làm mới lịch",
|
||||
"today": "Hôm nay"
|
||||
},
|
||||
"chat": {
|
||||
@@ -53,16 +53,16 @@
|
||||
"messagebox": "Nhập nội dung tin nhắn",
|
||||
"nickname": {
|
||||
"popover": "Chọn tên",
|
||||
"title": "Nhập tên của bạn để tán gẫu",
|
||||
"titleWithPolls": "Nhập tên của bạn để tán gẫu"
|
||||
"title": "Nhập tên của bạn để gửi tin nhắn",
|
||||
"titleWithPolls": "Nhập tên của bạn để gửi tin nhắn"
|
||||
},
|
||||
"sendButton": "Gửi",
|
||||
"title": "Tán gẫu",
|
||||
"titleWithPolls": "Tán gẫu",
|
||||
"title": "Cuộc hội thoại",
|
||||
"titleWithPolls": "Cuộc hội thoại",
|
||||
"you": "bạn"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "Đang kết nối tới cuộc họp của bạn…"
|
||||
"joiningRoom": "Đang kết nối tới cuộc họp của bạn..."
|
||||
},
|
||||
"connection": {
|
||||
"ATTACHED": "Đã đính kèm",
|
||||
@@ -78,35 +78,35 @@
|
||||
},
|
||||
"connectionindicator": {
|
||||
"address": "Địa chỉ:",
|
||||
"bandwidth": "Băng thông ước tính:",
|
||||
"bandwidth": "Băng thông:",
|
||||
"bitrate": "Tốc độ:",
|
||||
"bridgeCount": "Máy chủ:",
|
||||
"connectedTo": "Đã kết nối tới:",
|
||||
"framerate": "Tỷ lệ khung hình:",
|
||||
"framerate": "FPS:",
|
||||
"less": "Hiển thị ít hơn",
|
||||
"localaddress_0": "Địa chỉ địa phương:",
|
||||
"localaddress_1": "Các địa chỉ địa phương:",
|
||||
"localport_0": "Cổng địa phương:",
|
||||
"localport_1": "Các cổng địa phương:",
|
||||
"localaddress_0": "IP thiết bị:",
|
||||
"localaddress_1": "Các IP thiết bị:",
|
||||
"localport_0": "Cổng thiết bị:",
|
||||
"localport_1": "Các cổng thiết bị:",
|
||||
"more": "Hiển thị nhiều hơn",
|
||||
"packetloss": "Mất gói tin:",
|
||||
"participant_id": "ID của người tham dự:",
|
||||
"packetloss": "Dữ liệu hỏng:",
|
||||
"participant_id": "ID người tham dự:",
|
||||
"quality": {
|
||||
"good": "Tốt",
|
||||
"inactive": "Không active",
|
||||
"inactive": "Đang treo máy",
|
||||
"lost": "Mất kết nối",
|
||||
"nonoptimal": "Không tối ưu",
|
||||
"poor": "Kém chất lượng"
|
||||
},
|
||||
"remoteaddress_0": "Địa chỉ từ xa:",
|
||||
"remoteaddress_1": "Các địa chỉ từ xa:",
|
||||
"remoteaddress_0": "IP từ xa:",
|
||||
"remoteaddress_1": "Các IP từ xa:",
|
||||
"remoteport_0": "Cổng từ xa:",
|
||||
"remoteport_1": "Các cổng từ xa:",
|
||||
"resolution": "Độ phân giải:",
|
||||
"status": "Kết nối:",
|
||||
"status": "Trạng thái kết nối:",
|
||||
"transport_0": "Vận chuyển:",
|
||||
"transport_1": "Các vận chuyển:",
|
||||
"turn": "turn"
|
||||
"turn": "lượt"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "Sớm hơn",
|
||||
@@ -114,27 +114,27 @@
|
||||
"yesterday": "Hôm qua"
|
||||
},
|
||||
"deepLinking": {
|
||||
"appNotInstalled": "Bạn cần ứng dụng {{app}} mobile để tham gia vào cuộc họp này bằng điện thoại.",
|
||||
"description": "Không có gì diễn ra? Chúng tôi đang chạy cuộc họp trên ứng dụng desktop {{app}}. Thử lại hoặc chạy trên ứng dụng web {{app}}.",
|
||||
"descriptionWithoutWeb": "",
|
||||
"downloadApp": "Tải phần mềm",
|
||||
"launchWebButton": "Chạy trên web",
|
||||
"openApp": "Tiếp tục ứng dụng này",
|
||||
"appNotInstalled": "Bạn cần ứng dụng {{app}} để tham gia vào cuộc họp này bằng điện thoại.",
|
||||
"description": "Chúng tôi đã yêu cầu chạy cuộc họp trên ứng dụng {{app}}, ứng dụng vẫn không mở? Thử lại hoặc chạy trên trang web.",
|
||||
"descriptionWithoutWeb": "Chúng tôi đã yêu cầu chạy cuộc họp trên ứng dụng {{app}}, ứng dụng vẫn không mở? Hãy thử lại.",
|
||||
"downloadApp": "Tải ứng dụng",
|
||||
"launchWebButton": "Chạy trên trang web",
|
||||
"openApp": "Tiếp tục trên ứng dụng này",
|
||||
"title": "Thực hiện cuộc họp trên {{app}}…",
|
||||
"tryAgainButton": "Thử lại trên desktop"
|
||||
"tryAgainButton": "Thử lại"
|
||||
},
|
||||
"defaultLink": "ví dụ: {{url}}",
|
||||
"defaultLink": "Ví dụ: {{url}}",
|
||||
"deviceError": {
|
||||
"cameraError": "Truy cập camera thất bại",
|
||||
"cameraPermission": "Lỗi đọc quyền của camera",
|
||||
"microphoneError": "Truy cập Microphone thất bại",
|
||||
"microphonePermission": "Lỗi đọc quyền của microphone"
|
||||
"cameraPermission": "Lỗi cấp quyền camera",
|
||||
"microphoneError": "Truy cập micro thất bại",
|
||||
"microphonePermission": "Lỗi cấp quyền micro"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"noPermission": "Không được cấp quyền",
|
||||
"previewUnavailable": "Xem trước không khả dụng",
|
||||
"selectADevice": "Chọn một thiết bị",
|
||||
"testAudio": "Chạy thử tệp âm thanh"
|
||||
"testAudio": "Phát thử âm thanh"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "hiện đang {{status}}"
|
||||
@@ -142,38 +142,38 @@
|
||||
"dialog": {
|
||||
"Back": "Quay lại",
|
||||
"Cancel": "Hủy",
|
||||
"IamHost": "Tôi là chủ nghị",
|
||||
"Ok": "Được",
|
||||
"IamHost": "Mình là quản trị viên",
|
||||
"Ok": "Đồng ý",
|
||||
"Remove": "Xóa",
|
||||
"Share": "Chia sẻ",
|
||||
"Submit": "Đăng ký",
|
||||
"WaitForHostMsg": "Cuộc họp chưa được khởi tạo. Nếu bạn là chủ nghị vui lòng xác thực. Nếu không, vui lòng đợi chủ nghị.",
|
||||
"WaitingForHost": "Đang đợi chủ nghị …",
|
||||
"WaitForHostMsg": "Cuộc họp chưa được bắt đầu. Nếu bạn là quản trị viên vui lòng xác thực. Nếu không, vui lòng đợi quản trị viên.",
|
||||
"WaitingForHost": "Đang đợi quản trị viên...",
|
||||
"Yes": "Có",
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": "Phát trực tuyến"
|
||||
},
|
||||
"allow": "Cho phép",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"alreadySharedVideoTitle": "Mỗi lúc chỉ một người được chia sẻ video.",
|
||||
"alreadySharedVideoTitle": "Chỉ một người được chia sẻ video đồng thời.",
|
||||
"applicationWindow": "Cửa sổ ứng dụng",
|
||||
"cameraConstraintFailedError": "Camera của bạn không đáp ứng được một số yêu cầu bắt buộc.",
|
||||
"cameraNotFoundError": "Không tìm thấy camera.",
|
||||
"cameraNotSendingData": "Không truy cập được camera của bạn. Kiểm tra xem có ứng dung khác đang sử dụng camera không, hoặc chọn một camera khác trong phần cài đặt, hay tải lại ứng dụng",
|
||||
"cameraNotSendingDataTitle": "Không truy cập được camera",
|
||||
"cameraPermissionDeniedError": "Bạn chưa cho phép sử dụng camera của mình. Bạn vẫn có thể tham gia hội nghị nhưng những người khác sẽ không nhìn thấy bạn. Sử dụng nút camera trên thanh địa chỉ để sửa lỗi này.",
|
||||
"cameraUnknownError": "Không thể sử dụng camera vì lý do không rõ ràng.",
|
||||
"cameraPermissionDeniedError": "Bạn chưa cho phép sử dụng camera của mình. Bạn vẫn có thể tham gia cuộc họp nhưng những người khác sẽ không nhìn thấy bạn. Sử dụng nút camera trên thanh điều hướng để sửa lỗi này.",
|
||||
"cameraUnknownError": "Không thể sử dụng camera vì một lý do không xác định.",
|
||||
"cameraUnsupportedResolutionError": "Camera của bạn không hỗ trợ độ phân giải video yêu cầu.",
|
||||
"close": "Đóng",
|
||||
"conferenceDisconnectMsg": "Bạn có thể muốn kiểm tra kết nối mạng của mình. Đang kết nối lại trong {{seconds}} giây…",
|
||||
"conferenceDisconnectMsg": "Bạn có thể cần kiểm tra kết nối mạng của mình. Đang kết nối lại trong {{seconds}} giây...",
|
||||
"conferenceDisconnectTitle": "Bạn đã bị ngắt kết nối.",
|
||||
"conferenceReloadMsg": "Chúng tôi đang cố gắng sửa lỗi này. Đang kết nối lại trong {{seconds}} giây…",
|
||||
"conferenceReloadTitle": "Thật không may, có điều gì đó đã sai.",
|
||||
"conferenceReloadMsg": "Chúng tôi đang cố gắng sửa lỗi này. Đang kết nối lại trong {{seconds}} giây...",
|
||||
"conferenceReloadTitle": "Thật không may, đã có lỗi xảy ra.",
|
||||
"confirm": "Xác nhận",
|
||||
"confirmNo": "Không",
|
||||
"confirmYes": "Có",
|
||||
"connectError": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với hội nghị.",
|
||||
"connectErrorWithMsg": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với hội nghị: {{msg}}",
|
||||
"connectError": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với cuộc họp.",
|
||||
"connectErrorWithMsg": "Rất tiếc! Đã xảy ra sự cố và chúng tôi không thể kết nối với cuộc họp. Nguyên nhân: {{msg}}",
|
||||
"connecting": "Đang kết nối",
|
||||
"contactSupport": "Liên hệ hỗ trợ kỹ thuật",
|
||||
"copy": "Sao chép",
|
||||
@@ -189,39 +189,39 @@
|
||||
"externalInstallationTitle": "Yêu cầu tiện ích mở rộng",
|
||||
"goToStore": "Đi tới cửa hàng trên mạng",
|
||||
"gracefulShutdown": "Dịch vụ của chúng tôi hiện đang bảo trì. Vui lòng thử lại sau.",
|
||||
"grantModeratorDialog": " Bạn có thực sự muốn cấp quyền quản trị cho người này?",
|
||||
"grantModeratorDialog": "Bạn có thực sự muốn cấp quyền quản trị cho người này?",
|
||||
"incorrectPassword": "Tên người dùng hoặc mật khẩu không đúng",
|
||||
"incorrectRoomLockPassword": "",
|
||||
"incorrectRoomLockPassword": "Mật khẩu không đúng",
|
||||
"inlineInstallExtension": "Cài đặt ngay",
|
||||
"inlineInstallationMsg": "Bạn cần cài đặt tiện ích mở rộng chia sẻ máy tính của chúng tôi.",
|
||||
"internalError": "Duh! Có lỗi xẩy ra. Lỗi cụ thể là: {{error}}",
|
||||
"internalError": "Đã có lỗi xảy ra. Chi tiết: {{error}}",
|
||||
"internalErrorTitle": "Lỗi cục bộ",
|
||||
"kickMessage": "",
|
||||
"kickParticipantButton": "Đẩy ra",
|
||||
"kickParticipantDialog": "Bạn có chắc muốn đẩy người này ra?",
|
||||
"kickParticipantButton": "Đuổi ra",
|
||||
"kickParticipantDialog": "Bạn có chắc muốn đuổi người này ra?",
|
||||
"kickParticipantTitle": "Tắt tiếng của người tham dự này?",
|
||||
"kickTitle": "",
|
||||
"liveStreaming": "Phát trực tuyến",
|
||||
"liveStreamingDisabledForGuestTooltip": "Khách không thể phát trực tuyến.",
|
||||
"liveStreamingDisabledTooltip": "Khởi tạo phát trực tuyến đã tắt.",
|
||||
"lockMessage": "Khóa hội nghị thất bại.",
|
||||
"lockMessage": "Khóa cuộc họp thất bại.",
|
||||
"lockRoom": "",
|
||||
"lockTitle": "Khóa thất bại",
|
||||
"logoutQuestion": "Bạn có chắc chắn muốn đăng xuất và dừng hội nghị?",
|
||||
"logoutQuestion": "Bạn có chắc chắn muốn đăng xuất và dừng cuộc họp?",
|
||||
"logoutTitle": "Đăng xuất",
|
||||
"maxUsersLimitReached": "",
|
||||
"maxUsersLimitReachedTitle": "",
|
||||
"micConstraintFailedError": "Microphone của bạn không đáp ứng được một số yêu cầu bắt buộc.",
|
||||
"micNotFoundError": "Không tìm thấy microphone.",
|
||||
"micConstraintFailedError": "Micro của bạn không đáp ứng được một số yêu cầu bắt buộc.",
|
||||
"micNotFoundError": "Không tìm thấy micro.",
|
||||
"micNotSendingData": "",
|
||||
"micNotSendingDataTitle": "",
|
||||
"micPermissionDeniedError": "Bạn chưa cấp phép sử dụng microphone của bạn. Bạn vẫn có thể tham gia hội nghị nhưng những người khác sẽ không nghe thấy bạn. Sử dụng nút camera trên thanh địa chỉ để sửa lỗi này.",
|
||||
"micUnknownError": "Không thể sử dụng microphone vì lý do không rõ ràng.",
|
||||
"micPermissionDeniedError": "Bạn chưa cấp phép sử dụng micro của bạn. Bạn vẫn có thể tham gia hội nghị nhưng những người khác sẽ không nghe thấy bạn. Sử dụng nút micro trên thanh điều hướng để sửa lỗi này.",
|
||||
"micUnknownError": "Không thể sử dụng micro vì một lý do không xác định.",
|
||||
"muteEveryoneDialog": " Bạn có thực sự muốn tắt tiếng tất cả mọi người? Bạn sẽ không thể bật lại tiếng cho họ nhưng họ có thể tự mở tiếng lại bất kỳ lúc nào.",
|
||||
"muteEveryoneElseDialog": "Một khi đã tắt tiếng, bạn không thể bật lại. Nhưng họ có thể tự mở tiếng lại bất kỳ lúc nào.",
|
||||
"muteEveryoneElseTitle": "Tắt tiếng tất cả ngoại trừ {{whom}}?",
|
||||
"muteEveryoneTitle": "Tắt tiếng tất cả mọi người?",
|
||||
"muteEveryonesVideoDialog": " Bạn có chắc muốn tắt camera của tất cả mọi người? Bạn không thể mở lại camera của người tham dự nhưng họ có thể mở lại bất kỳ lúc nào.",
|
||||
"muteEveryonesVideoDialog": "Bạn có chắc muốn tắt camera của tất cả mọi người? Bạn không thể mở lại camera của người tham dự nhưng họ có thể mở lại bất kỳ lúc nào.",
|
||||
"muteEveryonesVideoTitle": "Tắt camera của tất cả mọi người?",
|
||||
"muteParticipantBody": "Bạn không thể tắt tiếng của họ, nhưng họ có thể tự tắt tiếng bất cứ lúc nào.",
|
||||
"muteParticipantButton": "Tắt tiếng",
|
||||
@@ -234,12 +234,12 @@
|
||||
"passwordNotSupported": "Phòng họp không hỗ trợ khóa bằng mật khẩu.",
|
||||
"passwordNotSupportedTitle": "",
|
||||
"passwordRequired": "",
|
||||
"popupError": "Trình duyệt của bạn đã chặn cửa sổ pop-up từ website hiện hành. Làm ơn cho phép pop-up trong cài đặt của trình duyệt và thử lại",
|
||||
"popupError": "Trình duyệt của bạn đã chặn cửa sổ pop-up từ trang web hiện tại. Vui lòng cho phép pop-up trong cài đặt của trình duyệt và thử lại",
|
||||
"popupErrorTitle": "Cửa sổ Pop-Up bị chặn",
|
||||
"recording": "Đang ghi âm",
|
||||
"recording": "Đang ghi hình",
|
||||
"recordingDisabledForGuestTooltip": "Khách không thể khởi tạo ghi hình.",
|
||||
"recordingDisabledTooltip": "Khởi động ghi âm đã bị tắt.",
|
||||
"rejoinNow": "Tham gia lại luôn",
|
||||
"recordingDisabledTooltip": "Khởi động ghi hình đã bị tắt.",
|
||||
"rejoinNow": "Tham gia lại ngay",
|
||||
"remoteControlAllowedMessage": "{{user}} đã chấp nhận yêu cầu điều khiển từ xa của bạn!",
|
||||
"remoteControlDeniedMessage": "{{user}} đã từ chối yêu cầu điều khiển từ xa của bạn!",
|
||||
"remoteControlErrorMessage": "Đã xảy ra lỗi khi cố gắng yêu cầu quyền điều khiển từ xa từ {{user}}!",
|
||||
@@ -254,11 +254,11 @@
|
||||
"reservationErrorMsg": "Mã lỗi: {{code}}, thông báo: {{msg}}",
|
||||
"retry": "Thử lại",
|
||||
"screenSharingAudio": "Chia sẻ âm thanh",
|
||||
"screenSharingFailedToInstall": "Duh! Không cài đặt được bộ mở rộng chia sẻ màn hình",
|
||||
"screenSharingFailedToInstallTitle": "Duh! Bộ mở rộng chia sẻ màn hình có vấn đề với cấu hình bảo mật. Làm ơn tải và thử lại ",
|
||||
"screenSharingFailedToInstall": "Lỗi! Không cài đặt được bộ mở rộng chia sẻ màn hình",
|
||||
"screenSharingFailedToInstallTitle": "Lỗi! Bộ mở rộng chia sẻ màn hình có vấn đề với cấu hình bảo mật. Vui lòng tải và thử lại ",
|
||||
"screenSharingFirefoxPermissionDeniedError": "Có gì đó sai khi chúng tôi cố gắng chia sẻ màn hình của bạn. Vui lòng đảm bảo bạn đã cho phép chúng tôi thực hiện.",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "Chúng tôi không thể chia sẻ màn hình!",
|
||||
"screenSharingPermissionDeniedError": "Không thể truy cập micro",
|
||||
"screenSharingPermissionDeniedError": "Không thể truy cập màn hình do lỗi cấp quyền",
|
||||
"serviceUnavailable": "Dịch vụ không khả dụng",
|
||||
"sessTerminated": "Cuộc gọi kết thúc",
|
||||
"shareVideoLinkError": "Vui lòng cung cấp liên kết chính xác.",
|
||||
@@ -267,13 +267,13 @@
|
||||
"shareYourScreenDisabled": "Chia sẻ màn hình đã tắt.",
|
||||
"shareYourScreenDisabledForGuest": "Khách không thể chia sẻ màn hình.",
|
||||
"startLiveStreaming": "Bắt đầu phát trực tuyến",
|
||||
"startRecording": "Bắt đầu ghi âm",
|
||||
"startRecording": "Bắt đầu ghi hình",
|
||||
"startRemoteControlErrorMessage": "Có lỗi khi thử khởi động phiên điều khiển từ xa",
|
||||
"stopLiveStreaming": "Dừng phát trực tuyến",
|
||||
"stopRecording": "Dừng ghi âm",
|
||||
"stopRecordingWarning": "Bạn có chắc chắn muốn dừng ghi âm không?",
|
||||
"stopRecording": "Dừng ghi hình",
|
||||
"stopRecordingWarning": "Bạn có chắc chắn muốn dừng ghi hình không?",
|
||||
"stopStreamingWarning": "Bạn có chắc chắn muốn dừng phát trực tuyến?",
|
||||
"streamKey": "Key phát trực tuyến",
|
||||
"streamKey": "Mã phát trực tuyến",
|
||||
"thankYou": "Cám ơn bạn đã sử dụng {{appName}}!",
|
||||
"token": "mã thông báo",
|
||||
"tokenAuthFailed": "Rất tiếc, bạn không được phép tham gia cuộc gọi này.",
|
||||
@@ -308,13 +308,13 @@
|
||||
"cancelPassword": "",
|
||||
"conferenceURL": "Liên kết:",
|
||||
"country": "Quốc gia",
|
||||
"dialANumber": "Để tham gia cuộc họp của bạn, quay một trong các số sau và nhập mã.",
|
||||
"dialANumber": "Để tham gia cuộc họp của bạn, gọi một trong các số sau và nhập mã.",
|
||||
"dialInConferenceID": "Mã:",
|
||||
"dialInNotSupported": "Xin lỗi, quay số không được hỗ trợ.",
|
||||
"dialInNumber": "Quay số:",
|
||||
"dialInSummaryError": "Lỗi nạp thông tin quay số. Vui lòng thử lại.",
|
||||
"dialInTollFree": "Miễn phí",
|
||||
"genericError": "Chà, có gì đó không ổn.",
|
||||
"genericError": "Lỗi, có gì đó không ổn.",
|
||||
"inviteLiveStream": "Để xem phát trực tuyến cuộc họp này, chọn liên kết: {{url}}",
|
||||
"invitePhone": "",
|
||||
"invitePhoneAlternatives": "",
|
||||
@@ -333,10 +333,10 @@
|
||||
"tooltip": "Chia sẻ liên kết và thông tin quay số của cuộc họp này"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Chúng tôi hơi vấp chút.",
|
||||
"msg": "Chúng tôi đang xảy ra chút lỗi.",
|
||||
"retry": "Thử lại",
|
||||
"support": "Hỗ trợ",
|
||||
"supportMsg": "Nếu vẫn xảy ra, hãy liên hệ với"
|
||||
"supportMsg": "Nếu vẫn xảy ra, hãy liên hệ với "
|
||||
},
|
||||
"inviteDialog": {
|
||||
"alertText": "Không thể mời một vài người.",
|
||||
@@ -347,20 +347,20 @@
|
||||
"send": "Gửi"
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "Tập trung vào video của bạn",
|
||||
"focusRemote": "Tập trung vào hình ảnh của người khác",
|
||||
"focusLocal": "Tập trung vào khung hình của bạn",
|
||||
"focusRemote": "Tập trung vào khung hình của người khác",
|
||||
"fullScreen": "Xem hoặc thoát chế độ toàn màn hình",
|
||||
"keyboardShortcuts": "Phím tắt",
|
||||
"localRecording": "Hiện hoặc ẩn Kiểm soát ghi hình cục bộ",
|
||||
"mute": "Tắt hoặc bật microphone của bạn",
|
||||
"mute": "Tắt hoặc bật micro của bạn",
|
||||
"pushToTalk": "Ấn chuông để nói chuyện",
|
||||
"raiseHand": "Giơ hoặc Hạ tay",
|
||||
"raiseHand": "Giơ hoặc hạ tay",
|
||||
"showSpeakerStats": "Hiển thị thống kê của diễn giả",
|
||||
"toggleChat": "Mở hoặc Đóng cuộc hội thoại",
|
||||
"toggleChat": "Mở hoặc đóng cuộc hội thoại",
|
||||
"toggleFilmstrip": "Hiện hoặc ẩn hình ảnh thu nhỏ",
|
||||
"toggleScreensharing": "Chuyển đổi giữa camera và chia sẻ màn hình",
|
||||
"toggleShortcuts": "Hiện hoặc ẩn phím tắt",
|
||||
"videoMute": "Bật hoặc Tắt camera của bạn"
|
||||
"videoMute": "Bật hoặc tắt camera của bạn"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "Chúng tôi đang giải phóng tài nguyên streaming. Xin thử lại sau vài phút.",
|
||||
@@ -374,13 +374,13 @@
|
||||
"errorLiveStreamNotEnabled": "Phát trực tuyến không được bật với email {{email}}. Vui lòng bật phát trực tuyến hoặc truy cập một tài khoản đã bật phát trực tuyến.",
|
||||
"expandedOff": "Phát trực tuyến đã dừng",
|
||||
"expandedOn": "Cuộc họp đang được phát trên Youtube.",
|
||||
"expandedPending": "Phát trực tuyến đang bắt đầu…",
|
||||
"expandedPending": "Phát trực tuyến đang bắt đầu...",
|
||||
"failedToStart": "Không thể bắt đầu phát trực tuyến",
|
||||
"getStreamKeyManually": "Không thể thu nhận phát trực tuyến nào. Thử lấy mã phát trực tuyến từ Youtube.",
|
||||
"invalidStreamKey": "Mã phát trực tuyến có thể sai.",
|
||||
"off": "Phát trực tuyến đã dừng",
|
||||
"on": "Phát trực tuyến",
|
||||
"pending": "Đang bắt đầu phát trực tuyến…",
|
||||
"pending": "Đang bắt đầu phát trực tuyến...",
|
||||
"serviceName": "Dịch vụ Phát trực tuyến",
|
||||
"signIn": "Đăng nhập với Google",
|
||||
"signInCTA": "Đăng nhập hoặc nhập key phát trực tuyến từ Youtube.",
|
||||
@@ -389,7 +389,7 @@
|
||||
"start": "Bắt đầu phát trực tuyến",
|
||||
"streamIdHelp": "Đây là gì?",
|
||||
"title": "Phát trực tuyến",
|
||||
"unavailableTitle": "Không Live Stream được"
|
||||
"unavailableTitle": "Không thể hát trực tuyến"
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
@@ -401,23 +401,23 @@
|
||||
"duration": "Thời lượng",
|
||||
"durationNA": "Không",
|
||||
"encoding": "Mã hóa",
|
||||
"label": "Trời ơi",
|
||||
"label": "Ghi hình",
|
||||
"labelToolTip": "Ghi hình cục bộ đang bận",
|
||||
"localRecording": "Ghi hình cục bộ",
|
||||
"me": "Tôi",
|
||||
"messages": {
|
||||
"engaged": "Ghi hình cục bộ đã bận.",
|
||||
"finished": "Phiên ghi hình {{token}} đã kết thúc. Vui lòng gửi tệp ghi hình cho người điều hành.",
|
||||
"finishedModerator": "Token phiên ghi hình {{token}} đã kết thúc. Ghi hình cục bộ đã được lưu. Vui lòng hỏi những người tham gia khác để cung cấp ghi hình của họ.",
|
||||
"finishedModerator": "Phiên ghi hình {{token}} đã kết thúc. Ghi hình cục bộ đã được lưu. Vui lòng hỏi những người tham gia khác để cung cấp ghi hình của họ.",
|
||||
"notModerator": "Bạn không phải người điều hành. Bạn không thể khởi tạo hoặc dừng ghi hình."
|
||||
},
|
||||
"moderator": "Quản trị viên",
|
||||
"no": "Không",
|
||||
"participant": "Người tham gia",
|
||||
"participantStats": "Trạng thái người tham gia",
|
||||
"participant": "Người tham dự",
|
||||
"participantStats": "Thống kê người tham dự",
|
||||
"sessionToken": "Mã phiên",
|
||||
"start": "Bắt đầu ghi âm",
|
||||
"stop": "Dừng ghi âm",
|
||||
"start": "Bắt đầu ghi hình",
|
||||
"stop": "Dừng ghi hình",
|
||||
"yes": "Có"
|
||||
},
|
||||
"lockRoomPassword": "Mật khẩu",
|
||||
@@ -425,32 +425,32 @@
|
||||
"me": "Tôi",
|
||||
"notify": {
|
||||
"connectedOneMember": "{{name}} đã tham gia cuộc họp",
|
||||
"connectedThreePlusMembers": "{{name}} và{{count}} khác đã tham gia cuộc họp",
|
||||
"connectedTwoMembers": "{{first}} và{{second}} đã tham gia cuộc họp",
|
||||
"connectedThreePlusMembers": "{{name}} và {{count}} người khác đã tham gia cuộc họp",
|
||||
"connectedTwoMembers": "{{first}} và {{second}} đã tham gia cuộc họp",
|
||||
"disconnected": "đã ngắt kết nối",
|
||||
"focus": "Hội nghị tập trung",
|
||||
"focus": "Cuộc họp tập trung",
|
||||
"focusFail": "{{component}} không khả dụng - thử lại trong {{ms}} giây",
|
||||
"grantedTo": "Quyền của người điều hành đã được cấp cho {{to}}!",
|
||||
"grantedTo": "Quyền quản trị viên đã được cấp cho {{to}}!",
|
||||
"invitedOneMember": "{{name}} đã được mời",
|
||||
"invitedThreePlusMembers": "",
|
||||
"invitedTwoMembers": "",
|
||||
"kickParticipant": "",
|
||||
"me": "Tôi",
|
||||
"moderator": "Quyền của người điều hành đã được cấp!",
|
||||
"moderator": "Quyền quản trị viên đã được cấp!",
|
||||
"muted": "Bạn đã bắt đầu cuộc trò chuyện bị tắt tiếng.",
|
||||
"mutedRemotelyDescription": "",
|
||||
"mutedRemotelyTitle": "",
|
||||
"mutedTitle": "Bạn bị tắt tiếng!",
|
||||
"newDeviceAction": "Sử dụng",
|
||||
"newDeviceAudioTitle": "Thiết bị âm thanh mới được phát hiện",
|
||||
"newDeviceCameraTitle": "Camera mới được phát hiện",
|
||||
"newDeviceCameraTitle": "Thiết bị camera mới được phát hiện",
|
||||
"passwordRemovedRemotely": "",
|
||||
"passwordSetRemotely": "",
|
||||
"raisedHand": "{{name}} muốn phát biểu.",
|
||||
"somebody": "Ai đó",
|
||||
"startSilentDescription": "",
|
||||
"startSilentTitle": "",
|
||||
"suboptimalExperienceDescription": "Chúng tôi lo rằng trải nghiệm của bạn với {{appName}} đang không tốt. Chúng tôi đang tìm cách cải thiện, hiện tại thử một trong <a href='{{recommendedBrowserPageLink}}' target='_blank'>các trình duyệt được hỗ trợ</a>.",
|
||||
"suboptimalExperienceDescription": "Chúng tôi lo rằng trải nghiệm của bạn với {{appName}} đang không tốt. Chúng tôi đang tìm cách cải thiện, hiện tại thử một trong <a href='{{recommendedBrowserPageLink}}' target='_blank'>các trình duyệt được hỗ trợ</a>.",
|
||||
"suboptimalExperienceTitle": "Cảnh báo trình duyệt",
|
||||
"unmute": ""
|
||||
},
|
||||
@@ -461,7 +461,7 @@
|
||||
"mute": "Tắt tiếng",
|
||||
"muteAll": "Tắt tiếng tất cả mọi người",
|
||||
"muteEveryoneElse": "Tắt tiếng tất cả những người khác",
|
||||
"startModeration": "Unmute themselves or start video",
|
||||
"startModeration": "Tự bật tiếng hoặc bắt đầu video",
|
||||
"stopEveryonesVideo": "Tắt hình của tất cả mọi người",
|
||||
"stopVideo": "Tắt hình",
|
||||
"unblockEveryoneMicCamera": "Mở khóa camera và micro của tất cả mọi người"
|
||||
@@ -492,7 +492,7 @@
|
||||
"profile": {
|
||||
"setDisplayNameLabel": "Nhập tên hiển thị của bạn",
|
||||
"setEmailInput": "Nhập địa chỉ email",
|
||||
"setEmailLabel": "Nhập địa chỉ Gravatar thư điện tử của bạn",
|
||||
"setEmailLabel": "Nhập địa chỉ email tài khoản Gravatar của bạn",
|
||||
"title": "Hồ sơ"
|
||||
},
|
||||
"recording": {
|
||||
@@ -501,28 +501,28 @@
|
||||
"beta": "Bản thử nghiệm",
|
||||
"busy": "Chương trình đang bận giải phóng tài nguyên thu hình. Xin thử lại sau vài phút.",
|
||||
"busyTitle": "Tất cả các đầu ghi hình hiện đang bận.",
|
||||
"error": "Ghi âm không thành công. Vui lòng thử lại.",
|
||||
"error": "Ghi hình không thành công. Vui lòng thử lại.",
|
||||
"expandedOff": "Ghi hình đã dừng",
|
||||
"expandedOn": "Cuộc họp đang được ghi hình.",
|
||||
"expandedPending": "Ghi hình đang khởi động…",
|
||||
"failedToStart": "Khởi động ghi âm thất bại",
|
||||
"expandedPending": "Ghi hình đang khởi động...",
|
||||
"failedToStart": "Khởi động ghi hình thất bại",
|
||||
"fileSharingdescription": "Chia sẻ ghi hình với người tham gia họp",
|
||||
"live": "Trực tuyến",
|
||||
"loggedIn": "Đã đăng nhập dưới tên {{userName}}",
|
||||
"off": "Đã ngừng ghi âm",
|
||||
"on": "Đang ghi âm",
|
||||
"pending": "Đang chuẩn bị để ghi hình cuộc họp…",
|
||||
"off": "Đã ngừng ghi hình",
|
||||
"on": "Đang ghi hình",
|
||||
"pending": "Đang chuẩn bị để ghi hình cuộc họp...",
|
||||
"rec": "REC",
|
||||
"serviceDescription": "Ghi hình của bạn sẽ được lưu bởi dịch vụ ghi hình",
|
||||
"serviceName": "Dịch vụ ghi hình",
|
||||
"signIn": "Đăng nhập",
|
||||
"signOut": "Đăng xuất",
|
||||
"title": "Đang ghi âm",
|
||||
"title": "Đang ghi hình",
|
||||
"unavailable": "Rất tiếc! Dịch vụ {{serviceName}} đang không sẵn sàng. Chúng tôi đang xử lý vấn đề này. Vui lòng thử lại sau.",
|
||||
"unavailableTitle": "Ghi hình không hoạt động."
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Kéo để làm tươi"
|
||||
"pullToRefresh": "Kéo để làm mới"
|
||||
},
|
||||
"security": {
|
||||
"about": "Bạn có thể thiết lập mật khẩu cho cuộc họp. Người tham dự cần phải nhập mật khẩu trước khi được phép vào phòng họp.",
|
||||
@@ -547,7 +547,7 @@
|
||||
"noDevice": "Không",
|
||||
"selectAudioOutput": "Đầu ra âm thanh",
|
||||
"selectCamera": "Camera",
|
||||
"selectMic": "Microphone",
|
||||
"selectMic": "Micro",
|
||||
"startAudioMuted": "Mọi người bắt đầu đều bị tắt tiếng",
|
||||
"startVideoMuted": "Mọi người bắt đầu đều bị ẩn",
|
||||
"title": "Cài đặt"
|
||||
@@ -557,14 +557,14 @@
|
||||
"alertTitle": "Cảnh báo",
|
||||
"alertURLText": "URL máy chủ đã nhập không hợp lệ",
|
||||
"buildInfoSection": "Thông tin phiên bản",
|
||||
"conferenceSection": "Hội nghị",
|
||||
"conferenceSection": "Cuộc họp",
|
||||
"displayName": "Tên hiển thị",
|
||||
"email": "Email",
|
||||
"header": "Cài đặt",
|
||||
"profileSection": "Hồ sơ",
|
||||
"serverURL": "URL máy chủ",
|
||||
"startWithAudioMuted": "Bắt đầu mà không thu tiếng",
|
||||
"startWithVideoMuted": "Bắt đầu không thu hình",
|
||||
"startWithAudioMuted": "Bắt đầu không mở tiếng",
|
||||
"startWithVideoMuted": "Bắt đầu không hiện hình",
|
||||
"version": "Phiên bản"
|
||||
},
|
||||
"share": {
|
||||
@@ -573,72 +573,72 @@
|
||||
},
|
||||
"speaker": "Diễn giả",
|
||||
"speakerStats": {
|
||||
"hours": "{{count}}h",
|
||||
"minutes": "{{count}}m",
|
||||
"hours": "{{count}} giờ",
|
||||
"minutes": "{{count}} phút",
|
||||
"name": "Tên",
|
||||
"seconds": "{{count}}s",
|
||||
"seconds": "{{count}} giây",
|
||||
"speakerStats": "Thống kê về diễn giả",
|
||||
"speakerTime": "Thời gian của diễn giả"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"title": "{{app}} cần sử dụng microphone và camera của bạn."
|
||||
"title": "{{app}} cần sử dụng micro và camera của bạn."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"rejoinKeyTitle": "Tham gia lại",
|
||||
"text": "Bấm nút <i> Rejoin </i> để kết nối lại.",
|
||||
"title": "Cuộc gọi hình của bạn bị gián đoạn vì máy tính này chuyển sang trạng thái ngủ."
|
||||
"text": "Bấm nút <i> Tham gia lại </i> để kết nối lại.",
|
||||
"title": "Cuộc họp của bạn bị gián đoạn vì máy tính này chuyển sang trạng thái ngủ."
|
||||
},
|
||||
"toolbar": {
|
||||
"Settings": "Cài đặt",
|
||||
"accessibilityLabel": {
|
||||
"Settings": "Mở/Đóng Cấu hình",
|
||||
"audioOnly": "Chuyển sang chỉ tiếng",
|
||||
"audioOnly": "Chuyển sang chỉ nghe âm thanh",
|
||||
"audioRoute": "Chọn thiết bị âm thanh",
|
||||
"callQuality": "",
|
||||
"cc": "Mở/Đóng phụ đề",
|
||||
"chat": "Mở/Đóng cửa sổ Chat",
|
||||
"chat": "Mở/Đóng cuộc hội thoại",
|
||||
"document": "Mở/Đóng tài liệu được chia sẻ",
|
||||
"feedback": "Để lại phản hồi",
|
||||
"fullScreen": "Mở/Đóng toàn màn hình",
|
||||
"fullScreen": "Bật/Tắt toàn màn hình",
|
||||
"grantModerator": "Cấp quyền quản trị",
|
||||
"hangup": "Rời cuộc gọi",
|
||||
"invite": "Mời người tham gia",
|
||||
"kick": "Đẩy người tham gia ra",
|
||||
"localRecording": "Mở/Đóng điều khiển ghi hình cục bộ",
|
||||
"kick": "Đuổi người tham gia ra",
|
||||
"localRecording": "Bật/Tắt điều khiển ghi hình cục bộ",
|
||||
"lockRoom": "Mở/Đóng mật khẩu phòng họp",
|
||||
"moreActions": "Mở/Đóng Thêm hành động",
|
||||
"moreActionsMenu": "Menu Thêm hành động",
|
||||
"mute": "Mở/Đóng Tắt tiếng",
|
||||
"moreActions": "Xem thêm",
|
||||
"moreActionsMenu": "Menu thêm",
|
||||
"mute": "Bật/Tắt tiếng",
|
||||
"muteEveryone": "Tắt tiếng tất cả mọi người",
|
||||
"muteEveryoneElse": "Tắt tiếng những người khác",
|
||||
"muteEveryonesVideo": "Tắt tất cả camera",
|
||||
"pip": "Mở/Đóng chế độ Hình-trong-Hình",
|
||||
"pip": "Bật/Tắt chế độ Hình-trong-Hình",
|
||||
"profile": "Chỉnh sửa hồ sơ cá nhân",
|
||||
"raiseHand": "Mở/Đóng Giơ tay",
|
||||
"recording": "Mở/Đóng Ghi hình",
|
||||
"raiseHand": "Giơ/Hạ tay",
|
||||
"recording": "Bật/Tắt ghi hình",
|
||||
"remoteMute": "Tắt tiếng người tham gia",
|
||||
"selectBackground": "Chọn hình nền",
|
||||
"shareRoom": "Mời ai đó",
|
||||
"shareYourScreen": "Mở/Đóng Chia sẻ màn hình",
|
||||
"sharedvideo": "Mở/Đóng Chia",
|
||||
"shortcuts": "Mở/Đóng Phím tắt",
|
||||
"shareYourScreen": "Bật/Tắt chia sẻ màn hình",
|
||||
"sharedvideo": "Mở/Đóng chia sẻ video",
|
||||
"shortcuts": "Bật/Tắt phím tắt",
|
||||
"show": "",
|
||||
"speakerStats": "Mở/Đóng Thống kê",
|
||||
"tileView": "Mở/Đóng Xem dạng lưới",
|
||||
"toggleCamera": "Mở/Đóng Camera",
|
||||
"speakerStats": "Mở/Đóng thống kê",
|
||||
"tileView": "Mở/Đóng xem dạng lưới",
|
||||
"toggleCamera": "Bật/Tắt Camera",
|
||||
"videoblur": "Chuyển đổi làm mờ video",
|
||||
"videomute": "Mở/Đóng Tiếng, Hình"
|
||||
"videomute": "Bật/Tắt tiếng và hình"
|
||||
},
|
||||
"addPeople": "Thêm người vào cuộc gọi",
|
||||
"audioOnlyOff": "Chế độ chỉ tắt tiếng",
|
||||
"audioOnlyOn": "Bật chế độ Chỉ âm thanh",
|
||||
"addPeople": "Thêm người vào cuộc họp",
|
||||
"audioOnlyOff": "Tắt chế độ chỉ nghe âm thanh",
|
||||
"audioOnlyOn": "Bật chế độ chỉ nghe âm thanh",
|
||||
"audioRoute": "Chọn thiết bị âm thanh",
|
||||
"audioSettings": "Cài đặt âm thanh",
|
||||
"authenticate": "Xác thực",
|
||||
"callQuality": "Chỉnh chất lượng",
|
||||
"chat": "Mở / Đóng cuộc hội thoại",
|
||||
"closeChat": "Đóng Chat",
|
||||
"chat": "Mở/Đóng cuộc hội thoại",
|
||||
"closeChat": "Đóng cuộc hội thoại",
|
||||
"documentClose": "Đóng tài liệu được chia sẻ",
|
||||
"documentOpen": "Mở tài liệu được chia sẻ",
|
||||
"enterFullScreen": "Xem toàn màn hình",
|
||||
@@ -646,29 +646,29 @@
|
||||
"exitFullScreen": "Thoát toàn màn hình",
|
||||
"exitTileView": "Thoát xem dạng lưới",
|
||||
"feedback": "Để lại phản hồi",
|
||||
"hangup": "Thoát",
|
||||
"hangup": "Rời cuộc họp",
|
||||
"invite": "Mời người tham gia",
|
||||
"login": "Đăng nhập",
|
||||
"logout": "Đăng xuất",
|
||||
"lowerYourHand": "Hạ tay",
|
||||
"moreActions": "Thêm hành động",
|
||||
"mute": "Tắt tiếng / Bật tiếng",
|
||||
"mute": "Tắt/Bật tiếng",
|
||||
"muteEveryone": "Tắt tiếng tất cả mọi người",
|
||||
"muteEveryonesVideo": "Tắt tất cả camera",
|
||||
"noisyAudioInputDesc": "Dường như micro của bạn đang tạo ra tiếng ồn. Vui lòng tắt tiếng hoặc thay thiết bị khác.",
|
||||
"noisyAudioInputTitle": "Micro của bạn dường như có nhiều tiếng ồn!",
|
||||
"openChat": "Mở Chat",
|
||||
"openChat": "Mở cuộc hội thoại",
|
||||
"participants": "Những người tham dự",
|
||||
"pip": "Vào chế độ Ảnh-trong-Ảnh",
|
||||
"profile": "Chỉnh sửa hồ sơ cá nhân",
|
||||
"raiseHand": "Giơ / Hạ tay",
|
||||
"raiseHand": "Giơ/Hạ tay",
|
||||
"raiseYourHand": "Giơ tay",
|
||||
"security": "Tùy chọn bảo mật",
|
||||
"selectBackground": "Chọn hình nền",
|
||||
"shareRoom": "Mời ai đó",
|
||||
"shareRoom": "Chia sẻ phòng",
|
||||
"sharedvideo": "Chia sẻ video",
|
||||
"shortcuts": "Xem phím tắt",
|
||||
"speakerStats": "Thống kê về diễn giả",
|
||||
"speakerStats": "Thống kê về người tham dự",
|
||||
"startScreenSharing": "Bắt đầu chia sẻ màn hình",
|
||||
"startSubtitles": "Bắt đầu phụ đề",
|
||||
"startvideoblur": "",
|
||||
@@ -676,11 +676,11 @@
|
||||
"stopSharedVideo": "Dừng video",
|
||||
"stopSubtitles": "Dừng phụ đề",
|
||||
"stopvideoblur": "",
|
||||
"talkWhileMutedPopup": "Cố gắng để nói chuyện? Bạn đang tắt tiếng.",
|
||||
"tileViewToggle": "Mở/Đóng Xem dạng lưới",
|
||||
"toggleCamera": "Mở/Đóng Camera",
|
||||
"talkWhileMutedPopup": "Đang nói chuyện? Bạn đang tắt tiếng.",
|
||||
"tileViewToggle": "Mở/Đóng xem dạng lưới",
|
||||
"toggleCamera": "Mở/Đóng camera",
|
||||
"videoSettings": "Cài đặt hình ảnh",
|
||||
"videomute": "Bật / Tắt camera"
|
||||
"videomute": "Bật/Tắt camera"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Chạy/Dừng phụ đề",
|
||||
@@ -689,7 +689,7 @@
|
||||
"failedToStart": "Khởi chạy phiên âm thất bại",
|
||||
"labelToolTip": "Cuộc họp đang được phiên âm",
|
||||
"off": "Phiên âm đã dừng",
|
||||
"pending": "Đang chuẩn bị phiên âm cuộc họp…",
|
||||
"pending": "Đang chuẩn bị phiên âm cuộc họp...",
|
||||
"start": "Bắt đầu hiển thị phụ đề",
|
||||
"stop": "Dừng hiển thị phụ đề",
|
||||
"tr": "TR"
|
||||
@@ -698,17 +698,17 @@
|
||||
"androidGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"chromeGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"edgeGrantPermissions": "Chọn <b><i>Có</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"electronGrantPermissions": "Vui lòng cấp quyền sử dụng camera và microphone của bạn",
|
||||
"electronGrantPermissions": "Vui lòng cấp quyền sử dụng camera và micro của bạn",
|
||||
"firefoxGrantPermissions": "Chọn <b><i>Chia sẻ thiết bị đã chọn</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"iexplorerGrantPermissions": "Chọn <b><i>Có</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"nwjsGrantPermissions": "Vui lòng cấp quyền sử dụng camera và microphone của bạn",
|
||||
"nwjsGrantPermissions": "Vui lòng cấp quyền sử dụng camera và micro của bạn",
|
||||
"operaGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"react-nativeGrantPermissions": "Chọn <b><i>Cho phép</i></b> khi trình duyệt của bạn yêu cầu cấp phép.",
|
||||
"safariGrantPermissions": "Chọn <b><i>Có</i></b> khi trình duyệt của bạn yêu cầu cấp phép."
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "Chúng tôi đang giải phóng tài nguyên. Vui lòng thử lại sau vài phút.",
|
||||
"busyTitle": "Dịch vụ Phòng họp đang bận",
|
||||
"busyTitle": "Dịch vụ phòng họp đang bận",
|
||||
"errorAlreadyInvited": "{{displayName}} đã được mời",
|
||||
"errorInvite": "Cuộc họp chưa được khởi tạo. Vui lòng thử lại.",
|
||||
"errorInviteFailed": "Chúng tôi đang xử lý vấn đề. Vui lòng thử lại sau.",
|
||||
@@ -718,22 +718,22 @@
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "AUD",
|
||||
"audioOnlyExpanded": "Bạn đang ở chế độ chỉ tiếng. Chế độ này giảm băng thông nhưng không thấy hình ảnh người khác.",
|
||||
"audioOnlyExpanded": "Bạn đang ở chế độ chỉ nghe âm thanh. Chế độ này giảm băng thông nhưng không thấy hình ảnh người khác.",
|
||||
"callQuality": "Chất lượng hình ảnh",
|
||||
"hd": "HD",
|
||||
"highDefinition": "HD",
|
||||
"highDefinition": "Độ phân giải cao",
|
||||
"labelTooiltipNoVideo": "Không hình ảnh",
|
||||
"labelTooltipAudioOnly": "Chế độ chỉ tiếng đã bật",
|
||||
"labelTooltipAudioOnly": "Chế độ chỉ nghe âm thanh đã bật",
|
||||
"ld": "LD",
|
||||
"lowDefinition": "Phân giải thấp",
|
||||
"lowDefinition": "Độ phân giải thấp",
|
||||
"onlyAudioAvailable": "Chỉ có âm thanh sẵn sàng",
|
||||
"onlyAudioSupported": "Chỉ hỗ trợ âm thanh trên trình duyệt này.",
|
||||
"sd": "SD",
|
||||
"standardDefinition": "Phân giải SD"
|
||||
"standardDefinition": "Độ phân giải thường"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"domute": "Tắt tiếng",
|
||||
"flip": "Lật",
|
||||
"flip": "Lật hình",
|
||||
"grantModerator": "Cấp quyền quản trị",
|
||||
"kick": "Đẩy ra",
|
||||
"moderator": "Quản trị viên",
|
||||
@@ -771,22 +771,22 @@
|
||||
"join": "Chạm để tham gia",
|
||||
"roomname": "Nhập tên phòng"
|
||||
},
|
||||
"appDescription": "Tiếp tục, chat hình với toàn bộ nhóm. Thực tế, mời người bạn biết. {{app}} được mã hóa, 100% giải pháp hội nghị mã mở mà bạn có thể sử dụng hàng ngày, miễn phí.",
|
||||
"appDescription": "{{app}} được mã hóa, 100% giải pháp cuộc họp trực tuyến mã nguồn mở mà bạn có thể sử dụng hàng ngày, miễn phí.",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Tiếng",
|
||||
"video": "Hình ảnh"
|
||||
},
|
||||
"calendar": "Lịch",
|
||||
"connectCalendarButton": "Kết nối Lịch của bạn",
|
||||
"connectCalendarButton": "Kết nối lịch của bạn",
|
||||
"connectCalendarText": "Kết nối lịch của bạn để xem tất cả các cuộc họp {{app}}. Thêm, thêm cuộc họp {{provider}} vào lịch của bạn và bắt đầu.",
|
||||
"enterRoomTitle": "Bắt đầu cuộc họp mới",
|
||||
"go": "ĐI",
|
||||
"go": "Đi",
|
||||
"info": "Thông tin",
|
||||
"join": "THAM GIA",
|
||||
"join": "Tham gia",
|
||||
"privacy": "Bảo mật",
|
||||
"recentList": "Hiện tại",
|
||||
"recentListDelete": "Xóa",
|
||||
"recentListEmpty": "Danh sách cuộc họp rỗng. Thực hiện cuộc họp và bạn sẽ thấy danh sách hiện tại đây.",
|
||||
"recentListEmpty": "Danh sách cuộc họp trống. Thực hiện cuộc họp và bạn sẽ thấy danh sách hiện tại đây.",
|
||||
"reducedUIText": "",
|
||||
"roomname": "Nhập tên phòng",
|
||||
"roomnameHint": "Thêm tên hoặc URL của phòng họp bạn muốn tham gia. Ban có thể tạo tên phòng, gửi cho người bạn muốn mời để họ sử dụng tên đó.",
|
||||
|
||||
@@ -675,6 +675,7 @@
|
||||
"connectedTwoMembers": "{{first}} and {{second}} joined the meeting",
|
||||
"dataChannelClosed": "Video quality impaired",
|
||||
"dataChannelClosedDescription": "The bridge channel has been disconnected and thus video quality is limited to its lowest setting.",
|
||||
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
|
||||
"disconnected": "disconnected",
|
||||
"displayNotifications": "Display notifications for",
|
||||
"dontRemindMe": "Do not remind me",
|
||||
|
||||
@@ -115,7 +115,11 @@ import { muteAllParticipants } from '../../react/features/video-menu/actions';
|
||||
import { setVideoQuality } from '../../react/features/video-quality/actions';
|
||||
import { getJitsiMeetTransport } from '../transport';
|
||||
|
||||
import { API_ID, ENDPOINT_TEXT_MESSAGE_NAME } from './constants';
|
||||
import {
|
||||
API_ID,
|
||||
ASSUMED_BANDWIDTH_BPS,
|
||||
ENDPOINT_TEXT_MESSAGE_NAME
|
||||
} from './constants';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
@@ -310,6 +314,23 @@ function initCommands() {
|
||||
|
||||
APP.store.dispatch(sendTones(tones, duration, pause));
|
||||
},
|
||||
'set-assumed-bandwidth-bps': value => {
|
||||
logger.debug('Set assumed bandwidth bps command received', value);
|
||||
|
||||
if (typeof value !== 'number' || isNaN(value)) {
|
||||
logger.error('Assumed bandwidth bps must be a number.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { conference } = APP.store.getState()['features/base/conference'];
|
||||
|
||||
if (conference) {
|
||||
conference.setAssumedBandwidthBps(value < ASSUMED_BANDWIDTH_BPS
|
||||
? ASSUMED_BANDWIDTH_BPS
|
||||
: value);
|
||||
}
|
||||
},
|
||||
'set-follow-me': value => {
|
||||
logger.debug('Set follow me command received');
|
||||
|
||||
|
||||
@@ -15,3 +15,10 @@ export const API_ID = parseURLParams(window.location).jitsi_meet_external_api_id
|
||||
* The payload name for the datachannel/endpoint text message event.
|
||||
*/
|
||||
export const ENDPOINT_TEXT_MESSAGE_NAME = 'endpoint-text-message';
|
||||
|
||||
/**
|
||||
* The min value that can be set for the assumed bandwidth.
|
||||
* Setting it to this value means not assuming any bandwidth,
|
||||
* but rather allowing the estimations to take place.
|
||||
*/
|
||||
export const ASSUMED_BANDWIDTH_BPS = -1;
|
||||
|
||||
9
modules/API/external/external_api.js
vendored
9
modules/API/external/external_api.js
vendored
@@ -59,6 +59,7 @@ const commands = {
|
||||
sendEndpointTextMessage: 'send-endpoint-text-message',
|
||||
sendParticipantToRoom: 'send-participant-to-room',
|
||||
sendTones: 'send-tones',
|
||||
setAssumedBandwidthBps: 'set-assumed-bandwidth-bps',
|
||||
setFollowMe: 'set-follow-me',
|
||||
setLargeVideoParticipant: 'set-large-video-participant',
|
||||
setMediaEncryptionKey: 'set-media-encryption-key',
|
||||
@@ -393,14 +394,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
this._frame.name = frameName;
|
||||
this._frame.id = frameName;
|
||||
this._setSize(height, width);
|
||||
this._frame.sandbox = [
|
||||
'allow-scripts',
|
||||
'allow-same-origin',
|
||||
'allow-popups',
|
||||
'allow-forms',
|
||||
'allow-downloads',
|
||||
'allow-top-navigation-by-user-activation'
|
||||
].join(' ');
|
||||
this._frame.setAttribute('allowFullScreen', 'true');
|
||||
this._frame.style.border = 0;
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import { setColorAlpha } from '../../react/features/base/util/helpers';
|
||||
import { setDocumentUrl } from '../../react/features/etherpad/actions';
|
||||
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
|
||||
import {
|
||||
joinLeaveNotificationsDisabled,
|
||||
setNotificationsEnabled,
|
||||
showNotification
|
||||
} from '../../react/features/notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../react/features/notifications/constants';
|
||||
import { joinLeaveNotificationsDisabled } from '../../react/features/notifications/functions';
|
||||
import {
|
||||
dockToolbox,
|
||||
setToolboxEnabled,
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
/* global APP */
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import {
|
||||
ACTION_SHORTCUT_PRESSED as PRESSED,
|
||||
ACTION_SHORTCUT_RELEASED as RELEASED,
|
||||
createShortcutEvent
|
||||
} from '../../react/features/analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../react/features/analytics/functions';
|
||||
import { clickOnVideo } from '../../react/features/filmstrip/actions';
|
||||
import { openSettingsDialog } from '../../react/features/settings/actions';
|
||||
import { SETTINGS_TABS } from '../../react/features/settings/constants';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Map of shortcuts. When a shortcut is registered it enters the mapping.
|
||||
* @type {Map}
|
||||
*/
|
||||
const _shortcuts = new Map();
|
||||
|
||||
/**
|
||||
* Map of registered keyboard keys and translation keys describing the
|
||||
* action performed by the key.
|
||||
* @type {Map}
|
||||
*/
|
||||
const _shortcutsHelp = new Map();
|
||||
|
||||
/**
|
||||
* The key used to save in local storage if keyboard shortcuts are enabled.
|
||||
*/
|
||||
const _enableShortcutsKey = 'enableShortcuts';
|
||||
|
||||
/**
|
||||
* Prefer keyboard handling of these elements over global shortcuts.
|
||||
* If a button is triggered using the Spacebar it should not trigger PTT.
|
||||
* If an input element is focused and M is pressed it should not mute audio.
|
||||
*/
|
||||
const _elementsBlacklist = [
|
||||
'input',
|
||||
'textarea',
|
||||
'button',
|
||||
'[role=button]',
|
||||
'[role=menuitem]',
|
||||
'[role=radio]',
|
||||
'[role=tab]',
|
||||
'[role=option]',
|
||||
'[role=switch]',
|
||||
'[role=range]',
|
||||
'[role=log]'
|
||||
];
|
||||
|
||||
/**
|
||||
* An element selector for elements that have their own keyboard handling.
|
||||
*/
|
||||
const _focusedElementsSelector = `:focus:is(${_elementsBlacklist.join(',')})`;
|
||||
|
||||
/**
|
||||
* Maps keycode to character, id of popover for given function and function.
|
||||
*/
|
||||
const KeyboardShortcut = {
|
||||
|
||||
init() {
|
||||
this._initGlobalShortcuts();
|
||||
|
||||
window.onkeyup = e => {
|
||||
if (!this.getEnabled()) {
|
||||
return;
|
||||
}
|
||||
const key = this._getKeyboardKey(e).toUpperCase();
|
||||
const num = parseInt(key, 10);
|
||||
|
||||
if (!document.querySelector(_focusedElementsSelector)) {
|
||||
if (_shortcuts.has(key)) {
|
||||
_shortcuts.get(key).function(e);
|
||||
} else if (!isNaN(num) && num >= 0 && num <= 9) {
|
||||
APP.store.dispatch(clickOnVideo(num));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onkeydown = e => {
|
||||
if (!this.getEnabled()) {
|
||||
return;
|
||||
}
|
||||
const focusedElement = document.querySelector(_focusedElementsSelector);
|
||||
|
||||
if (!focusedElement) {
|
||||
if (this._getKeyboardKey(e).toUpperCase() === ' ') {
|
||||
if (APP.conference.isLocalAudioMuted()) {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'push.to.talk',
|
||||
PRESSED));
|
||||
logger.log('Talk shortcut pressed');
|
||||
APP.conference.muteAudio(false);
|
||||
}
|
||||
}
|
||||
} else if (this._getKeyboardKey(e).toUpperCase() === 'ESCAPE') {
|
||||
// Allow to remove focus from selected elements using ESC key.
|
||||
if (focusedElement && focusedElement.blur) {
|
||||
focusedElement.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables/Disables the keyboard shortcuts.
|
||||
* @param {boolean} value - the new value.
|
||||
*/
|
||||
enable(value) {
|
||||
jitsiLocalStorage.setItem(_enableShortcutsKey, value);
|
||||
},
|
||||
|
||||
getEnabled() {
|
||||
// Should be enabled if not explicitly set to false
|
||||
// eslint-disable-next-line no-unneeded-ternary
|
||||
return jitsiLocalStorage.getItem(_enableShortcutsKey) === 'false' ? false : true;
|
||||
},
|
||||
|
||||
getShortcutsDescriptions() {
|
||||
return _shortcutsHelp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the {@SettingsDialog} dialog on the Shortcuts page.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
openDialog() {
|
||||
APP.store.dispatch(openSettingsDialog(SETTINGS_TABS.SHORTCUTS, false));
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a new shortcut.
|
||||
*
|
||||
* @param shortcutChar the shortcut character triggering the action
|
||||
* @param shortcutAttr the "shortcut" html element attribute mapping an
|
||||
* element to this shortcut and used to show the shortcut character on the
|
||||
* element tooltip
|
||||
* @param exec the function to be executed when the shortcut is pressed
|
||||
* @param helpDescription the description of the shortcut that would appear
|
||||
* in the help menu
|
||||
* @param altKey whether or not the alt key must be pressed.
|
||||
*/
|
||||
registerShortcut(// eslint-disable-line max-params
|
||||
shortcutChar,
|
||||
shortcutAttr,
|
||||
exec,
|
||||
helpDescription,
|
||||
altKey = false) {
|
||||
_shortcuts.set(altKey ? `:${shortcutChar}` : shortcutChar, {
|
||||
character: shortcutChar,
|
||||
function: exec,
|
||||
shortcutAttr,
|
||||
altKey
|
||||
});
|
||||
|
||||
if (helpDescription) {
|
||||
this._addShortcutToHelp(altKey ? `:${shortcutChar}` : shortcutChar, helpDescription);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregisters a shortcut.
|
||||
*
|
||||
* @param shortcutChar unregisters the given shortcut, which means it will
|
||||
* no longer be usable
|
||||
* @param altKey whether or not shortcut is combo with alt key
|
||||
*/
|
||||
unregisterShortcut(shortcutChar, altKey = false) {
|
||||
_shortcuts.delete(altKey ? `:${shortcutChar}` : shortcutChar);
|
||||
_shortcutsHelp.delete(shortcutChar);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param e a KeyboardEvent
|
||||
* @returns {string} e.key or something close if not supported
|
||||
*/
|
||||
_getKeyboardKey(e) {
|
||||
// If alt is pressed a different char can be returned so this takes
|
||||
// the char from the code. It also prefixes with a colon to differentiate
|
||||
// alt combo from simple keypress.
|
||||
if (e.altKey) {
|
||||
const key = e.code.replace('Key', '');
|
||||
|
||||
return `:${key}`;
|
||||
}
|
||||
|
||||
// If e.key is a string, then it is assumed it already plainly states
|
||||
// the key pressed. This may not be true in all cases, such as with Edge
|
||||
// and "?", when the browser cannot properly map a key press event to a
|
||||
// keyboard key. To be safe, when a key is "Unidentified" it must be
|
||||
// further analyzed by jitsi to a key using e.which.
|
||||
if (typeof e.key === 'string' && e.key !== 'Unidentified') {
|
||||
return e.key;
|
||||
}
|
||||
if (e.type === 'keypress'
|
||||
&& ((e.which >= 32 && e.which <= 126)
|
||||
|| (e.which >= 160 && e.which <= 255))) {
|
||||
return String.fromCharCode(e.which);
|
||||
}
|
||||
|
||||
// try to fallback (0-9A-Za-z and QWERTY keyboard)
|
||||
switch (e.which) {
|
||||
case 27:
|
||||
return 'Escape';
|
||||
case 191:
|
||||
return e.shiftKey ? '?' : '/';
|
||||
}
|
||||
if (e.shiftKey || e.type === 'keypress') {
|
||||
return String.fromCharCode(e.which);
|
||||
}
|
||||
|
||||
return String.fromCharCode(e.which).toLowerCase();
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given shortcut to the help dialog.
|
||||
*
|
||||
* @param shortcutChar the shortcut character
|
||||
* @param shortcutDescriptionKey the description of the shortcut
|
||||
* @private
|
||||
*/
|
||||
_addShortcutToHelp(shortcutChar, shortcutDescriptionKey) {
|
||||
_shortcutsHelp.set(shortcutChar, shortcutDescriptionKey);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise global shortcuts.
|
||||
* Global shortcuts are shortcuts for features that don't have a button or
|
||||
* link associated with the action. In other words they represent actions
|
||||
* triggered _only_ with a shortcut.
|
||||
*/
|
||||
_initGlobalShortcuts() {
|
||||
this.registerShortcut('?', null, () => {
|
||||
sendAnalytics(createShortcutEvent('help'));
|
||||
this.openDialog();
|
||||
}, 'keyboardShortcuts.toggleShortcuts');
|
||||
|
||||
// register SPACE shortcut in two steps to insure visibility of help
|
||||
// message
|
||||
this.registerShortcut(' ', null, () => {
|
||||
sendAnalytics(createShortcutEvent('push.to.talk', RELEASED));
|
||||
logger.log('Talk shortcut released');
|
||||
APP.conference.muteAudio(true);
|
||||
});
|
||||
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
|
||||
|
||||
/**
|
||||
* FIXME: Currently focus keys are directly implemented below in
|
||||
* onkeyup. They should be moved to the SmallVideo instead.
|
||||
*/
|
||||
this._addShortcutToHelp('0', 'keyboardShortcuts.focusLocal');
|
||||
this._addShortcutToHelp('1-9', 'keyboardShortcuts.focusRemote');
|
||||
}
|
||||
};
|
||||
|
||||
export default KeyboardShortcut;
|
||||
3095
package-lock.json
generated
3095
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@@ -16,14 +16,10 @@
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"@amplitude/react-native": "2.7.0",
|
||||
"@atlaskit/inline-dialog": "13.0.9",
|
||||
"@atlaskit/inline-message": "11.0.8",
|
||||
"@atlaskit/multi-select": "15.0.5",
|
||||
"@atlaskit/theme": "11.0.2",
|
||||
"@emotion/react": "11.10.0",
|
||||
"@emotion/styled": "11.10.0",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
"@giphy/js-fetch-api": "4.7.1",
|
||||
"@giphy/react-components": "5.6.0",
|
||||
"@giphy/react-components": "6.8.1",
|
||||
"@giphy/react-native-sdk": "1.7.0",
|
||||
"@hapi/bourne": "2.0.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.12/jitsi-excalidraw-0.0.12.tgz",
|
||||
@@ -33,13 +29,13 @@
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
"@mui/material": "5.10.2",
|
||||
"@mui/styles": "5.10.2",
|
||||
"@mui/material": "5.12.1",
|
||||
"@mui/styles": "5.12.0",
|
||||
"@react-native-async-storage/async-storage": "1.17.3",
|
||||
"@react-native-community/clipboard": "1.5.1",
|
||||
"@react-native-community/netinfo": "7.1.7",
|
||||
"@react-native-community/slider": "4.1.12",
|
||||
"@react-native-google-signin/google-signin": "7.0.4",
|
||||
"@react-native-google-signin/google-signin": "9.0.2",
|
||||
"@react-navigation/bottom-tabs": "6.5.3",
|
||||
"@react-navigation/elements": "1.3.13",
|
||||
"@react-navigation/material-top-tabs": "6.5.2",
|
||||
@@ -48,13 +44,9 @@
|
||||
"@svgr/webpack": "6.3.1",
|
||||
"@tensorflow/tfjs-backend-wasm": "3.13.0",
|
||||
"@tensorflow/tfjs-core": "3.13.0",
|
||||
"@types/amplitude-js": "8.16.2",
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/w3c-image-capture": "1.0.6",
|
||||
"@types/w3c-web-hid": "1.0.3",
|
||||
"@vladmandic/human": "2.6.5",
|
||||
"@vladmandic/human-models": "2.5.9",
|
||||
"@xmldom/xmldom": "0.7.9",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"amplitude-js": "8.2.1",
|
||||
"base64-js": "1.3.1",
|
||||
"bc-css-flags": "3.0.0",
|
||||
@@ -73,7 +65,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/v1612.0.0+67d92a96/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -112,7 +104,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.7",
|
||||
"react-native-webrtc": "111.0.0",
|
||||
"react-native-webview": "11.15.1",
|
||||
"react-native-youtube-iframe": "2.2.1",
|
||||
"react-redux": "7.1.0",
|
||||
@@ -123,7 +115,6 @@
|
||||
"redux-thunk": "2.4.1",
|
||||
"resemblejs": "4.0.0",
|
||||
"seamless-scroll-polyfill": "2.1.8",
|
||||
"styled-components": "3.4.9",
|
||||
"tss-react": "4.4.4",
|
||||
"util": "0.12.1",
|
||||
"uuid": "8.3.2",
|
||||
@@ -140,17 +131,23 @@
|
||||
"@babel/preset-flow": "7.16.0",
|
||||
"@babel/preset-react": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.1.5",
|
||||
"@types/amplitude-js": "8.16.2",
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/lodash": "4.14.182",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react-linkify": "1.0.1",
|
||||
"@types/react-native": "0.68.9",
|
||||
"@types/react-native-video": "5.0.14",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/react-window": "1.8.5",
|
||||
"@types/resemblejs": "^4.1.0",
|
||||
"@types/unorm": "1.3.28",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/w3c-image-capture": "1.0.6",
|
||||
"@types/w3c-web-hid": "1.0.3",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||
"@typescript-eslint/parser": "5.30.4",
|
||||
@@ -176,11 +173,16 @@
|
||||
"ts-loader": "9.4.1",
|
||||
"typescript": "4.7.4",
|
||||
"unorm": "1.6.0",
|
||||
"webpack": "5.57.1",
|
||||
"webpack": "5.76.0",
|
||||
"webpack-bundle-analyzer": "4.4.2",
|
||||
"webpack-cli": "4.9.0",
|
||||
"webpack-dev-server": "4.7.3"
|
||||
},
|
||||
"overrides": {
|
||||
"strophe.js@1.6.0": {
|
||||
"@xmldom/xmldom": "0.8.7"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
@@ -197,6 +199,8 @@
|
||||
"lint-fix": "eslint --ext .js,.ts,.tsx --max-warnings 0 --fix .",
|
||||
"postinstall": "patch-package --error-on-fail && jetify",
|
||||
"validate": "npm ls",
|
||||
"tsc-test:web": "tsc --project tsconfig.web.json --listFilesOnly | grep -v node_modules | grep native",
|
||||
"tsc-test:native": "tsc --project tsconfig.native.json --listFilesOnly | grep -v node_modules | grep web",
|
||||
"start": "make dev"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
diff --git a/node_modules/@giphy/js-analytics/dist/send-pingback.js b/node_modules/@giphy/js-analytics/dist/send-pingback.js
|
||||
index 001345a..f303443 100644
|
||||
index 989f0ff..149e77c 100644
|
||||
--- a/node_modules/@giphy/js-analytics/dist/send-pingback.js
|
||||
+++ b/node_modules/@giphy/js-analytics/dist/send-pingback.js
|
||||
@@ -10,6 +10,9 @@ var global_1 = __importDefault(require("./global"));
|
||||
var environment = (global_1.default === null || global_1.default === void 0 ? void 0 : global_1.default.GIPHY_PINGBACK_URL) || 'https://pingback.giphy.com';
|
||||
var pingBackUrl = environment + "/v2/pingback?apikey=l0HlIwPWyBBUDAUgM";
|
||||
var pingBackUrl = "".concat(environment, "/v2/pingback?apikey=l0HlIwPWyBBUDAUgM");
|
||||
var sendPingback = function (events) {
|
||||
+ // Disabled.
|
||||
+ return Promise.resolve();
|
||||
+
|
||||
var headers = js_util_1.getGiphySDKRequestHeaders();
|
||||
var headers = (0, js_util_1.getGiphySDKRequestHeaders)();
|
||||
/* istanbul ignore next */
|
||||
headers === null || headers === void 0 ? void 0 : headers.set('Content-Type', 'application/json');
|
||||
@@ -1,12 +0,0 @@
|
||||
diff --git a/node_modules/@giphy/js-brand/dist/typography.js b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
index af796bc..585fa00 100644
|
||||
--- a/node_modules/@giphy/js-brand/dist/typography.js
|
||||
+++ b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.css = exports.fontSize = exports.fontFamily = void 0;
|
||||
var emotion_1 = require("emotion");
|
||||
// eslint-disable-next-line
|
||||
-emotion_1.injectGlobal(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"], ["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"])));
|
||||
exports.fontFamily = {
|
||||
title: "'nexablack', sans-serif",
|
||||
body: 'interface, Helvetica Neue, helvetica, sans-serif;',
|
||||
13
patches/@giphy+js-brand+2.2.2.patch
Normal file
13
patches/@giphy+js-brand+2.2.2.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/node_modules/@giphy/js-brand/dist/typography.js b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
index 75ad96b..815cb8b 100644
|
||||
--- a/node_modules/@giphy/js-brand/dist/typography.js
|
||||
+++ b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
@@ -6,7 +6,7 @@ var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cook
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.css = exports.fontSize = exports.fontFamily = exports.addFonts = void 0;
|
||||
var emotion_1 = require("emotion");
|
||||
-var addFonts = function () { return (0, emotion_1.injectGlobal)(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"], ["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"]))); };
|
||||
+var addFonts = function () { };
|
||||
exports.addFonts = addFonts;
|
||||
try {
|
||||
// in an env where process.env exists,
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that local media duration has changed.
|
||||
*
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface IProps {
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
export class AbstractApp extends BaseApp<IProps> {
|
||||
export class AbstractApp<P extends IProps = IProps> extends BaseApp<P> {
|
||||
/**
|
||||
* The deferred for the initialisation {{promise, resolve, reject}}.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { ComponentType } from 'react';
|
||||
import { NativeModules, Platform, StyleSheet, View } from 'react-native';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import SplashScreen from 'react-native-splash-screen';
|
||||
@@ -14,14 +14,13 @@ import { updateSettings } from '../../base/settings/actions';
|
||||
import { _getRouteToRender } from '../getRouteToRender.native';
|
||||
import logger from '../logger';
|
||||
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
import type { Props as AbstractAppProps } from './AbstractApp';
|
||||
import { AbstractApp, IProps as AbstractAppProps } from './AbstractApp';
|
||||
|
||||
// Register middlewares and reducers.
|
||||
import '../middlewares';
|
||||
import '../reducers';
|
||||
import '../middlewares.native';
|
||||
import '../reducers.native';
|
||||
|
||||
declare var __DEV__;
|
||||
declare let __DEV__: any;
|
||||
|
||||
const { AppInfo } = NativeModules;
|
||||
|
||||
@@ -32,37 +31,33 @@ const DialogContainerWrapper = Platform.select({
|
||||
/**
|
||||
* The type of React {@code Component} props of {@link App}.
|
||||
*/
|
||||
type Props = AbstractAppProps & {
|
||||
interface IProps extends AbstractAppProps {
|
||||
|
||||
/**
|
||||
* An object with the feature flags.
|
||||
*/
|
||||
flags: Object,
|
||||
flags: Object;
|
||||
|
||||
/**
|
||||
* An object with user information (display name, email, avatar URL).
|
||||
*/
|
||||
userInfo: ?Object
|
||||
};
|
||||
userInfo?: Object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Root app {@code Component} on mobile/React Native.
|
||||
*
|
||||
* @augments AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
/**
|
||||
* The deferred for the initialisation {{promise, resolve, reject}}.
|
||||
*/
|
||||
_init: Object;
|
||||
export class App extends AbstractApp<IProps> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code App} instance.
|
||||
*
|
||||
* @param {Props} props - The read-only React {@code Component} props with
|
||||
* @param {IProps} props - The read-only React {@code Component} props with
|
||||
* which the new instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// In the Release configuration, React Native will (intentionally) throw
|
||||
@@ -99,10 +94,11 @@ export class App extends AbstractApp {
|
||||
* @returns {void}
|
||||
*/
|
||||
async _extraInit() {
|
||||
const { dispatch, getState } = this.state.store;
|
||||
const { dispatch, getState } = this.state.store ?? {};
|
||||
const { flags } = this.props;
|
||||
|
||||
// We set these early enough so then we avoid any unnecessary re-renders.
|
||||
dispatch(updateFlags(this.props.flags));
|
||||
dispatch?.(updateFlags(flags));
|
||||
|
||||
const route = await _getRouteToRender();
|
||||
|
||||
@@ -113,8 +109,9 @@ export class App extends AbstractApp {
|
||||
// Wait until the root navigator is ready.
|
||||
// We really need to break the inheritance relationship between App,
|
||||
// AbstractApp and BaseApp, it's very inflexible and cumbersome right now.
|
||||
const rootNavigationReady = new Promise(resolve => {
|
||||
const rootNavigationReady = new Promise<void>(resolve => {
|
||||
const i = setInterval(() => {
|
||||
// @ts-ignore
|
||||
const { ready } = getState()['features/app'] || {};
|
||||
|
||||
if (ready) {
|
||||
@@ -127,26 +124,27 @@ export class App extends AbstractApp {
|
||||
await rootNavigationReady;
|
||||
|
||||
// Check if serverURL is configured externally and not allowed to change.
|
||||
const serverURLChangeEnabled = getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true);
|
||||
const serverURLChangeEnabled = getState && getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true);
|
||||
|
||||
if (!serverURLChangeEnabled) {
|
||||
// As serverURL is provided externally, so we push it to settings.
|
||||
if (typeof this.props.url !== 'undefined') {
|
||||
// @ts-ignore
|
||||
const { serverURL } = this.props.url;
|
||||
|
||||
if (typeof serverURL !== 'undefined') {
|
||||
dispatch(updateSettings({ serverURL }));
|
||||
dispatch?.(updateSettings({ serverURL }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(updateSettings(this.props.userInfo || {}));
|
||||
dispatch?.(updateSettings(this.props.userInfo || {}));
|
||||
|
||||
// Update settings with feature-flag.
|
||||
const callIntegrationEnabled = this.props.flags[CALL_INTEGRATION_ENABLED];
|
||||
const callIntegrationEnabled = flags[CALL_INTEGRATION_ENABLED as keyof typeof flags];
|
||||
|
||||
if (typeof callIntegrationEnabled !== 'undefined') {
|
||||
dispatch(updateSettings({ disableCallIntegration: !callIntegrationEnabled }));
|
||||
dispatch?.(updateSettings({ disableCallIntegration: !callIntegrationEnabled }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +154,7 @@ export class App extends AbstractApp {
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
_createMainElement(component, props) {
|
||||
_createMainElement(component: ComponentType<any>, props: Object) {
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<DimensionsDetector
|
||||
@@ -196,17 +194,19 @@ export class App extends AbstractApp {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const oldHandler = global.ErrorUtils.getGlobalHandler();
|
||||
const newHandler = _handleException;
|
||||
|
||||
if (!oldHandler || oldHandler !== newHandler) {
|
||||
// @ts-ignore
|
||||
newHandler.next = oldHandler;
|
||||
|
||||
// @ts-ignore
|
||||
global.ErrorUtils.setGlobalHandler(newHandler);
|
||||
}
|
||||
}
|
||||
|
||||
_onDimensionsChanged: (width: number, height: number) => void;
|
||||
|
||||
/**
|
||||
* Updates the known available size for the app to occupy.
|
||||
*
|
||||
@@ -216,9 +216,9 @@ export class App extends AbstractApp {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDimensionsChanged(width: number, height: number) {
|
||||
const { dispatch } = this.state.store;
|
||||
const { dispatch } = this.state.store ?? {};
|
||||
|
||||
dispatch(clientResized(width, height));
|
||||
dispatch?.(clientResized(width, height));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,10 +232,10 @@ export class App extends AbstractApp {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSafeAreaInsetsChanged(insets) {
|
||||
const { dispatch } = this.state.store;
|
||||
_onSafeAreaInsetsChanged(insets: Object) {
|
||||
const { dispatch } = this.state.store ?? {};
|
||||
|
||||
dispatch(setSafeAreaInsets(insets));
|
||||
dispatch?.(setSafeAreaInsets(insets));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,7 +266,7 @@ export class App extends AbstractApp {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _handleException(error, fatal) {
|
||||
function _handleException(error: Error, fatal: boolean) {
|
||||
if (fatal) {
|
||||
// In the Release configuration, React Native will (intentionally) throw
|
||||
// an unhandled JavascriptException for an unhandled JavaScript error.
|
||||
@@ -275,6 +275,7 @@ function _handleException(error, fatal) {
|
||||
logger.error(error);
|
||||
} else {
|
||||
// Forward to the next globalHandler of ErrorUtils.
|
||||
// @ts-ignore
|
||||
const { next } = _handleException;
|
||||
|
||||
typeof next === 'function' && next(error, fatal);
|
||||
@@ -1,5 +1,4 @@
|
||||
import { AtlasKitThemeProvider } from '@atlaskit/theme';
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import GlobalStyles from '../../base/ui/components/GlobalStyles.web';
|
||||
import JitsiThemeProvider from '../../base/ui/components/JitsiThemeProvider.web';
|
||||
@@ -31,9 +30,9 @@ export class App extends AbstractApp {
|
||||
*/
|
||||
_createExtraElement() {
|
||||
return (
|
||||
<Fragment>
|
||||
<JitsiThemeProvider>
|
||||
<OverlayContainer />
|
||||
</Fragment>
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,14 +42,12 @@ export class App extends AbstractApp {
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
_createMainElement(component, props) {
|
||||
_createMainElement(component: React.ComponentType, props: any) {
|
||||
return (
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<GlobalStyles />
|
||||
<ChromeExtensionBanner />
|
||||
{ super._createMainElement(component, props) }
|
||||
</AtlasKitThemeProvider>
|
||||
<GlobalStyles />
|
||||
<ChromeExtensionBanner />
|
||||
{ super._createMainElement(component, props) }
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -63,9 +60,7 @@ export class App extends AbstractApp {
|
||||
_renderDialogContainer() {
|
||||
return (
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
<DialogContainer />
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -10,8 +10,9 @@ const route = {
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
* store.
|
||||
*
|
||||
* @param {any} _stateful - Used on web.
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function _getRouteToRender() {
|
||||
export function _getRouteToRender(_stateful?: any) {
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
|
||||
// @ts-expect-error
|
||||
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
|
||||
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { isRoomValid } from '../base/conference/functions';
|
||||
import { isSupportedBrowser } from '../base/environment/environment';
|
||||
import { toState } from '../base/redux/functions';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import Conference from '../conference/components/web/Conference';
|
||||
import { getDeepLinkingPage } from '../deep-linking/functions';
|
||||
import UnsupportedDesktopBrowser from '../unsupported-browser/components/UnsupportedDesktopBrowser';
|
||||
import BlankPage from '../welcome/components/BlankPage';
|
||||
import WelcomePage from '../welcome/components/WelcomePage';
|
||||
import BlankPage from '../welcome/components/BlankPage.web';
|
||||
import WelcomePage from '../welcome/components/WelcomePage.web';
|
||||
import { getCustomLandingPageURL, isWelcomePageEnabled } from '../welcome/functions';
|
||||
|
||||
import { IReduxState } from './types';
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
* store.
|
||||
@@ -19,7 +24,7 @@ import { getCustomLandingPageURL, isWelcomePageEnabled } from '../welcome/functi
|
||||
* {@code getState} function.
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function _getRouteToRender(stateful) {
|
||||
export function _getRouteToRender(stateful: IStateful) {
|
||||
const state = toState(stateful);
|
||||
|
||||
return _getWebConferenceRoute(state) || _getWebWelcomePageRoute(state);
|
||||
@@ -32,7 +37,7 @@ export function _getRouteToRender(stateful) {
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Promise|undefined}
|
||||
*/
|
||||
function _getWebConferenceRoute(state) {
|
||||
function _getWebConferenceRoute(state: IReduxState) {
|
||||
if (!isRoomValid(state['features/base/conference'].room)) {
|
||||
return;
|
||||
}
|
||||
@@ -45,8 +50,8 @@ function _getWebConferenceRoute(state) {
|
||||
// room into account.
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
|
||||
if (window.location.href !== locationURL.href) {
|
||||
route.href = locationURL.href;
|
||||
if (window.location.href !== locationURL?.href) {
|
||||
route.href = locationURL?.href;
|
||||
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
@@ -71,7 +76,7 @@ function _getWebConferenceRoute(state) {
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
function _getWebWelcomePageRoute(state) {
|
||||
function _getWebWelcomePageRoute(state: IReduxState) {
|
||||
const route = _getEmptyRoute();
|
||||
|
||||
if (isWelcomePageEnabled(state)) {
|
||||
@@ -102,7 +107,10 @@ function _getWebWelcomePageRoute(state) {
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _getEmptyRoute() {
|
||||
function _getEmptyRoute(): {
|
||||
component: React.ReactNode;
|
||||
href?: string;
|
||||
} {
|
||||
return {
|
||||
component: BlankPage,
|
||||
href: undefined
|
||||
@@ -1,4 +1,4 @@
|
||||
// @flow
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import { createConnectionEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
@@ -10,6 +10,7 @@ import { inIframe } from '../base/util/iframeUtils';
|
||||
|
||||
import { reloadNow } from './actions';
|
||||
import { _getRouteToRender } from './getRouteToRender';
|
||||
import { IStore } from './types';
|
||||
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
@@ -39,7 +40,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
* specified {@code action}.
|
||||
*/
|
||||
function _connectionEstablished(store, next, action) {
|
||||
function _connectionEstablished(store: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
|
||||
// In the Web app we explicitly do not want to display the hash and
|
||||
@@ -47,6 +48,7 @@ function _connectionEstablished(store, next, action) {
|
||||
// importantly, its params are used not only in jitsi-meet but also in
|
||||
// lib-jitsi-meet. Consequently, the time to remove the params is
|
||||
// determined by when no one needs them anymore.
|
||||
// @ts-ignore
|
||||
const { history, location } = window;
|
||||
|
||||
if (inIframe()) {
|
||||
@@ -57,12 +59,14 @@ function _connectionEstablished(store, next, action) {
|
||||
&& location
|
||||
&& history.length
|
||||
&& typeof history.replaceState === 'function') {
|
||||
// @ts-ignore
|
||||
const replacement = getURLWithoutParams(location);
|
||||
|
||||
// @ts-ignore
|
||||
if (location !== replacement) {
|
||||
history.replaceState(
|
||||
history.state,
|
||||
(document && document.title) || '',
|
||||
document?.title || '',
|
||||
replacement);
|
||||
}
|
||||
}
|
||||
@@ -81,7 +85,7 @@ function _connectionEstablished(store, next, action) {
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
function _connectionFailed({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
// In the case of a split-brain error, reload early and prevent further
|
||||
// handling of the action.
|
||||
if (_isMaybeSplitBrainError(getState, action)) {
|
||||
@@ -104,7 +108,7 @@ function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function _isMaybeSplitBrainError(getState, action) {
|
||||
function _isMaybeSplitBrainError(getState: IStore['getState'], action: AnyAction) {
|
||||
const { error } = action;
|
||||
const isShardChangedError = error
|
||||
&& error.message === 'item-not-found'
|
||||
@@ -116,7 +120,7 @@ function _isMaybeSplitBrainError(getState, action) {
|
||||
const { timeEstablished } = state['features/base/connection'];
|
||||
const { _immediateReloadThreshold } = state['features/base/config'];
|
||||
|
||||
const timeSinceConnectionEstablished = timeEstablished && Date.now() - timeEstablished;
|
||||
const timeSinceConnectionEstablished = Number(timeEstablished && Date.now() - timeEstablished);
|
||||
const reloadThreshold = typeof _immediateReloadThreshold === 'number' ? _immediateReloadThreshold : 1500;
|
||||
|
||||
const isWithinSplitBrainThreshold = !timeEstablished || timeSinceConnectionEstablished <= reloadThreshold;
|
||||
@@ -142,7 +146,7 @@ function _isMaybeSplitBrainError(getState, action) {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _navigate({ getState }) {
|
||||
function _navigate({ getState }: IStore) {
|
||||
const state = getState();
|
||||
const { app } = state['features/base/app'];
|
||||
|
||||
@@ -163,7 +167,7 @@ function _navigate({ getState }) {
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
* specified {@code action}.
|
||||
*/
|
||||
function _setRoom(store, next, action) {
|
||||
function _setRoom(store: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
|
||||
_navigate(store);
|
||||
@@ -5,6 +5,7 @@ import '../base/media/middleware';
|
||||
import '../dynamic-branding/middleware';
|
||||
import '../e2ee/middleware';
|
||||
import '../external-api/middleware';
|
||||
import '../keyboard-shortcuts/middleware';
|
||||
import '../no-audio-signal/middleware';
|
||||
import '../notifications/middleware';
|
||||
import '../noise-detection/middleware';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { _ROOT_NAVIGATION_READY } from '../mobile/navigation/actionTypes';
|
||||
* @param {string} action.type - Type of action.
|
||||
* @returns {Object}
|
||||
*/
|
||||
ReducerRegistry.register('features/app', (state = {}, action) => {
|
||||
ReducerRegistry.register('features/app', (state: Object = {}, action) => {
|
||||
switch (action.type) {
|
||||
case _ROOT_NAVIGATION_READY:
|
||||
return {
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import '../analytics/reducer';
|
||||
import '../authentication/reducer';
|
||||
import '../av-moderation/reducer';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import '../mobile/audio-mode/reducer';
|
||||
import '../mobile/background/reducer';
|
||||
import '../mobile/call-integration/reducer';
|
||||
|
||||
@@ -3,6 +3,7 @@ import '../base/tooltip/reducer';
|
||||
import '../e2ee/reducer';
|
||||
import '../face-landmarks/reducer';
|
||||
import '../feedback/reducer';
|
||||
import '../keyboard-shortcuts/reducer';
|
||||
import '../no-audio-signal/reducer';
|
||||
import '../noise-detection/reducer';
|
||||
import '../participants-pane/reducer';
|
||||
|
||||
@@ -43,6 +43,7 @@ import { IGifsState } from '../gifs/reducer';
|
||||
import { IGoogleApiState } from '../google-api/reducer';
|
||||
import { IInviteState } from '../invite/reducer';
|
||||
import { IJaaSState } from '../jaas/reducer';
|
||||
import { IKeyboardShortcutsState } from '../keyboard-shortcuts/types';
|
||||
import { ILargeVideoState } from '../large-video/reducer';
|
||||
import { ILobbyState } from '../lobby/reducer';
|
||||
import { IMobileAudioModeState } from '../mobile/audio-mode/reducer';
|
||||
@@ -133,6 +134,7 @@ export interface IReduxState {
|
||||
'features/google-api': IGoogleApiState;
|
||||
'features/invite': IInviteState;
|
||||
'features/jaas': IJaaSState;
|
||||
'features/keyboard-shortcuts': IKeyboardShortcutsState;
|
||||
'features/large-video': ILargeVideoState;
|
||||
'features/lobby': ILobbyState;
|
||||
'features/mobile/audio-mode': IMobileAudioModeState;
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
UPGRADE_ROLE_FINISHED,
|
||||
UPGRADE_ROLE_STARTED, WAIT_FOR_OWNER
|
||||
} from './actionTypes';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { LoginDialog, WaitForOwnerDialog } from './components';
|
||||
import logger from './logger';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @ts-ignore
|
||||
export { default as LoginDialog } from './native/LoginDialog';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from 'react-native-dialog';
|
||||
import { connect as reduxConnect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { IJitsiConference } from '../../../base/conference/reducer';
|
||||
import { connect } from '../../../base/connection/actions.native';
|
||||
import { toJid } from '../../../base/connection/functions';
|
||||
import { _abstractMapStateToProps } from '../../../base/dialog/functions';
|
||||
@@ -13,61 +14,68 @@ import { authenticateAndUpgradeRole, cancelLogin } from '../../actions.native';
|
||||
/**
|
||||
* The type of the React {@link Component} props of {@link LoginDialog}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* {@link JitsiConference} That needs authentication - will hold a valid
|
||||
* value in XMPP login + guest access mode.
|
||||
*/
|
||||
_conference: Object,
|
||||
_conference?: IJitsiConference;
|
||||
|
||||
/**
|
||||
* The server hosts specified in the global config.
|
||||
*/
|
||||
_configHosts: Object,
|
||||
_configHosts?: {
|
||||
anonymousdomain?: string;
|
||||
authdomain?: string;
|
||||
domain: string;
|
||||
focus?: string;
|
||||
muc: string;
|
||||
visitorFocus: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if the dialog should display "connecting" status message.
|
||||
*/
|
||||
_connecting: boolean,
|
||||
_connecting: boolean;
|
||||
|
||||
/**
|
||||
* The error which occurred during login/authentication.
|
||||
*/
|
||||
_error: Object,
|
||||
_error: any;
|
||||
|
||||
/**
|
||||
* The progress in the floating range between 0 and 1 of the authenticating
|
||||
* and upgrading the role of the local participant/user.
|
||||
*/
|
||||
_progress: number,
|
||||
_progress?: number;
|
||||
|
||||
/**
|
||||
* Redux store dispatch method.
|
||||
*/
|
||||
dispatch: Dispatch<any>,
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
t: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@link Component} state of {@link LoginDialog}.
|
||||
*/
|
||||
type State = {
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* The user entered password for the conference.
|
||||
*/
|
||||
password: string,
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* The user entered local participant name.
|
||||
*/
|
||||
username: string
|
||||
};
|
||||
username: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog asks user for username and password.
|
||||
@@ -96,14 +104,14 @@ type State = {
|
||||
* See {@link https://github.com/jitsi/jicofo#secure-domain} for a description
|
||||
* of the configuration parameters.
|
||||
*/
|
||||
class LoginDialog extends Component<Props, State> {
|
||||
class LoginDialog extends Component<IProps, IState> {
|
||||
/**
|
||||
* Initializes a new LoginDialog instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -179,7 +187,7 @@ class LoginDialog extends Component<Props, State> {
|
||||
} = this.props;
|
||||
|
||||
let messageKey;
|
||||
const messageOptions = {};
|
||||
const messageOptions = { msg: '' };
|
||||
|
||||
if (progress && progress < 1) {
|
||||
messageKey = 'connection.FETCH_SESSION_ID';
|
||||
@@ -196,7 +204,7 @@ class LoginDialog extends Component<Props, State> {
|
||||
&& credentials.jid
|
||||
=== toJid(
|
||||
this.state.username,
|
||||
this.props._configHosts)
|
||||
this.props._configHosts ?? {})
|
||||
&& credentials.password === this.state.password) {
|
||||
messageKey = 'dialog.incorrectPassword';
|
||||
}
|
||||
@@ -222,7 +230,7 @@ class LoginDialog extends Component<Props, State> {
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
_onUsernameChange(text) {
|
||||
_onUsernameChange(text: string) {
|
||||
this.setState({
|
||||
username: text.trim()
|
||||
});
|
||||
@@ -235,7 +243,7 @@ class LoginDialog extends Component<Props, State> {
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
_onPasswordChange(text) {
|
||||
_onPasswordChange(text: string) {
|
||||
this.setState({
|
||||
password: text
|
||||
});
|
||||
@@ -261,7 +269,7 @@ class LoginDialog extends Component<Props, State> {
|
||||
_onLogin() {
|
||||
const { _conference: conference, dispatch } = this.props;
|
||||
const { password, username } = this.state;
|
||||
const jid = toJid(username, this.props._configHosts);
|
||||
const jid = toJid(username, this.props._configHosts ?? {});
|
||||
let r;
|
||||
|
||||
// If there's a conference it means that the connection has succeeded,
|
||||
@@ -282,9 +290,9 @@ class LoginDialog extends Component<Props, State> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const {
|
||||
error: authenticateAndUpgradeRoleError,
|
||||
progress,
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { IStore } from '../../../app/types';
|
||||
import ConfirmDialog from '../../../base/dialog/components/native/ConfirmDialog';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { cancelWaitForOwner, openLoginDialog } from '../../actions.native';
|
||||
@@ -9,18 +9,18 @@ import { cancelWaitForOwner, openLoginDialog } from '../../actions.native';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link WaitForOwnerDialog}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Redux store dispatch function.
|
||||
*/
|
||||
dispatch: Dispatch<any>,
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
t: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* The dialog is display in XMPP password + guest access configuration, after
|
||||
@@ -28,14 +28,14 @@ type Props = {
|
||||
*
|
||||
* See {@link LoginDialog} description for more details.
|
||||
*/
|
||||
class WaitForOwnerDialog extends Component<Props> {
|
||||
class WaitForOwnerDialog extends Component<IProps> {
|
||||
/**
|
||||
* Initializes a new WaitForWonderDialog instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -186,7 +186,7 @@ export default class BaseApp<P> extends Component<P, IState> {
|
||||
* @abstract
|
||||
* @protected
|
||||
*/
|
||||
_createExtraElement() {
|
||||
_createExtraElement(): React.ReactElement | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ export default class BaseApp<P> extends Component<P, IState> {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_navigate(route: {
|
||||
component?: ComponentType;
|
||||
component?: ComponentType<any>;
|
||||
href?: string;
|
||||
props?: Object;
|
||||
}): Promise<any> {
|
||||
@@ -280,5 +280,7 @@ export default class BaseApp<P> extends Component<P, IState> {
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderDialogContainer: () => React.ReactElement;
|
||||
_renderDialogContainer(): React.ReactElement | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which sets the audio-only flag for the current
|
||||
* conference.
|
||||
|
||||
@@ -71,7 +71,7 @@ export interface IProps {
|
||||
/**
|
||||
* The size of the avatar.
|
||||
*/
|
||||
size: number;
|
||||
size?: number;
|
||||
|
||||
/**
|
||||
* One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
|
||||
@@ -185,6 +185,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
|
||||
id?: string;
|
||||
status?: string;
|
||||
testId?: string;
|
||||
url?: string;
|
||||
useCORS?: boolean;
|
||||
} = {
|
||||
className,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Image, Text, View } from 'react-native';
|
||||
import { Image, Text, TextStyle, View, ViewStyle } from 'react-native';
|
||||
|
||||
import Icon from '../../../icons/components/Icon';
|
||||
import { StyleType } from '../../../styles/functions.native';
|
||||
import { isIcon } from '../../functions';
|
||||
import { IAvatarProps } from '../../types';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
@@ -23,6 +22,11 @@ interface IProps extends IAvatarProps {
|
||||
* External style passed to the component.
|
||||
*/
|
||||
style?: StyleType;
|
||||
|
||||
/**
|
||||
* The URL of the avatar to render.
|
||||
*/
|
||||
url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +70,7 @@ export default class StatelessAvatar extends Component<IProps> {
|
||||
<View>
|
||||
<View
|
||||
style = { [
|
||||
styles.avatarContainer(size),
|
||||
styles.avatarContainer(size) as ViewStyle,
|
||||
style
|
||||
] }>
|
||||
{ avatar }
|
||||
@@ -90,7 +94,7 @@ export default class StatelessAvatar extends Component<IProps> {
|
||||
|
||||
return (
|
||||
<View style = { styles.badgeContainer }>
|
||||
<View style = { styles.badge(size, status) } />
|
||||
<View style = { styles.badge(size, status) as ViewStyle } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -125,7 +129,7 @@ export default class StatelessAvatar extends Component<IProps> {
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
styles.initialsContainer,
|
||||
styles.initialsContainer as ViewStyle,
|
||||
{
|
||||
backgroundColor: color
|
||||
}
|
||||
@@ -148,12 +152,12 @@ export default class StatelessAvatar extends Component<IProps> {
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
styles.initialsContainer,
|
||||
styles.initialsContainer as ViewStyle,
|
||||
{
|
||||
backgroundColor: color
|
||||
}
|
||||
] }>
|
||||
<Text style = { styles.initialsText(size) }> { initials } </Text>
|
||||
<Text style = { styles.initialsText(size) as TextStyle }> { initials } </Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -166,9 +170,11 @@ export default class StatelessAvatar extends Component<IProps> {
|
||||
_renderURLAvatar() {
|
||||
const { onAvatarLoadError, size, url } = this.props;
|
||||
|
||||
return ( // @ts-ignore
|
||||
return (
|
||||
<Image
|
||||
defaultSource = { DEFAULT_AVATAR }
|
||||
|
||||
// @ts-ignore
|
||||
onError = { onAvatarLoadError }
|
||||
resizeMode = 'cover'
|
||||
source = {{ uri: url }}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { ColorPalette } from '../../../styles/components/styles/ColorPalette';
|
||||
@@ -35,6 +35,11 @@ interface IProps extends IAvatarProps {
|
||||
*/
|
||||
testId?: string;
|
||||
|
||||
/**
|
||||
* The URL of the avatar to render.
|
||||
*/
|
||||
url?: string | Function;
|
||||
|
||||
/**
|
||||
* Indicates whether to load the avatar using CORS or not.
|
||||
*/
|
||||
|
||||
@@ -24,9 +24,4 @@ export interface IAvatarProps {
|
||||
* Expected size of the avatar.
|
||||
*/
|
||||
size?: number;
|
||||
|
||||
/**
|
||||
* The URL of the avatar to render.
|
||||
*/
|
||||
url?: string | Function;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class ColorSchemeRegistry {
|
||||
* @param {StyleType} style - The style definition to register.
|
||||
* @returns {void}
|
||||
*/
|
||||
register(componentName: string, style: StyleType): void {
|
||||
register(componentName: string, style: any): void {
|
||||
this._styleTemplates.set(componentName, style);
|
||||
|
||||
// If this is a style overwrite, we need to delete the processed version
|
||||
@@ -120,7 +120,6 @@ class ColorSchemeRegistry {
|
||||
} else if (typeof styleValue === 'function') {
|
||||
// The value is a function, which indicates that it's a
|
||||
// dynamic, schemed color we need to resolve.
|
||||
// $FlowExpectedError
|
||||
const value = styleValue();
|
||||
|
||||
schemedStyle[styleName]
|
||||
|
||||
@@ -3,6 +3,7 @@ import { sendAnalytics } from '../../analytics/functions';
|
||||
import { appNavigate } from '../../app/actions';
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { endpointMessageReceived } from '../../subtitles/actions.any';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { getReplaceParticipant } from '../config/functions';
|
||||
import { disconnect } from '../connection/actions';
|
||||
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
|
||||
@@ -450,11 +451,12 @@ export function conferenceUniqueIdSet(conference: IJitsiConference) {
|
||||
*/
|
||||
export function _conferenceWillJoin(conference: IJitsiConference) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
const localTracks
|
||||
= getLocalTracks(getState()['features/base/tracks'])
|
||||
= getLocalTracks(state['features/base/tracks'])
|
||||
.map(t => t.jitsiTrack);
|
||||
|
||||
if (localTracks.length) {
|
||||
if (localTracks.length && !iAmVisitor(state)) {
|
||||
_addLocalTracksToConference(conference, localTracks);
|
||||
}
|
||||
|
||||
@@ -530,8 +532,6 @@ export function createConference(overrideRoom?: string | String) {
|
||||
if (tmp.domain) {
|
||||
// eslint-disable-next-line no-new-wrappers
|
||||
_room = new String(tmp);
|
||||
|
||||
// $FlowExpectedError
|
||||
_room.domain = tmp.domain;
|
||||
}
|
||||
|
||||
@@ -808,7 +808,7 @@ export function setStartReactionsMuted(muted: boolean, updateBackend = false) {
|
||||
export function setPassword(
|
||||
conference: IJitsiConference | undefined,
|
||||
method: Function | undefined,
|
||||
password: string) {
|
||||
password?: string) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
if (!conference) {
|
||||
return;
|
||||
|
||||
@@ -39,3 +39,8 @@ export const CONFERENCE_LEAVE_REASONS = {
|
||||
SWITCH_ROOM: 'switch_room',
|
||||
UNRECOVERABLE_ERROR: 'unrecoverable_error'
|
||||
};
|
||||
|
||||
/**
|
||||
* Timeout for properly leaving the conference if it was destroyed.
|
||||
*/
|
||||
export const CONFERENCE_DESTROYED_LEAVE_TIMEOUT = 10000;
|
||||
|
||||
@@ -159,7 +159,6 @@ export function forEachConference(
|
||||
// Does the value of the base/conference's property look like a
|
||||
// JitsiConference?
|
||||
if (v && typeof v === 'object') {
|
||||
// $FlowFixMe
|
||||
const url: URL = v[JITSI_CONFERENCE_URL_KEY];
|
||||
|
||||
// XXX The Web version of Jitsi Meet does not utilize
|
||||
@@ -309,6 +308,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
muc: config.oldConfig.hosts.muc
|
||||
},
|
||||
focusUserJid: focusJid,
|
||||
disableLocalStats: false,
|
||||
bosh: config.oldConfig.bosh && appendURLParam(config.oldConfig.bosh, 'customusername', username),
|
||||
websocket: config.oldConfig.websocket
|
||||
&& appendURLParam(config.oldConfig.websocket, 'customusername', username),
|
||||
@@ -339,6 +339,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
},
|
||||
focusUserJid: focusJid,
|
||||
disableFocus: true, // This flag disables sending the initial conference request
|
||||
disableLocalStats: true,
|
||||
bosh: config.bosh && appendURLParam(config.bosh, 'vnode', vnode),
|
||||
websocket: config.websocket && appendURLParam(config.websocket, 'vnode', vnode)
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import { readyToClose } from '../../../features/mobile/external-api/actions';
|
||||
import {
|
||||
ACTION_PINNED,
|
||||
ACTION_UNPINNED,
|
||||
@@ -9,11 +8,14 @@ import {
|
||||
} from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { reloadNow } from '../../app/actions';
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { removeLobbyChatParticipant } from '../../chat/actions.any';
|
||||
import { openDisplayNamePrompt } from '../../display-name/actions';
|
||||
import { readyToClose } from '../../mobile/external-api/actions';
|
||||
import { showErrorNotification, showWarningNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
import { setIAmVisitor } from '../../visitors/actions';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { overwriteConfig } from '../config/actions';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection/actionTypes';
|
||||
import { connect, connectionDisconnected, disconnect } from '../connection/actions';
|
||||
@@ -43,10 +45,15 @@ import {
|
||||
conferenceFailed,
|
||||
conferenceWillLeave,
|
||||
createConference,
|
||||
leaveConference,
|
||||
setLocalSubject,
|
||||
setSubject
|
||||
} from './actions';
|
||||
import { CONFERENCE_LEAVE_REASONS, TRIGGER_READY_TO_CLOSE_REASONS } from './constants';
|
||||
import {
|
||||
CONFERENCE_DESTROYED_LEAVE_TIMEOUT,
|
||||
CONFERENCE_LEAVE_REASONS,
|
||||
TRIGGER_READY_TO_CLOSE_REASONS
|
||||
} from './constants';
|
||||
import {
|
||||
_addLocalTracksToConference,
|
||||
_removeLocalTracksFromConference,
|
||||
@@ -57,12 +64,10 @@ import {
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Handler for before unload event.
|
||||
*/
|
||||
let beforeUnloadHandler;
|
||||
let beforeUnloadHandler: Function | undefined;
|
||||
|
||||
/**
|
||||
* Implements the middleware of the feature base/conference.
|
||||
@@ -124,7 +129,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
function _conferenceFailed({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { conference, error } = action;
|
||||
|
||||
if (error.name === JitsiConferenceErrors.REDIRECTED) {
|
||||
@@ -152,6 +157,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
} else {
|
||||
APP.API.notifyReadyToClose();
|
||||
}
|
||||
setTimeout(() => dispatch(leaveConference()), CONFERENCE_DESTROYED_LEAVE_TIMEOUT);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -185,7 +191,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
const newConfig = restoreConferenceOptions(getState);
|
||||
|
||||
if (newConfig) {
|
||||
dispatch(overwriteConfig(newConfig))
|
||||
dispatch(overwriteConfig(newConfig)) // @ts-ignore
|
||||
.then(dispatch(conferenceWillLeave(conference)))
|
||||
.then(conference.leave())
|
||||
.then(dispatch(disconnect()))
|
||||
@@ -211,7 +217,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
|
||||
const [ vnode ] = error.params;
|
||||
|
||||
dispatch(overwriteConfig(newConfig))
|
||||
dispatch(overwriteConfig(newConfig)) // @ts-ignore
|
||||
.then(dispatch(conferenceWillLeave(conference)))
|
||||
.then(conference.leave())
|
||||
.then(dispatch(disconnect()))
|
||||
@@ -228,7 +234,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
if (typeof APP === 'undefined') {
|
||||
!error.recoverable
|
||||
&& conference
|
||||
&& conference.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch(reason => {
|
||||
&& conference.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch((reason: Error) => {
|
||||
// Even though we don't care too much about the failure, it may be
|
||||
// good to know that it happen, so log it (on the info level).
|
||||
logger.info('JitsiConference.leave() rejected with:', reason);
|
||||
@@ -261,7 +267,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
function _conferenceJoined({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
const { conference } = action;
|
||||
const { pendingSubjectChange } = getState()['features/base/conference'];
|
||||
@@ -282,6 +288,8 @@ function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
beforeUnloadHandler = () => {
|
||||
dispatch(conferenceWillLeave(conference));
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
window.addEventListener(disableBeforeUnloadHandlers ? 'unload' : 'beforeunload', beforeUnloadHandler);
|
||||
|
||||
if (requireDisplayName
|
||||
@@ -307,7 +315,7 @@ function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _connectionEstablished({ dispatch }, next, action) {
|
||||
function _connectionEstablished({ dispatch }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
|
||||
// FIXME: Workaround for the web version. Currently, the creation of the
|
||||
@@ -324,7 +332,7 @@ function _connectionEstablished({ dispatch }, next, action) {
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _logJwtErrors(message, state) {
|
||||
function _logJwtErrors(message: string, state: IReduxState) {
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
if (!jwt) {
|
||||
@@ -351,7 +359,7 @@ function _logJwtErrors(message, state) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
function _connectionFailed({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
_logJwtErrors(action.error.message, getState());
|
||||
|
||||
const result = next(action);
|
||||
@@ -411,7 +419,7 @@ function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceSubjectChanged({ dispatch, getState }, next, action) {
|
||||
function _conferenceSubjectChanged({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
const { subject } = getState()['features/base/conference'];
|
||||
|
||||
@@ -436,7 +444,7 @@ function _conferenceSubjectChanged({ dispatch, getState }, next, action) {
|
||||
* @param {Object} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _conferenceWillLeave({ getState }: { getState: Function }) {
|
||||
function _conferenceWillLeave({ getState }: IStore) {
|
||||
_removeUnloadHandler(getState);
|
||||
}
|
||||
|
||||
@@ -454,7 +462,7 @@ function _conferenceWillLeave({ getState }: { getState: Function }) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _pinParticipant({ getState }, next, action) {
|
||||
function _pinParticipant({ getState }: IStore, next: Function, action: AnyAction) {
|
||||
const state = getState();
|
||||
const { conference } = state['features/base/conference'];
|
||||
|
||||
@@ -467,7 +475,7 @@ function _pinParticipant({ getState }, next, action) {
|
||||
const pinnedParticipant = getPinnedParticipant(state);
|
||||
const actionName = id ? ACTION_PINNED : ACTION_UNPINNED;
|
||||
const local
|
||||
= (participantById && participantById.local)
|
||||
= participantById?.local
|
||||
|| (!id && pinnedParticipant && pinnedParticipant.local);
|
||||
let participantIdForEvent;
|
||||
|
||||
@@ -475,7 +483,7 @@ function _pinParticipant({ getState }, next, action) {
|
||||
participantIdForEvent = local;
|
||||
} else {
|
||||
participantIdForEvent
|
||||
= actionName === ACTION_PINNED ? id : pinnedParticipant && pinnedParticipant.id;
|
||||
= actionName === ACTION_PINNED ? id : pinnedParticipant?.id;
|
||||
}
|
||||
|
||||
sendAnalytics(createPinnedEvent(
|
||||
@@ -495,10 +503,11 @@ function _pinParticipant({ getState }, next, action) {
|
||||
* @param {Function} getState - The redux getState function.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _removeUnloadHandler(getState) {
|
||||
function _removeUnloadHandler(getState: IStore['getState']) {
|
||||
if (typeof beforeUnloadHandler !== 'undefined') {
|
||||
const { disableBeforeUnloadHandlers = false } = getState()['features/base/config'];
|
||||
|
||||
// @ts-ignore
|
||||
window.removeEventListener(disableBeforeUnloadHandlers ? 'unload' : 'beforeunload', beforeUnloadHandler);
|
||||
beforeUnloadHandler = undefined;
|
||||
}
|
||||
@@ -516,7 +525,7 @@ function _removeUnloadHandler(getState) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _sendTones({ getState }, next, action) {
|
||||
function _sendTones({ getState }: IStore, next: Function, action: AnyAction) {
|
||||
const state = getState();
|
||||
const { conference } = state['features/base/conference'];
|
||||
|
||||
@@ -543,15 +552,15 @@ function _sendTones({ getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _setRoom({ dispatch, getState }, next, action) {
|
||||
function _setRoom({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const state = getState();
|
||||
const { localSubject, subject } = state['features/base/config'];
|
||||
const { room } = action;
|
||||
|
||||
if (room) {
|
||||
// Set the stored subject.
|
||||
dispatch(setLocalSubject(localSubject));
|
||||
dispatch(setSubject(subject));
|
||||
dispatch(setLocalSubject(localSubject ?? ''));
|
||||
dispatch(setSubject(subject ?? ''));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
@@ -566,15 +575,20 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _syncConferenceLocalTracksWithState({ getState }, action) {
|
||||
const conference = getCurrentConference(getState);
|
||||
function _syncConferenceLocalTracksWithState({ getState }: IStore, action: AnyAction) {
|
||||
const state = getState();
|
||||
const conference = getCurrentConference(state);
|
||||
let promise;
|
||||
|
||||
if (conference) {
|
||||
const track = action.track.jitsiTrack;
|
||||
|
||||
if (action.type === TRACK_ADDED) {
|
||||
promise = _addLocalTracksToConference(conference, [ track ]);
|
||||
// If gUM is slow and tracks are created after the user has already joined the conference, avoid
|
||||
// adding the tracks to the conference if the user is a visitor.
|
||||
if (!iAmVisitor(state)) {
|
||||
promise = _addLocalTracksToConference(conference, [ track ]);
|
||||
}
|
||||
} else {
|
||||
promise = _removeLocalTracksFromConference(conference, [ track ]);
|
||||
}
|
||||
@@ -597,7 +611,7 @@ function _syncConferenceLocalTracksWithState({ getState }, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _trackAddedOrRemoved(store, next, action) {
|
||||
function _trackAddedOrRemoved(store: IStore, next: Function, action: AnyAction) {
|
||||
const track = action.track;
|
||||
|
||||
// TODO All track swapping should happen here instead of conference.js.
|
||||
@@ -622,7 +636,7 @@ function _trackAddedOrRemoved(store, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _updateLocalParticipantInConference({ dispatch, getState }, next, action) {
|
||||
function _updateLocalParticipantInConference({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { conference } = getState()['features/base/conference'];
|
||||
const { participant } = action;
|
||||
const result = next(action);
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
setPrejoinPageVisibility,
|
||||
setSkipPrejoinOnReload
|
||||
@@ -60,6 +60,7 @@ export interface IJitsiConference {
|
||||
getMetadataHandler: Function;
|
||||
getName: Function;
|
||||
getParticipantById: Function;
|
||||
getParticipantCount: Function;
|
||||
getParticipants: Function;
|
||||
getRole: Function;
|
||||
getSpeakerStats: () => ISpeakerStats;
|
||||
@@ -101,7 +102,9 @@ export interface IJitsiConference {
|
||||
sendMessage: Function;
|
||||
sendPrivateTextMessage: Function;
|
||||
sendTextMessage: Function;
|
||||
sendTones: Function;
|
||||
sessionId: string;
|
||||
setAssumedBandwidthBps: (value: number) => void;
|
||||
setDesktopSharingFrameRate: Function;
|
||||
setDisplayName: Function;
|
||||
setLocalParticipantProperty: Function;
|
||||
|
||||
@@ -112,8 +112,8 @@ export interface IDeeplinkingMobileConfig extends IDeeplinkingPlatformConfig {
|
||||
export interface IDeeplinkingConfig {
|
||||
android?: IDeeplinkingMobileConfig;
|
||||
desktop?: IDeeplinkingPlatformConfig;
|
||||
disabled: boolean;
|
||||
hideLogo: boolean;
|
||||
disabled?: boolean;
|
||||
hideLogo?: boolean;
|
||||
ios?: IDeeplinkingMobileConfig;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,8 @@ export interface INoiseSuppressionConfig {
|
||||
|
||||
export interface IConfig {
|
||||
_desktopSharingSourceDevice?: string;
|
||||
_screenshotHistoryRegionUrl?: string;
|
||||
_immediateReloadThreshold?: string;
|
||||
_screenshotHistoryRegionUrl?: number;
|
||||
analytics?: {
|
||||
amplitudeAPPKey?: string;
|
||||
blackListedEvents?: string[];
|
||||
@@ -252,6 +253,7 @@ export interface IConfig {
|
||||
disableDeepLinking?: boolean;
|
||||
disableFilmstripAutohiding?: boolean;
|
||||
disableFocus?: boolean;
|
||||
disableIframeAPI?: boolean;
|
||||
disableIncomingMessageSound?: boolean;
|
||||
disableInitialGUM?: boolean;
|
||||
disableInviteFunctions?: boolean;
|
||||
|
||||
@@ -7,6 +7,7 @@ import _ from 'lodash';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { browser } from '../lib-jitsi-meet';
|
||||
import { IMediaState } from '../media/reducer';
|
||||
import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
import { IConfig } from './configType';
|
||||
@@ -65,7 +66,7 @@ export function getMeetingRegion(state: IReduxState) {
|
||||
* @param {Object} _state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getMultipleVideoSendingSupportFeatureFlag(_state: IReduxState) {
|
||||
export function getMultipleVideoSendingSupportFeatureFlag(_state: IReduxState | IMediaState) {
|
||||
return browser.supportsUnifiedPlan();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IReduxState } from '../../app/types';
|
||||
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
||||
|
||||
import { IConfig, IDeeplinkingConfig, IDeeplinkingMobileConfig, IDeeplinkingPlatformConfig } from './configType';
|
||||
import { TOOLBAR_BUTTONS } from './constants';
|
||||
@@ -75,8 +76,7 @@ export function isToolbarButtonEnabled(buttonName: string, state: IReduxState |
|
||||
* @returns {boolean}
|
||||
*/
|
||||
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;
|
||||
return !state['features/base/config'].disableAudioLevels && JitsiMeetJS.isCollectingLocalStats();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,25 +45,18 @@ const INITIAL_NON_RN_STATE: IConfig = {
|
||||
const INITIAL_RN_STATE: IConfig = {
|
||||
analytics: {},
|
||||
|
||||
// FIXME The support for audio levels in lib-jitsi-meet polls the statistics
|
||||
// of WebRTC at a short interval multiple times a second. Unfortunately,
|
||||
// React Native is slow to fetch these statistics from the native WebRTC
|
||||
// API, through the React Native bridge and eventually to JavaScript.
|
||||
// Because the audio levels are of no interest to the mobile app, it is
|
||||
// fastest to merely disable them.
|
||||
disableAudioLevels: true,
|
||||
|
||||
// FIXME: Mobile codecs should probably be configurable separately, rather
|
||||
// than requiring this override here...
|
||||
// FIXME: than requiring this override here...
|
||||
|
||||
// TODO: Remove comments later, after next release, so that the fix is applied
|
||||
p2p: {
|
||||
disabledCodec: 'vp9',
|
||||
preferredCodec: 'vp8'
|
||||
// disabledCodec: 'vp9',
|
||||
// preferredCodec: 'vp8'
|
||||
},
|
||||
|
||||
videoQuality: {
|
||||
disabledCodec: 'vp9',
|
||||
preferredCodec: 'vp8'
|
||||
// disabledCodec: 'vp9',
|
||||
// preferredCodec: 'vp8'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a connection disconnected.
|
||||
*
|
||||
|
||||
@@ -72,7 +72,7 @@ export type ConnectionFailedError = {
|
||||
* connection: JitsiConnection
|
||||
* }}
|
||||
*/
|
||||
export function connectionDisconnected(connection: Object) {
|
||||
export function connectionDisconnected(connection?: Object) {
|
||||
return {
|
||||
type: CONNECTION_DISCONNECTED,
|
||||
connection
|
||||
|
||||
@@ -89,6 +89,13 @@ export function isInviteURLReady(stateOrGetState: IStateful): boolean {
|
||||
* @returns {string} A string in the form of a JID (i.e.
|
||||
* {@code user@server.com}).
|
||||
*/
|
||||
export function toJid(id: string, { authdomain, domain }: { authdomain?: string; domain?: string; }): string {
|
||||
export function toJid(id: string, { authdomain, domain }: {
|
||||
anonymousdomain?: string;
|
||||
authdomain?: string;
|
||||
domain?: string;
|
||||
focus?: string;
|
||||
muc?: string;
|
||||
visitorFocus?: string;
|
||||
}): string {
|
||||
return id.indexOf('@') >= 0 ? id : `${id}@${authdomain || domain}`;
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ interface IProps {
|
||||
/**
|
||||
* The component to render.
|
||||
*/
|
||||
_component: ComponentType;
|
||||
_component?: ComponentType<any>;
|
||||
|
||||
/**
|
||||
* The props to pass to the component that will be rendered.
|
||||
*/
|
||||
_componentProps: Object;
|
||||
_componentProps?: Object;
|
||||
|
||||
/**
|
||||
* Array of reactions to be displayed.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { Text, TextStyle } from 'react-native';
|
||||
|
||||
import { brandedDialog as styles } from './native/styles';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { brandedDialog as styles } from './native/styles';
|
||||
* contain HTML to render.
|
||||
* @returns {ReactElement[]|string}
|
||||
*/
|
||||
export function renderHTML(html) {
|
||||
export function renderHTML(html?: string) {
|
||||
if (typeof html === 'string') {
|
||||
// At the time of this writing, the specified HTML contains a couple
|
||||
// of spaces one after the other. They do not cause a visible
|
||||
@@ -36,7 +36,7 @@ export function renderHTML(html) {
|
||||
if (c = closing.exec(html)) {
|
||||
r.push(html.substring(prevClosingLastIndex, o.index));
|
||||
r.push(
|
||||
<Text style = { styles.boldDialogText }>
|
||||
<Text style = { (styles.boldDialogText as TextStyle) }>
|
||||
{ html.substring(opening.lastIndex, c.index) }
|
||||
</Text>);
|
||||
opening.lastIndex
|
||||
@@ -25,7 +25,7 @@ export interface IState {
|
||||
/**
|
||||
* An abstract implementation of a dialog on Web/React and mobile/react-native.
|
||||
*/
|
||||
export default class AbstractDialog<P extends IProps, S extends IState>
|
||||
export default class AbstractDialog<P extends IProps, S extends IState = IState>
|
||||
extends Component<P, S> {
|
||||
|
||||
_mounted: boolean;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import Dialog from 'react-native-dialog';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -6,10 +7,9 @@ import { translate } from '../../../i18n/functions';
|
||||
import { _abstractMapStateToProps } from '../../functions';
|
||||
import { renderHTML } from '../functions.native';
|
||||
|
||||
import AbstractDialog, { type Props as AbstractProps } from './AbstractDialog';
|
||||
import AbstractDialog, { IProps as AbstractProps } from './AbstractDialog';
|
||||
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps, WithTranslation {
|
||||
|
||||
/**
|
||||
* Untranslated i18n key of the content to be displayed.
|
||||
@@ -18,19 +18,14 @@ type Props = AbstractProps & {
|
||||
* translated using the provided params. See i18n function
|
||||
* {@code translate(string, Object)} for more details.
|
||||
*/
|
||||
contentKey: string | { key: string, params: Object},
|
||||
|
||||
/**
|
||||
* Translation function.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
contentKey: string | { key: string; params: Object; };
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an alert dialog, to simply show an error or a message,
|
||||
* then disappear on dismiss.
|
||||
*/
|
||||
class AlertDialog extends AbstractDialog<Props> {
|
||||
class AlertDialog extends AbstractDialog<IProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render}.
|
||||
*
|
||||
@@ -56,8 +51,6 @@ class AlertDialog extends AbstractDialog<Props> {
|
||||
</Dialog.Container>
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(_abstractMapStateToProps)(AlertDialog));
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { type Node, PureComponent } from 'react';
|
||||
import { SafeAreaView, ScrollView, View } from 'react-native';
|
||||
import React, { PureComponent, ReactNode } from 'react';
|
||||
import { SafeAreaView, ScrollView, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import SlidingView from '../../../react/components/native/SlidingView';
|
||||
@@ -15,43 +15,43 @@ type Props = {
|
||||
/**
|
||||
* Whether to add padding to scroll view.
|
||||
*/
|
||||
addScrollViewPadding?: boolean,
|
||||
addScrollViewPadding?: boolean;
|
||||
|
||||
/**
|
||||
* The children to be displayed within this component.
|
||||
*/
|
||||
children: Node,
|
||||
children: ReactNode;
|
||||
|
||||
/**
|
||||
* Redux Dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* Handler for the cancel event, which happens when the user dismisses
|
||||
* the sheet.
|
||||
*/
|
||||
onCancel: ?Function,
|
||||
|
||||
/**
|
||||
* Function to render a bottom sheet header element, if necessary.
|
||||
*/
|
||||
renderHeader: ?Function,
|
||||
onCancel?: Function;
|
||||
|
||||
/**
|
||||
* Function to render a bottom sheet footer element, if necessary.
|
||||
*/
|
||||
renderFooter: ?Function,
|
||||
renderFooter?: Function;
|
||||
|
||||
/**
|
||||
* Function to render a bottom sheet header element, if necessary.
|
||||
*/
|
||||
renderHeader?: Function;
|
||||
|
||||
/**
|
||||
* Whether to show sliding view or not.
|
||||
*/
|
||||
showSlidingView?: boolean,
|
||||
showSlidingView?: boolean;
|
||||
|
||||
/**
|
||||
* The component's external style.
|
||||
*/
|
||||
style: Object
|
||||
style?: Object;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -109,19 +109,19 @@ class BottomSheet extends PureComponent<Props> {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<SlidingView
|
||||
<SlidingView // @ts-ignore
|
||||
accessibilityRole = 'menu'
|
||||
accessibilityViewIsModal = { true }
|
||||
onHide = { this._onCancel }
|
||||
position = 'bottom'
|
||||
show = { showSlidingView }>
|
||||
show = { Boolean(showSlidingView) }>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.sheetContainer }>
|
||||
style = { styles.sheetContainer as ViewStyle }>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.sheetAreaCover } />
|
||||
{ renderHeader && renderHeader() }
|
||||
{ renderHeader?.() }
|
||||
<SafeAreaView
|
||||
style = { [
|
||||
styles.sheetItemContainer,
|
||||
@@ -140,7 +140,7 @@ class BottomSheet extends PureComponent<Props> {
|
||||
] } >
|
||||
{ this.props.children }
|
||||
</ScrollView>
|
||||
{ renderFooter && renderFooter() }
|
||||
{ renderFooter?.() }
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</SlidingView>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user