mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-05 22:32:31 +00:00
Compare commits
76 Commits
8762
...
fix-webhoo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b8caf56dc | ||
|
|
039caa4d3b | ||
|
|
37252bdf2d | ||
|
|
85f52471b2 | ||
|
|
13b956dae2 | ||
|
|
77981d825b | ||
|
|
cc312877f4 | ||
|
|
eb8b6159ec | ||
|
|
f9d8feacd2 | ||
|
|
f5668b6e8b | ||
|
|
4219d9ad4d | ||
|
|
d68b9b1cad | ||
|
|
8f0b9575c4 | ||
|
|
f780207c22 | ||
|
|
ce19e6d40b | ||
|
|
b108db832f | ||
|
|
e4283e61dd | ||
|
|
50e2458124 | ||
|
|
329df31811 | ||
|
|
fce39be9d2 | ||
|
|
6c5a9ea199 | ||
|
|
196192c97f | ||
|
|
71f358c62a | ||
|
|
7aa7e76ccd | ||
|
|
cd77b6bbe4 | ||
|
|
e94df6799e | ||
|
|
2e92818b53 | ||
|
|
8a3129f7bf | ||
|
|
eb03642ea6 | ||
|
|
c436e48956 | ||
|
|
58db02bab8 | ||
|
|
abc1f3d33b | ||
|
|
b2166d9874 | ||
|
|
901a13a99a | ||
|
|
1e15d9421b | ||
|
|
9252bbb036 | ||
|
|
f1bae8bc10 | ||
|
|
5a54511d2c | ||
|
|
61764273b2 | ||
|
|
e39f38f75b | ||
|
|
2d25f48c72 | ||
|
|
2cb727fc58 | ||
|
|
c069c0d7c3 | ||
|
|
5de69d501d | ||
|
|
599c88a71d | ||
|
|
5476321df6 | ||
|
|
076b6a2a7e | ||
|
|
8b9df0cd37 | ||
|
|
44f5de3db4 | ||
|
|
fb69225d42 | ||
|
|
32df284277 | ||
|
|
253679cfb9 | ||
|
|
057c19f4dd | ||
|
|
6159a23c55 | ||
|
|
1685c39c5d | ||
|
|
2cecc61b97 | ||
|
|
df2262ae53 | ||
|
|
d61deab163 | ||
|
|
e7eab72c0c | ||
|
|
c1e803c6e3 | ||
|
|
dc1f20e059 | ||
|
|
61ee9af304 | ||
|
|
d75de3642e | ||
|
|
1ae1729545 | ||
|
|
8cea505417 | ||
|
|
b0a96b32d2 | ||
|
|
dac9b5e244 | ||
|
|
d15cfd845a | ||
|
|
91e4ac1665 | ||
|
|
fda42e5230 | ||
|
|
142d4441c1 | ||
|
|
5814c4dda7 | ||
|
|
8c1dc03363 | ||
|
|
ff6fc198f1 | ||
|
|
f1a0012fc1 | ||
|
|
85522aea25 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -152,12 +152,13 @@ jobs:
|
||||
xcodebuild clean \
|
||||
-workspace ios/jitsi-meet.xcworkspace \
|
||||
-scheme JitsiMeetSDK
|
||||
xcodebuild -downloadPlatform iOS -buildVersion 18.2
|
||||
xcodebuild archive \
|
||||
-workspace ios/jitsi-meet.xcworkspace \
|
||||
-scheme JitsiMeetSDK \
|
||||
-configuration Release \
|
||||
-sdk iphoneos \
|
||||
-destination='generic/platform=iOS' \
|
||||
-destination 'generic/platform=iOS' \
|
||||
-archivePath ios/sdk/out/ios-device \
|
||||
SKIP_INSTALL=NO \
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
|
||||
|
||||
@@ -46,7 +46,6 @@ dependencies {
|
||||
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'androidx.startup:startup-runtime:1.1.0'
|
||||
implementation 'com.google.j2objc:j2objc-annotations:3.0.0'
|
||||
@@ -87,6 +86,7 @@ dependencies {
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-webview')
|
||||
implementation project(':react-native-worklets-core')
|
||||
|
||||
// Use `api` here so consumers can use WebRTCModuleOptions.
|
||||
api project(':react-native-webrtc')
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-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.sdk;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.squareup.duktape.Duktape;
|
||||
|
||||
@ReactModule(name = JavaScriptSandboxModule.NAME)
|
||||
class JavaScriptSandboxModule extends ReactContextBaseJavaModule {
|
||||
public static final String NAME = "JavaScriptSandbox";
|
||||
|
||||
public JavaScriptSandboxModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given code in a Duktape VM.
|
||||
* @param code - The code that needs to evaluated.
|
||||
* @param promise - Resolved with the output in case of success or rejected with an exception
|
||||
* in case of failure.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void evaluate(String code, Promise promise) {
|
||||
Duktape vm = Duktape.create();
|
||||
try {
|
||||
Object res = vm.evaluate(code);
|
||||
promise.resolve(res.toString());
|
||||
} catch (Throwable tr) {
|
||||
promise.reject(tr);
|
||||
} finally {
|
||||
vm.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,15 @@ import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
@@ -87,6 +93,28 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
launch(context, options);
|
||||
}
|
||||
|
||||
public static void addTopBottomInsets(@NonNull Window w, @NonNull View v) {
|
||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) return;
|
||||
|
||||
View decorView = w.getDecorView();
|
||||
|
||||
decorView.post(() -> {
|
||||
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(decorView);
|
||||
if (insets != null) {
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
params.topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
||||
params.bottomMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
|
||||
v.setLayoutParams(params);
|
||||
|
||||
decorView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
|
||||
view.setBackgroundColor(JitsiMeetView.BACKGROUND_COLOR);
|
||||
|
||||
return windowInsets;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Overrides
|
||||
//
|
||||
|
||||
@@ -107,6 +135,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
JitsiMeetActivityDelegate.onHostResume(this);
|
||||
|
||||
setContentView(R.layout.activity_jitsi_meet);
|
||||
addTopBottomInsets(getWindow(),findViewById(android.R.id.content));
|
||||
this.jitsiView = findViewById(R.id.jitsiView);
|
||||
|
||||
registerForBroadcastMessages();
|
||||
|
||||
@@ -36,7 +36,7 @@ public class JitsiMeetView extends FrameLayout {
|
||||
/**
|
||||
* Background color. Should match the background color set in JS.
|
||||
*/
|
||||
private static final int BACKGROUND_COLOR = 0xFF040404;
|
||||
public static final int BACKGROUND_COLOR = 0xFF040404;
|
||||
|
||||
/**
|
||||
* React Native root view.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -65,7 +64,6 @@ class ReactInstanceManagerHolder {
|
||||
new AudioModeModule(reactContext),
|
||||
new DropboxModule(reactContext),
|
||||
new ExternalAPIModule(reactContext),
|
||||
new JavaScriptSandboxModule(reactContext),
|
||||
new LocaleDetector(reactContext),
|
||||
new LogBridgeModule(reactContext),
|
||||
new PictureInPictureModule(reactContext),
|
||||
@@ -110,6 +108,7 @@ class ReactInstanceManagerHolder {
|
||||
new com.horcrux.svg.SvgPackage(),
|
||||
new org.wonday.orientation.OrientationPackage(),
|
||||
new com.splashview.SplashViewPackage(),
|
||||
new com.worklets.WorkletsCorePackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
|
||||
@@ -52,3 +52,5 @@ include ':react-native-webrtc'
|
||||
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
include ':react-native-worklets-core'
|
||||
project(':react-native-worklets-core').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-worklets-core/android')
|
||||
|
||||
@@ -8,9 +8,13 @@ module.exports = {
|
||||
|
||||
// This happens because react native has conflict with @babel/plugin-transform-private-methods plugin
|
||||
// https://github.com/ethers-io/ethers.js/discussions/4309#discussioncomment-6694524
|
||||
plugins: [ 'optional-require',
|
||||
[ '@babel/plugin-transform-private-methods', {
|
||||
'loose': true
|
||||
} ]
|
||||
plugins: [
|
||||
'optional-require',
|
||||
[
|
||||
'@babel/plugin-transform-private-methods', {
|
||||
'loose': true
|
||||
}
|
||||
],
|
||||
'react-native-worklets-core/plugin'
|
||||
]
|
||||
};
|
||||
|
||||
@@ -134,6 +134,7 @@ import {
|
||||
isLocalTrackMuted,
|
||||
isUserInteractionRequiredForUnmute
|
||||
} from './react/features/base/tracks/functions';
|
||||
import { getLocalJitsiAudioTrackSettings } from './react/features/base/tracks/functions.web';
|
||||
import { downloadJSON } from './react/features/base/util/downloadJSON';
|
||||
import { getJitsiMeetGlobalNSConnectionTimes } from './react/features/base/util/helpers';
|
||||
import { openLeaveReasonDialog } from './react/features/conference/actions.web';
|
||||
@@ -158,13 +159,14 @@ import { disableReceiver, stopReceiver } from './react/features/remote-control/a
|
||||
import { setScreenAudioShareState } from './react/features/screen-share/actions.web';
|
||||
import { isScreenAudioShared } from './react/features/screen-share/functions';
|
||||
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture/actions';
|
||||
import { setAudioSettings } from './react/features/settings/actions.web';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
|
||||
import { transcriberJoined, transcriberLeft } from './react/features/transcribing/actions';
|
||||
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('app:conference-web');
|
||||
let room;
|
||||
|
||||
/*
|
||||
@@ -566,7 +568,15 @@ export default {
|
||||
if (browser.isWebKitBased()) {
|
||||
this.muteAudio(true, true);
|
||||
} else {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
localTracks = localTracks.filter(track => {
|
||||
if (track.getType() === MEDIA_TYPE.AUDIO) {
|
||||
track.stopStream();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1763,7 +1773,11 @@ export default {
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
const state = APP.store.getState();
|
||||
const localAudio = getLocalJitsiAudioTrack(state);
|
||||
const settings = getLocalJitsiAudioTrackSettings(state);
|
||||
|
||||
APP.store.dispatch(setAudioSettings(settings));
|
||||
|
||||
if (localAudio && isDefaultMicSelected) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
|
||||
@@ -363,6 +363,7 @@ var config = {
|
||||
// Desktop sharing
|
||||
|
||||
// Optional desktop sharing frame rate options. Default value: min:5, max:5.
|
||||
// Setting higher min/max values will affect the resolution, it makes it worse.
|
||||
// desktopSharingFrameRate: {
|
||||
// min: 5,
|
||||
// max: 5,
|
||||
@@ -791,7 +792,6 @@ var config = {
|
||||
// Configs for prejoin page.
|
||||
// prejoinConfig: {
|
||||
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
|
||||
// // This replaces `prejoinPageEnabled`. Defaults to true.
|
||||
// enabled: true,
|
||||
// // Hides the participant name editing field in the prejoin screen.
|
||||
// // If requireDisplayName is also set as true, a name should still be provided through
|
||||
|
||||
@@ -1497,6 +1497,27 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-worklets-core (1.6.2):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.11.18.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- React-nativeconfig (0.77.2)
|
||||
- React-NativeModulesApple (0.77.2):
|
||||
- glog
|
||||
@@ -1932,6 +1953,7 @@ DEPENDENCIES:
|
||||
- react-native-video (from `../node_modules/react-native-video`)
|
||||
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- react-native-worklets-core (from `../node_modules/react-native-worklets-core`)
|
||||
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
|
||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
@@ -2101,6 +2123,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-webrtc"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
react-native-worklets-core:
|
||||
:path: "../node_modules/react-native-worklets-core"
|
||||
React-nativeconfig:
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-NativeModulesApple:
|
||||
@@ -2257,6 +2281,7 @@ SPEC CHECKSUMS:
|
||||
react-native-video: eb861d67a71dfef1bbf6086a811af5f338b13781
|
||||
react-native-webrtc: 2261a482150195092246fe70b3aff976f2e11ec5
|
||||
react-native-webview: 079eca50edf657503318b66687dadfb903731aa8
|
||||
react-native-worklets-core: b59cf88762c8fb6132d8796babd4cec15217d6f0
|
||||
React-nativeconfig: ecf4dc92c40b97e2b3f0c619938f78bfd6507b08
|
||||
React-NativeModulesApple: f457bbfb30fb3bc41979b1a87b99d292d7340d39
|
||||
React-perflogger: 1111b5feb064c4cc83df88fb403efda54b387951
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
C81E9AB925AC5AD800B134D9 /* ExternalAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */; };
|
||||
C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C8AFD2802462C613000293D2 /* InfoPlistUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */; };
|
||||
DE438CDA2350934700DD541D /* JavaScriptSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DE438CD82350934700DD541D /* JavaScriptSandbox.m */; };
|
||||
DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AAC92317FFCD00290BEC /* LogUtils.h */; };
|
||||
DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */; };
|
||||
DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -98,7 +97,6 @@
|
||||
DE9A015C289A9A9A00E41CBB /* JitsiMeetLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = DE81A2D32316AC4D00AE1940 /* JitsiMeetLogger.m */; };
|
||||
DE9A015E289A9A9A00E41CBB /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; };
|
||||
DE9A015F289A9A9A00E41CBB /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
|
||||
DE9A0160289A9A9A00E41CBB /* JavaScriptSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DE438CD82350934700DD541D /* JavaScriptSandbox.m */; };
|
||||
DE9A0162289A9A9A00E41CBB /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BB9AD781F5EC6D7001C08DB /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
DE9A0163289A9A9A00E41CBB /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BB9AD761F5EC6CE001C08DB /* CallKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
DE9A0166289A9A9A00E41CBB /* CallKitIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 0BC4B8681F8C01E100CE8B21 /* CallKitIcon.png */; };
|
||||
@@ -161,7 +159,6 @@
|
||||
C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExternalAPI.h; sourceTree = "<group>"; };
|
||||
C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfoPlistUtil.h; sourceTree = "<group>"; };
|
||||
C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InfoPlistUtil.m; sourceTree = "<group>"; };
|
||||
DE438CD82350934700DD541D /* JavaScriptSandbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JavaScriptSandbox.m; sourceTree = "<group>"; };
|
||||
DE65AAC92317FFCD00290BEC /* LogUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogUtils.h; sourceTree = "<group>"; };
|
||||
DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetBaseLogHandler+Private.h"; sourceTree = "<group>"; };
|
||||
DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetUserInfo.h; sourceTree = "<group>"; };
|
||||
@@ -250,7 +247,6 @@
|
||||
C69EFA02209A0EFD0027712B /* callkit */,
|
||||
A4A934E7212F3AB8001E9388 /* dropbox */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
DE438CD82350934700DD541D /* JavaScriptSandbox.m */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */,
|
||||
DEA9F283258A5D9900D4CD74 /* JitsiMeetSDK.h */,
|
||||
@@ -681,7 +677,6 @@
|
||||
DE81A2D52316AC4D00AE1940 /* JitsiMeetLogger.m in Sources */,
|
||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
|
||||
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */,
|
||||
DE438CDA2350934700DD541D /* JavaScriptSandbox.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -714,7 +709,6 @@
|
||||
4E0EF63328CA2FB3005D1B03 /* JMCallKitEmitter.m in Sources */,
|
||||
DE9A015E289A9A9A00E41CBB /* JitsiMeetView.m in Sources */,
|
||||
DE9A015F289A9A9A00E41CBB /* JitsiMeet.m in Sources */,
|
||||
DE9A0160289A9A9A00E41CBB /* JavaScriptSandbox.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-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.
|
||||
*/
|
||||
|
||||
@import JavaScriptCore;
|
||||
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
|
||||
@interface JavaScriptSandbox : NSObject<RCTBridgeModule>
|
||||
@end
|
||||
|
||||
@implementation JavaScriptSandbox
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup {
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Exported methods
|
||||
|
||||
RCT_EXPORT_METHOD(evaluate:(NSString *)code
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
__block BOOL hasError = NO;
|
||||
JSContext *ctx = [[JSContext alloc] init];
|
||||
ctx.exceptionHandler = ^(JSContext *context, JSValue *exception) {
|
||||
hasError = YES;
|
||||
reject(@"evaluate", [exception toString], nil);
|
||||
};
|
||||
JSValue *ret = [ctx evaluateScript:code];
|
||||
if (!hasError) {
|
||||
NSString *result = [ret toString];
|
||||
if (result == nil) {
|
||||
reject(@"evaluate", @"Error in string coercion", nil);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -112,7 +112,9 @@
|
||||
"disabled": "L'invio di messaggi in chat è disabilitato.",
|
||||
"enter": "Entra nella conversazione",
|
||||
"error": "Errore: il tuo messaggio non è stato inviato. Motivo: {{error}}",
|
||||
"everyone": "Tutti",
|
||||
"fieldPlaceHolder": "Scrivi qui il tuo messaggio",
|
||||
"guestsChatIndicator": "(ospite)",
|
||||
"lobbyChatMessageTo": "Messaggio a {{recipient}} in sala d'attesa",
|
||||
"message": "Messaggio",
|
||||
"messageAccessibleTitle": "{{user}} dice:",
|
||||
@@ -154,7 +156,7 @@
|
||||
"installExtensionText": "Installa un'estensione per integrare Google Calendar e Office 365"
|
||||
},
|
||||
"closedCaptionsTab": {
|
||||
"emptyState": "Il contenuto dei sottotitoli sarà disponibile una volta che l'organizzatore lo attiva",
|
||||
"emptyState": "Il contenuto dei sottotitoli sarà disponibile una volta che l'organizzatore lo attiverà",
|
||||
"startClosedCaptionsButton": "Attiva sottotitoli"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
@@ -300,6 +302,12 @@
|
||||
"alreadySharedVideoTitle": "È permessa una sola condivisione video alla volta",
|
||||
"applicationWindow": "Finestra dell'applicazione",
|
||||
"authenticationRequired": "Richiesta autenticazione",
|
||||
"cameraCaptureDialog": {
|
||||
"description": "Scatta ed invia una foto usando la fotocamera del telefono",
|
||||
"ok": "Apri la fotocamera",
|
||||
"reject": "Non adesso",
|
||||
"title": "Scatta una foto"
|
||||
},
|
||||
"cameraConstraintFailedError": "La tua videocamera non soddisfa alcuni dei requisiti.",
|
||||
"cameraNotFoundError": "Videocamera non trovata.",
|
||||
"cameraNotSendingData": "Impossibile accedere alla videocamera. Controlla che non sia in uso in un'altra applicazione, seleziona un altro dispositivo dalle impostazioni o prova a ricaricare l'applicazione.",
|
||||
@@ -323,7 +331,7 @@
|
||||
"contactSupport": "Contatta il supporto",
|
||||
"copied": "Copiato",
|
||||
"copy": "Copia",
|
||||
"demoteParticipantDialog": "Sei sicuro di voler far diventare questo partecipante uno spettatore?",
|
||||
"demoteParticipantDialog": "Vuoi far diventare questo partecipante uno spettatore?",
|
||||
"demoteParticipantTitle": "Fai diventare spettatore",
|
||||
"dismiss": "Scarta",
|
||||
"displayNameRequired": "Ciao, qual è il tuo nome?",
|
||||
@@ -338,8 +346,8 @@
|
||||
"error": "Errore",
|
||||
"errorRoomCreationRestriction": "Hai provato ad accedere alla riunione troppo presto, torna tra un po'.",
|
||||
"gracefulShutdown": "Il nostro servizio è al momento inattivo per manutenzione. Si prega di riprovare più tardi.",
|
||||
"grantModeratorDialog": "Sei sicuro di voler rendere organizzatore questo partecipante?",
|
||||
"grantModeratorTitle": "Fai diventare organizzatore",
|
||||
"grantModeratorDialog": "Vuoi rendere relatore questo partecipante?",
|
||||
"grantModeratorTitle": "Fai diventare relatore",
|
||||
"hide": "Nascondi",
|
||||
"hideShareAudioHelper": "Non mostrare più questa finestra",
|
||||
"incorrectPassword": "Nome utente o password errati",
|
||||
@@ -348,7 +356,7 @@
|
||||
"internalErrorTitle": "Errore interno",
|
||||
"kickMessage": "Puoi contattare {{participantDisplayName}} per maggiori dettagli.",
|
||||
"kickParticipantButton": "Espelli",
|
||||
"kickParticipantDialog": "Sei sicuro di voler espellere questo partecipante?",
|
||||
"kickParticipantDialog": "Vuoi espellere questo partecipante?",
|
||||
"kickParticipantTitle": "Espellere questo partecipante?",
|
||||
"kickSystemTitle": "Oh! Sei stato espulso dalla riunione",
|
||||
"kickTitle": "Oh! {{participantDisplayName}} ti ha espulso dalla riunione.",
|
||||
@@ -362,8 +370,8 @@
|
||||
"lockRoom": "Aggiungi una $t(lockRoomPassword) alla riunione",
|
||||
"lockTitle": "Blocco fallito",
|
||||
"login": "Accesso",
|
||||
"loginQuestion": "Sei sicuro di voler fare l'accesso e abbandonare la riunione?",
|
||||
"logoutQuestion": "Sei sicuro di volerti disconnettere e abbandonare la riunione?",
|
||||
"loginQuestion": "Vuoi fare l'accesso e abbandonare la riunione?",
|
||||
"logoutQuestion": "Vuoi disconnetterti e abbandonare la riunione?",
|
||||
"logoutTitle": "Disconnessione",
|
||||
"maxUsersLimitReached": "È stato raggiunto il numero massimo di partecipanti. La riunione è al completo. Contatta l'organizzatore o riprova più tardi!",
|
||||
"maxUsersLimitReachedTitle": "Raggiunto limite massimo partecipanti",
|
||||
@@ -375,27 +383,39 @@
|
||||
"micTimeoutError": "Impossibile avviare la sorgente audio. Tempo di attesa scaduto.",
|
||||
"micUnknownError": "Impossibile usare il microfono per un motivo sconosciuto.",
|
||||
"moderationAudioLabel": "Consenti ai partecipanti di attivare il microfono",
|
||||
"moderationDesktopLabel": "Consenti ai partecipanti di condividere lo schermo",
|
||||
"moderationVideoLabel": "Consenti ai partecipanti di attivare la videocamera",
|
||||
"muteEveryoneDialog": "I partecipanti possono attivare il microfono in qualsiasi momento.",
|
||||
"muteEveryoneDialogModerationOn": "I partecipanti possono chiedere di parlare in qualsiasi momento.",
|
||||
"muteEveryoneElseDialog": "Una volta spenti i microfoni non potrai riattivarli, ma ogni partecipante potrà farlo da sé in qualsiasi momento.",
|
||||
"muteEveryoneElseTitle": "Spegnere il microfono a tutti, tranne che a {{whom}}?",
|
||||
"muteEveryoneElsesDesktopDialog": "Una volta interrotta la condivisione dello schermo non potrai riattivarla, ma ogni partecipante potrà farlo da sé in qualsiasi momento.",
|
||||
"muteEveryoneElsesDesktopTitle": "Interrompere la condivisione dello schermo a tutti, tranne che a {{whom}}?",
|
||||
"muteEveryoneElsesVideoDialog": "Una volta spente le videocamere non potrai riaccenderle, ma ogni partecipante potrà farlo da sé in qualsiasi momento.",
|
||||
"muteEveryoneElsesVideoTitle": "Spegnere la videocamera a tutti, tranne che a {{whom}}?",
|
||||
"muteEveryoneSelf": "tu",
|
||||
"muteEveryoneStartMuted": "Tutti iniziano a microfono spento da adesso in avanti",
|
||||
"muteEveryoneTitle": "Spegnere il microfono a tutti?",
|
||||
"muteEveryonesDesktopDialog": "I partecipanti potranno condividere lo schermo in qualsiasi momento.",
|
||||
"muteEveryonesDesktopDialogModerationOn": "I partecipanti possono inviare una richiesta per condividere lo schermo in ogni momento.",
|
||||
"muteEveryonesDesktopTitle": "Interrompere la condivisione dello schermo a tutti?",
|
||||
"muteEveryonesVideoDialog": "Ogni partecipante potrà riavviare il video da sé in qualsiasi momento.",
|
||||
"muteEveryonesVideoDialogModerationOn": "I partecipanti possono chiedere di attivare il video in qualsiasi momento.",
|
||||
"muteEveryonesVideoDialogOk": "Spegni",
|
||||
"muteEveryonesVideoTitle": "Spegnere la videocamera a tutti?",
|
||||
"muteParticipantBody": "Non potrai riattivare il loro microfono, ma loro potranno farlo in qualsiasi momento.",
|
||||
"muteParticipantButton": "Silenzia",
|
||||
"muteParticipantsDesktopBody": "Non potrai riavviare la loro condivisione dello schermo, ma loro potranno farlo in qualsiasi momento.",
|
||||
"muteParticipantsDesktopBodyModerationOn": "Non potrai riavviare la loro condivisione dello schermo e nemmeno loro potranno.",
|
||||
"muteParticipantsDesktopButton": "Interrompi la condivisione dello schermo",
|
||||
"muteParticipantsDesktopDialog": "Vuoi interrompere la condivisione dello schermo di questo partecipante? Non potrai riattivarla, ma lui potrà farlo in qualsiasi momento.",
|
||||
"muteParticipantsDesktopDialogModerationOn": "Vuoi interrompere la condivisione dello schermo di questo partecipante? Non potrai riattivarla e nemmeno loro potranno.",
|
||||
"muteParticipantsDesktopTitle": "Disattivare la condivisione dello schermo di questo partecipante?",
|
||||
"muteParticipantsVideoBody": "Non potrai riattivare le videocamere, ma loro potranno farlo in qualsiasi momento.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Non potrai riattivare le videocamere e nemmeno loro potranno.",
|
||||
"muteParticipantsVideoButton": "Spegni videocamere",
|
||||
"muteParticipantsVideoDialog": "Sei sicuro di voler spegnere la videocamera di questo partecipante? Non potrai riattivarla, ma lui potrà farlo in qualsiasi momento.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Sei sicuro di voler spegnere la videocamera di questo partecipante? Non potrai riattivarla e nemmeno lui potrà farlo.",
|
||||
"muteParticipantsVideoDialog": "Vuoi spegnere la videocamera di questo partecipante? Non potrai riattivarla, ma lui potrà farlo in qualsiasi momento.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Vuoi spegnere la videocamera di questo partecipante? Non potrai riattivarla e nemmeno lui potrà farlo.",
|
||||
"muteParticipantsVideoTitle": "Spegnere la videocamera di questo partecipante?",
|
||||
"noDropboxToken": "Token Dropbox non valido",
|
||||
"password": "Password",
|
||||
@@ -424,7 +444,7 @@
|
||||
"remoteControlTitle": "Connessione desktop remoto",
|
||||
"remoteUserControls": "Controlli dell'utente remoto {{username}}",
|
||||
"removePassword": "Rimuovi la $t(lockRoomPassword)",
|
||||
"removeSharedVideoMsg": "Sei sicuro di voler rimuovere il tuo video condiviso?",
|
||||
"removeSharedVideoMsg": "Vuoi rimuovere il tuo video condiviso?",
|
||||
"removeSharedVideoTitle": "Rimuovi video condiviso",
|
||||
"renameBreakoutRoomLabel": "Nome della stanza",
|
||||
"renameBreakoutRoomTitle": "Rinomina stanza",
|
||||
@@ -477,8 +497,8 @@
|
||||
"startRemoteControlErrorMessage": "Si è verificato un errore nel tentativo di avviare la sessione di controllo remoto!",
|
||||
"stopLiveStreaming": "Ferma la diretta",
|
||||
"stopRecording": "Ferma registrazione",
|
||||
"stopRecordingWarning": "Sei sicuro di voler interrompere la registrazione?",
|
||||
"stopStreamingWarning": "Sei sicuro di voler interrompere la diretta?",
|
||||
"stopRecordingWarning": "Vuoi interrompere la registrazione?",
|
||||
"stopStreamingWarning": "Vuoi interrompere la diretta?",
|
||||
"streamKey": "Chiave della diretta",
|
||||
"thankYou": "Grazie per aver usato {{appName}}!",
|
||||
"token": "token",
|
||||
@@ -547,11 +567,15 @@
|
||||
"downloadFailedDescription": "Si prega di riprovare.",
|
||||
"downloadFailedTitle": "Download non riuscito",
|
||||
"downloadFile": "Download",
|
||||
"downloadStarted": "Download del file iniziato",
|
||||
"dragAndDrop": "Trascina e rilascia i file qui o da qualsiasi altra parte nella schermata",
|
||||
"fileAlreadyUploaded": "Questo file è già stato caricato nella riunione.",
|
||||
"fileTooLargeDescription": "Assicurati che il file non superi {{ maxFileSize }}.",
|
||||
"fileTooLargeTitle": "Il file selezionato è troppo grande",
|
||||
"fileUploadProgress": "Caricamento del file in corso",
|
||||
"fileUploadedSuccessfully": "Il file è stato caricato con successo",
|
||||
"removeFile": "Rimuovi",
|
||||
"removeFileSuccess": "File rimosso con successo",
|
||||
"uploadFailedDescription": "Si prega di riprovare.",
|
||||
"uploadFailedTitle": "Caricamento non riuscito",
|
||||
"uploadFile": "Condividi file"
|
||||
@@ -683,7 +707,7 @@
|
||||
"signInCTA": "Accedi o inserisci la tua chiave della diretta su YouTube.",
|
||||
"signOut": "Disconnetti",
|
||||
"signedInAs": "Hai effettuato l'accesso come:",
|
||||
"start": "Inizia una diretta",
|
||||
"start": "Avvia una diretta",
|
||||
"streamIdHelp": "Cos'è questo?",
|
||||
"title": "Diretta",
|
||||
"unavailableTitle": "La diretta non è disponibile",
|
||||
@@ -744,9 +768,9 @@
|
||||
"engaged": "Registrazione avviata.",
|
||||
"finished": "La registrazione della sessione {{token}} è terminata. Invia il file della registrazione all'organizzatore.",
|
||||
"finishedModerator": "La registrazione della sessione {{token}} è terminata. La registrazione della traccia è stata salvata. Chiedi ai partecipanti di inviare le loro registrazioni.",
|
||||
"notModerator": "Non sei un organizzatore. Non puoi avviare o interrompere la registrazione."
|
||||
"notModerator": "Non sei un relatore. Non puoi avviare o interrompere la registrazione."
|
||||
},
|
||||
"moderator": "Organizzatore",
|
||||
"moderator": "Relatore",
|
||||
"no": "No",
|
||||
"participant": "Partecipante",
|
||||
"participantStats": "Statistiche partecipante",
|
||||
@@ -767,8 +791,9 @@
|
||||
"me": "io",
|
||||
"notify": {
|
||||
"OldElectronAPPTitle": "Falla di sicurezza!",
|
||||
"allowAll": "Consenti tutto",
|
||||
"allowAudio": "Consenti l'audio",
|
||||
"allowBoth": "Entrambi",
|
||||
"allowDesktop": "Consenti la condivisione dello schermo",
|
||||
"allowVideo": "Consenti il video",
|
||||
"allowedUnmute": "Puoi accendere il microfono, avviare la videocamera o condividere il tuo schermo.",
|
||||
"audioUnmuteBlockedDescription": "L'accensione dei microfoni è stata temporaneamente bloccata per i limiti del sistema.",
|
||||
@@ -782,6 +807,7 @@
|
||||
"dataChannelClosedDescription": "Il canale bridge è inattivo, quindi la qualità video potrebbe essere limitata all'impostazione più bassa.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Il canale bridge è inattivo, quindi potrebbero esserci interruzioni di audio e video",
|
||||
"dataChannelClosedWithAudio": "La qualità audio e video potrebbe essere scarsa",
|
||||
"desktopMutedRemotelyTitle": "La tua condivisione dello schermo è stata interrotta da {{participantDisplayName}}",
|
||||
"disabledIframe": "L'incorporamento serve solo come dimostrazione, quindi questa chiamata si disconnetterà tra {{timeout}} minuti.",
|
||||
"disabledIframeSecondaryNative": "L'incorporamento {{domain}} serve solo come dimostrazione, quindi questa chiamata si disconnetterà tra {{timeout}} minuti.",
|
||||
"disabledIframeSecondaryWeb": "L'incorporamento {{domain}} serve solo come dimostrazione, quindi questa chiamata si disconnetterà tra {{timeout}} minuti. Si prega di usare <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> per la produzione di riunioni incorporate!",
|
||||
@@ -792,7 +818,7 @@
|
||||
"focusFail": "{{component}} non disponibile - nuovo tentativo tra {{ms}} sec",
|
||||
"gifsMenu": "GIPHY",
|
||||
"groupTitle": "Notifiche",
|
||||
"hostAskedUnmute": "L'organizzatore ti chiede di intervenire.",
|
||||
"hostAskedUnmute": "Il relatore ti chiede di intervenire.",
|
||||
"invalidTenant": "Nome non valido",
|
||||
"invalidTenantHyphenDescription": "Il nome che hai scelto non è valido (inizia o finisce con '-').",
|
||||
"invalidTenantLengthDescription": "Il nome che hai scelto è troppo lungo.",
|
||||
@@ -814,17 +840,17 @@
|
||||
"localRecordingStopped": "{{name}} ha smesso di registrare.",
|
||||
"me": "Io",
|
||||
"moderationInEffectCSDescription": "Alza la mano, se vuoi condividere lo schermo.",
|
||||
"moderationInEffectCSTitle": "La condivisione schermo è stata bloccata dall'organizzatore",
|
||||
"moderationInEffectCSTitle": "La condivisione schermo è stata bloccata dal relatore",
|
||||
"moderationInEffectDescription": "Alza la mano, se vuoi prendere la parola.",
|
||||
"moderationInEffectTitle": "Il tuo microfono è stato spento dall'organizzatore",
|
||||
"moderationInEffectTitle": "Il tuo microfono è stato spento dal relatore",
|
||||
"moderationInEffectVideoDescription": "Alza la mano, se vuoi avviare la tua videocamera.",
|
||||
"moderationInEffectVideoTitle": "La tua videocamera è stata spenta dall'organizzatore",
|
||||
"moderationRequestFromModerator": "L'organizzatore vorrebbe che tu accendessi il microfono",
|
||||
"moderationInEffectVideoTitle": "La tua videocamera è stata spenta dal relatore",
|
||||
"moderationRequestFromModerator": "Il relatore vorrebbe che tu accendessi il microfono",
|
||||
"moderationRequestFromParticipant": "Vuole parlare",
|
||||
"moderationStartedTitle": "Moderazione in corso",
|
||||
"moderationStoppedTitle": "Moderazione interrotta",
|
||||
"moderationToggleDescription": "da {{participantDisplayName}}",
|
||||
"moderator": "Ora sei un organizzatore!",
|
||||
"moderator": "Ora sei un relatore!",
|
||||
"muted": "Hai iniziato la conversazione con il microfono disattivato.",
|
||||
"mutedRemotelyDescription": "Puoi sempre attivare il microfono quando vuoi parlare. Spegni il microfono quando hai finito, per evitare rumori di fondo nella riunione.",
|
||||
"mutedRemotelyTitle": "{{participantDisplayName}} ti ha spento il microfono",
|
||||
@@ -839,6 +865,7 @@
|
||||
"oldElectronClientDescription1": "Sembra che tu stia usando una versione obsoleta del client Jitsi Meet, che ha vulnerabilità note. Assicurati di aggiornarlo alla nostra ",
|
||||
"oldElectronClientDescription2": "ultima versione",
|
||||
"oldElectronClientDescription3": " ora!",
|
||||
"openChat": "Apri chat",
|
||||
"participantWantsToJoin": "Vuole unirsi alla riunione",
|
||||
"participantsWantToJoin": "Vogliono unirsi alla riunione",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) è stata rimossa da un altro partecipante",
|
||||
@@ -862,6 +889,7 @@
|
||||
"suggestRecordingDescription": "Vuoi avviare la registrazione?",
|
||||
"suggestRecordingTitle": "Registra questa riunione",
|
||||
"unmute": "Accendi il microfono",
|
||||
"unmuteScreen": "Iniziata la condivisione dello schermo",
|
||||
"unmuteVideo": "Accendi la videocamera",
|
||||
"videoMutedRemotelyDescription": "Puoi riaccenderla in qualsiasi momento.",
|
||||
"videoMutedRemotelyTitle": "{{participantDisplayName}} ti ha spento la videocamera",
|
||||
@@ -881,11 +909,14 @@
|
||||
"admit": "Ammetti",
|
||||
"admitAll": "Ammetti tutti",
|
||||
"allow": "Permetti ai partecipanti di:",
|
||||
"allowDesktop": "Consenti la condivisione dello schermo",
|
||||
"allowVideo": "Permetti videocamere",
|
||||
"askDesktop": "Chiedi di condividere lo schermo",
|
||||
"askUnmute": "Chiedi di accendere il microfono",
|
||||
"audioModeration": "Riattivare il microfono",
|
||||
"blockEveryoneMicCamera": "Blocca microfono e videocamera a tutti",
|
||||
"breakoutRooms": "Stanze",
|
||||
"desktopModeration": "Avvia la condivisione dello schermo",
|
||||
"goLive": "Vai alla diretta",
|
||||
"invite": "Invita partecipanti",
|
||||
"lowerAllHands": "Abbassa tutte le mani",
|
||||
@@ -897,6 +928,8 @@
|
||||
"muteAll": "Silenzia tutti",
|
||||
"muteEveryoneElse": "Silenzia tutti gli altri",
|
||||
"reject": "Respingi",
|
||||
"stopDesktop": "Interrompi la condivisione dello schermo",
|
||||
"stopEveryonesDesktop": "Interrompi la condivisione dello schermo agli altri",
|
||||
"stopEveryonesVideo": "Ferma il video di tutti",
|
||||
"stopVideo": "Ferma il video",
|
||||
"unblockEveryoneMicCamera": "Sblocca microfono e videocamera a tutti",
|
||||
@@ -906,6 +939,7 @@
|
||||
"headings": {
|
||||
"lobby": "Sala d'attesa ({{count}})",
|
||||
"participantsList": "Partecipanti alla riunione ({{count}})",
|
||||
"viewerRequests": "Richieste spettatori ({{count}})",
|
||||
"visitorInQueue": " ({{count}} in attesa)",
|
||||
"visitorRequests": " ({{count}} richiesta/e)",
|
||||
"visitors": "Spettatori {{count}}",
|
||||
@@ -1073,7 +1107,7 @@
|
||||
"fileSharingdescription": "Condividi la registrazione con i partecipanti alla riunione",
|
||||
"highlight": "Evidenzia",
|
||||
"highlightMoment": "Evidenzia momento",
|
||||
"highlightMomentDisabled": "Puoi evidenziare dei momenti quando inizia la registrazione",
|
||||
"highlightMomentDisabled": "Puoi evidenziare i momenti in cui inizia la registrazione",
|
||||
"highlightMomentSuccess": "Momento evidenziato",
|
||||
"highlightMomentSucessDescription": "Il tuo momento evidenziato sarà aggiunto al riepilogo della riunione.",
|
||||
"inProgress": "Registrazione o diretta in corso",
|
||||
@@ -1084,7 +1118,7 @@
|
||||
"localRecordingNoVideo": "Il video non sta venendo registrato",
|
||||
"localRecordingStartWarning": "Assicurati di interrompere la registrazione prima di uscire dalla riunione, altrimenti la registrazione non verrà salvata.",
|
||||
"localRecordingStartWarningTitle": "Interrompi la registrazione per salvarla",
|
||||
"localRecordingVideoStop": "Interrompere il video fermerà anche la registrazione. Sei sicuro di voler continuare?",
|
||||
"localRecordingVideoStop": "Interrompere il video fermerà anche la registrazione. Vuoi continuare?",
|
||||
"localRecordingVideoWarning": "Per registrare il video, deve essere già avviato prima dell'inizio della registrazione",
|
||||
"localRecordingWarning": "Assicurati di aver selezionato la scheda corrente, per registrare gli audio e video corretti.",
|
||||
"loggedIn": "Accesso effettuato come {{userName}}",
|
||||
@@ -1153,8 +1187,8 @@
|
||||
"loggedIn": "Connesso come {{name}}",
|
||||
"maxStageParticipants": "Numero massimo di partecipanti che possono essere messi in evidenza nella schermata principale",
|
||||
"microphones": "Microfoni",
|
||||
"moderator": "Organizzatore",
|
||||
"moderatorOptions": "Opzioni organizzatore",
|
||||
"moderator": "Relatore",
|
||||
"moderatorOptions": "Opzioni relatore",
|
||||
"more": "Generali",
|
||||
"name": "Nome",
|
||||
"noDevice": "Nessuno",
|
||||
@@ -1190,7 +1224,7 @@
|
||||
"conferenceSection": "Riunione",
|
||||
"disableCallIntegration": "Disattiva l'integrazione nativa delle chiamate",
|
||||
"disableCrashReporting": "Disattiva la diagnostica dei crash",
|
||||
"disableCrashReportingWarning": "Sei sicuro di voler disattivare la diagnostica dei crash? Quest'impostazione verrà applicata al prossimo avvio dell'app.",
|
||||
"disableCrashReportingWarning": "Vuoi disattivare la diagnostica dei crash? Quest'impostazione verrà applicata al prossimo avvio dell'app.",
|
||||
"disableP2P": "Disattiva la modalità Peer-to-Peer",
|
||||
"displayName": "Nome visualizzato",
|
||||
"displayNamePlaceholderText": "Es: Mario Rossi",
|
||||
@@ -1207,8 +1241,8 @@
|
||||
"serverURL": "URL del server",
|
||||
"showAdvanced": "Mostra impostazioni avanzate",
|
||||
"startCarModeInLowBandwidthMode": "Avvia modalità auto in modalità larghezza di banda limitata",
|
||||
"startWithAudioMuted": "Inizia con audio disattivato",
|
||||
"startWithVideoMuted": "Inizia con video disattivato",
|
||||
"startWithAudioMuted": "Avvia con audio disattivato",
|
||||
"startWithVideoMuted": "Avvia con video disattivato",
|
||||
"terms": "Termini",
|
||||
"version": "Versione"
|
||||
},
|
||||
@@ -1283,7 +1317,7 @@
|
||||
"feedback": "Lascia un feedback",
|
||||
"fullScreen": "Attiva modalità a schermo intero",
|
||||
"giphy": "Mostra menu GIPHY",
|
||||
"grantModerator": "Concedi permessi di organizzatore",
|
||||
"grantModerator": "Concedi permessi da relatore",
|
||||
"hangup": "Lascia la riunione",
|
||||
"heading": "Barra degli strumenti",
|
||||
"help": "Aiuto",
|
||||
@@ -1346,6 +1380,20 @@
|
||||
"videounmute": "Accendi videocamera"
|
||||
},
|
||||
"addPeople": "Aggiungi partecipanti alla chiamata",
|
||||
"advancedAudioSettings": {
|
||||
"aec": {
|
||||
"label": "Cancellazione dell'eco"
|
||||
},
|
||||
"agc": {
|
||||
"label": "Controllo del guadagno automatico"
|
||||
},
|
||||
"ns": {
|
||||
"label": "Cancellazione del rumore"
|
||||
},
|
||||
"stereo": {
|
||||
"label": "Stereo"
|
||||
}
|
||||
},
|
||||
"audioOnlyOff": "Disabilita modalità larghezza di banda limitata",
|
||||
"audioOnlyOn": "Abilita modalità larghezza di banda limitata",
|
||||
"audioRoute": "Scegli il dispositivo audio",
|
||||
@@ -1412,11 +1460,12 @@
|
||||
"profile": "Modifica profilo",
|
||||
"raiseHand": "Alza la mano",
|
||||
"raiseYourHand": "Alza la mano",
|
||||
"reactionBoo": "Invia buu",
|
||||
"reactionClap": "Invia applauso",
|
||||
"reactionHeart": "Invia cuore",
|
||||
"reactionLaugh": "Invia risata",
|
||||
"reactionLike": "Invia mi piace",
|
||||
"reactionBoo": "Invia Buu",
|
||||
"reactionClap": "Invia Applauso",
|
||||
"reactionHeart": "Invia Cuore",
|
||||
"reactionLaugh": "Invia Risata",
|
||||
"reactionLike": "Invia Mi piace",
|
||||
"reactionLove": "Invia Love",
|
||||
"reactionSilence": "Invia senza parole",
|
||||
"reactionSurprised": "Invia a bocca aperta",
|
||||
"reactions": "Reazioni",
|
||||
@@ -1502,15 +1551,17 @@
|
||||
"connectionInfo": "Informazioni connessione",
|
||||
"demote": "Fai diventare spettatore",
|
||||
"domute": "Disattiva microfono",
|
||||
"domuteDesktop": "Interrompi la condivisione dello schermo",
|
||||
"domuteDesktopOfOthers": "Interrompi la condivisione dello schermo agli altri",
|
||||
"domuteOthers": "Disattiva microfono a tutti gli altri",
|
||||
"domuteVideo": "Disattiva videocamera",
|
||||
"domuteVideoOfOthers": "Disattiva videocamera a tutti gli altri",
|
||||
"flip": "Specchia",
|
||||
"grantModerator": "Concedi permessi da organizzatore",
|
||||
"grantModerator": "Concedi permessi da relatore",
|
||||
"hideSelfView": "Nascondi la tua immagine",
|
||||
"kick": "Espelli",
|
||||
"mirrorVideo": "Specchia il tuo video",
|
||||
"moderator": "Organizzatore",
|
||||
"moderator": "Relatore",
|
||||
"mute": "Il partecipante ha il microfono spento",
|
||||
"muted": "Microfono spento",
|
||||
"pinToStage": "Metti in primo piano",
|
||||
@@ -1557,7 +1608,7 @@
|
||||
"description": "Adesso sei uno spettatore in questa riunione.",
|
||||
"raiseHand": "Alza la mano",
|
||||
"title": "Ingresso nella riunione in corso",
|
||||
"wishToSpeak": "Se vuoi parlare, si prega di alzare la mano sotto e aspettare l'autorizzazione dell'organizzatore."
|
||||
"wishToSpeak": "Se vuoi parlare, si prega di alzare la mano sotto e aspettare l'autorizzazione del relatore."
|
||||
},
|
||||
"labelTooltip": "Numero di spettatori: {{count}}",
|
||||
"notification": {
|
||||
@@ -1618,7 +1669,7 @@
|
||||
"roomnameHint": "Inserisci il nome o l'URL della riunione a cui vuoi accedere. Puoi anche inventarti un nome, assicurati solo che le persone con cui vuoi collegarti lo conoscano, così che possano inserire lo stesso nome.",
|
||||
"sendFeedback": "Invia feedback",
|
||||
"settings": "Impostazioni",
|
||||
"startMeeting": "Inizia riunione",
|
||||
"startMeeting": "Avvia riunione",
|
||||
"terms": "Termini di utilizzo",
|
||||
"title": "Il sistema di videoconferenza sicuro, funzionale e completamente gratuito.",
|
||||
"upcomingMeetings": "Prossime riunioni"
|
||||
|
||||
@@ -112,7 +112,9 @@
|
||||
"disabled": "Tērzēšanas ziņojumu sūtīšana ir atspējota.",
|
||||
"enter": "Ienākt istabā",
|
||||
"error": "Kļūda: Jūsu ziņa netika nosūtīta. Cēlonis: {{error}}",
|
||||
"everyone": "Visi",
|
||||
"fieldPlaceHolder": "Rakstiet ziņu šeit",
|
||||
"guestsChatIndicator": "(viesis)",
|
||||
"lobbyChatMessageTo": "Vestibila tērzēšanas ziņa adresātam {{recipient}}",
|
||||
"message": "Ziņa",
|
||||
"messageAccessibleTitle": "{{user}} saka:",
|
||||
@@ -565,11 +567,15 @@
|
||||
"downloadFailedDescription": "Lūdzu, mēģiniet vēlreiz.",
|
||||
"downloadFailedTitle": "Lejuplādes kļūda",
|
||||
"downloadFile": "Lejuplādēt",
|
||||
"downloadStarted": "Sākta faila lejuplāde",
|
||||
"dragAndDrop": "Velciet un palaidiet failus šeit, vai jebkurā ekrāna vietā",
|
||||
"fileAlreadyUploaded": "Fails jau ir augšupielādēts šajā sanāksmē.",
|
||||
"fileAlreadyUploaded": "Fails jau ir augšuplādēts šajā sanāksmē.",
|
||||
"fileTooLargeDescription": "Lūdzu, pārliecinieties, vai faila lielums nepārsniedz {{ maxFileSize }}.",
|
||||
"fileTooLargeTitle": "Izvēlētais fails ir pārāk liels",
|
||||
"fileUploadProgress": "Faila augšuplādes gaita",
|
||||
"fileUploadedSuccessfully": "Fails veiksmīgi augšuplādēts",
|
||||
"removeFile": "Noņemt",
|
||||
"removeFileSuccess": "Fails veiksmīgi noņemts",
|
||||
"uploadFailedDescription": "Lūdzu, mēģiniet vēlreiz.",
|
||||
"uploadFailedTitle": "Augšuplādes kļūda",
|
||||
"uploadFile": "Kopīgot failu"
|
||||
|
||||
@@ -109,8 +109,10 @@
|
||||
}
|
||||
},
|
||||
"chat": {
|
||||
"disabled": "O envio de mensagens de chat está desativado.",
|
||||
"enter": "Entrar na sala",
|
||||
"error": "Erro: a sua mensagem não foi enviada. Motivo: {{error}}",
|
||||
"everyone": "Todos",
|
||||
"fieldPlaceHolder": "Aa",
|
||||
"lobbyChatMessageTo": "Mensagem de chat na sala de espera para {{recipient}}",
|
||||
"message": "Mensagem",
|
||||
@@ -122,7 +124,10 @@
|
||||
"nickname": {
|
||||
"popover": "Escolha um apelido",
|
||||
"title": "Introduza um apelido para usar o chat",
|
||||
"titleWithPolls": "Introduza um apelido para usar o chat e as sondagens"
|
||||
"titleWithCC": "Insira um apelido para usar o chat e as legendas ocultas",
|
||||
"titleWithPolls": "Digite um apelido para usar o chat e as sondagens",
|
||||
"titleWithPollsAndCC": "Insira um apelido para utilizar o chat, as sondagens e as legendas ocultas",
|
||||
"titleWithPollsAndCCAndFileSharing": "Insira um apelido para utilizar o chat, as sondagens, as legendas e os ficheiros"
|
||||
},
|
||||
"noMessagesMessage": "Ainda não há mensagens na reunião. Comece aqui uma conversa!",
|
||||
"privateNotice": "Mensagem privada para {{recipient}}",
|
||||
@@ -131,10 +136,15 @@
|
||||
"systemDisplayName": "Sistema",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"closedCaptions": "LO",
|
||||
"fileSharing": "Ficheiros",
|
||||
"polls": "Sondagens"
|
||||
},
|
||||
"title": "Chat",
|
||||
"titleWithPolls": "Chat e Sondagens",
|
||||
"titleWithCC": "LO",
|
||||
"titleWithFeatures": "Chat e",
|
||||
"titleWithFileSharing": "Ficheiros",
|
||||
"titleWithPolls": "Sondagens",
|
||||
"you": "você"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
@@ -144,6 +154,10 @@
|
||||
"dontShowAgain": "Não me mostre isto outra vez",
|
||||
"installExtensionText": "Instalar a extensão para a integração Google Calendar e Office 365"
|
||||
},
|
||||
"closedCaptionsTab": {
|
||||
"emptyState": "O conteúdo das legendas ocultas estará disponível assim que um moderador iniciar a sessão.",
|
||||
"startClosedCaptionsButton": "Iniciar legendas ocultas"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "A ligá-lo à reunião…"
|
||||
},
|
||||
@@ -263,6 +277,8 @@
|
||||
"Remove": "Remover",
|
||||
"Share": "Partilhar",
|
||||
"Submit": "Submeter",
|
||||
"Understand": "Entendo, mantenha-me em silêncio por enquanto.",
|
||||
"UnderstandAndUnmute": "Entendo, por favor, desative o silêncio.",
|
||||
"WaitForHostMsg": "A conferência ainda não começou porque ainda não chegaram moderadores. Se quiser ser um moderador, inicie a sessão. Caso contrário, aguarde.",
|
||||
"WaitForHostNoAuthMsg": "A conferência ainda não começou porque ainda não chegaram os moderadores. Por favor, aguarde.",
|
||||
"WaitingForHostButton": "Esperar pelo moderador",
|
||||
@@ -285,6 +301,12 @@
|
||||
"alreadySharedVideoTitle": "Só é permitido um vídeo partilhado de cada vez",
|
||||
"applicationWindow": "Janela de aplicação",
|
||||
"authenticationRequired": "Autenticação necessária",
|
||||
"cameraCaptureDialog": {
|
||||
"description": "Tire e envie uma foto usando a câmara do seu telemóvel",
|
||||
"ok": "Ligar a câmara",
|
||||
"reject": "Agora não",
|
||||
"title": "Tire uma foto"
|
||||
},
|
||||
"cameraConstraintFailedError": "A sua câmara não satisfaz algumas das restrições exigidas.",
|
||||
"cameraNotFoundError": "A câmara não foi encontrada.",
|
||||
"cameraNotSendingData": "Não podemos aceder à sua câmara. Verifique se outra aplicação está a utilizar este dispositivo, seleccione outro dispositivo do menu de definições ou tente recarregar a aplicação.",
|
||||
@@ -299,6 +321,7 @@
|
||||
"conferenceReloadMsg": "Estamos a tentar resolver isto. Reconexão em {{seconds}} seg…",
|
||||
"conferenceReloadTitle": "Infelizmente, algo correu mal.",
|
||||
"confirm": "Confirme",
|
||||
"confirmBack": "Voltar",
|
||||
"confirmNo": "Não",
|
||||
"confirmYes": "Sim",
|
||||
"connectError": "Oops! Algo correu mal e não conseguimos estabelecer uma ligação com a conferência.",
|
||||
@@ -307,8 +330,8 @@
|
||||
"contactSupport": "Contacte o suporte",
|
||||
"copied": "Copiado",
|
||||
"copy": "Cópia",
|
||||
"demoteParticipantDialog": "Tem a certeza de que pretende passar este participante para visitante?",
|
||||
"demoteParticipantTitle": "Passar a visitante",
|
||||
"demoteParticipantDialog": "Tem a certeza de que deseja mover este participante para espectador?",
|
||||
"demoteParticipantTitle": "Mover para espectador",
|
||||
"dismiss": "Dispensar",
|
||||
"displayNameRequired": "Olá! Qual é o seu nome?",
|
||||
"done": "Feito",
|
||||
@@ -334,7 +357,9 @@
|
||||
"kickParticipantButton": "Expulsar",
|
||||
"kickParticipantDialog": "Tem a certeza que quer expulsar este participante?",
|
||||
"kickParticipantTitle": "Expulsar este participante?",
|
||||
"kickSystemTitle": "Ai! Foste expulso da reunião.",
|
||||
"kickTitle": "Ai! {{participantDisplayName}} expulsou-o da reunião",
|
||||
"learnMore": "saiba mais",
|
||||
"linkMeeting": "Link da reunião",
|
||||
"linkMeetingTitle": "Link da reunião à Força de Vendas",
|
||||
"liveStreaming": "Transmissão em direto",
|
||||
@@ -356,23 +381,35 @@
|
||||
"micPermissionDeniedError": "Não concedeu autorização para utilizar o seu microfone. Ainda pode participar na conferência, mas outros não o ouvirão. Use o botão da câmara na barra de endereço para corrigir isto.",
|
||||
"micTimeoutError": "Não foi possível iniciar a fonte de áudio. Tempo limite expirado!",
|
||||
"micUnknownError": "Não pode usar microfone por uma razão desconhecida.",
|
||||
"moderationAudioLabel": "Permitir aos participantes ligar o som",
|
||||
"moderationVideoLabel": "Permitir aos participantes ligar a câmara",
|
||||
"moderationAudioLabel": "Permitir aos não moderadores ligar o som",
|
||||
"moderationDesktopLabel": "Permitir que não moderadores partilhem o seu ecrã",
|
||||
"moderationVideoLabel": "Permitir que não moderadores iniciem os seus vídeos",
|
||||
"muteEveryoneDialog": "Os participantes podem ligar o som a qualquer momento.",
|
||||
"muteEveryoneDialogModerationOn": "Os participantes podem enviar um pedido para falar a qualquer momento.",
|
||||
"muteEveryoneElseDialog": "Uma vez silenciados, não poderá reativá-los, mas eles podem ligar o microfone a qualquer momento.",
|
||||
"muteEveryoneElseTitle": "Silenciar todos excepto {{whom}}?",
|
||||
"muteEveryoneElsesDesktopDialog": "Depois que o compartilhamento for interrompido, não será possível reiniciá-lo, mas eles poderão fazê-lo a qualquer momento.",
|
||||
"muteEveryoneElsesDesktopTitle": "SInterromper a partilha de ecrã de todos, exceto {{whom}}?",
|
||||
"muteEveryoneElsesVideoDialog": "Quando a câmara for desligada, não poderá voltar a ligá-la, mas eles podem voltar a ligá-la em qualquer momento.",
|
||||
"muteEveryoneElsesVideoTitle": "Parar o vídeo de todos excepto {{whom}}?",
|
||||
"muteEveryoneSelf": "você mesmo",
|
||||
"muteEveryoneStartMuted": "A partir de agora, toda a gente começa a ficar calada",
|
||||
"muteEveryoneTitle": "Silenciar toda a gente?",
|
||||
"muteEveryonesDesktopDialog": "Os participantes podem partilhar o seu ecrã a qualquer momento.",
|
||||
"muteEveryonesDesktopDialogModerationOn": "Os participantes podem enviar um pedido para partilhar o seu ecrã a qualquer momento.",
|
||||
"muteEveryonesDesktopTitle": "Interromper a partilha de ecrã de todos?",
|
||||
"muteEveryonesVideoDialog": "Os participantes podem ligar a sua câmara a qualquer momento.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Os participantes podem enviar um pedido para ligar a sua câmara a qualquer momento.",
|
||||
"muteEveryonesVideoDialogOk": "Desativar",
|
||||
"muteEveryonesVideoTitle": "Desligar a câmara de todos?",
|
||||
"muteParticipantBody": "Não poderá reativá-los, mas eles podem reativar-se a qualquer momento.",
|
||||
"muteParticipantButton": "Silenciar",
|
||||
"muteParticipantsDesktopBody": "Não poderá iniciar a partilha de ecrã deles, mas eles podem fazê-lo a qualquer momento.",
|
||||
"muteParticipantsDesktopBodyModerationOn": "Não será possível iniciar a partilha de ecrã nem para si nem para eles.",
|
||||
"muteParticipantsDesktopButton": "Parar a partilha de ecrã",
|
||||
"muteParticipantsDesktopDialog": "Tem a certeza de que deseja desativar a partilha de ecrã deste participante? Não será possível reiniciá-la, mas ele poderá fazê-lo a qualquer momento.",
|
||||
"muteParticipantsDesktopDialogModerationOn": "Tem a certeza de que deseja desativar a partilha de ecrã deste participante? Não será possível reativar o ecrã, nem para si nem para ele.",
|
||||
"muteParticipantsDesktopTitle": "Desativar a partilha de ecrã deste participante?",
|
||||
"muteParticipantsVideoBody": "Não poderá voltar a ligar a câmara, mas eles podem voltar a ligá-la a qualquer momento.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Não será capaz de voltar a ligar a câmara e eles também não.",
|
||||
"muteParticipantsVideoButton": "Parar vídeo",
|
||||
@@ -392,6 +429,10 @@
|
||||
"recentlyUsedObjects": "Os seus objetos recentemente utilizados",
|
||||
"recording": "A gravar",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Não possível enquanto a transmissão em direto estiver activa",
|
||||
"recordingInProgressDescription": "Esta reunião está a ser gravada e analisada pela IA{{learnMore}}. O seu áudio e vídeo foram silenciados. Se optar por ativar o som, concorda em ser gravado.",
|
||||
"recordingInProgressDescriptionFirstHalf": "Esta reunião está a ser gravada e analisada por IA.",
|
||||
"recordingInProgressDescriptionSecondHalf": ". Your audio and video have been muted. If you choose to unmute, you consent to being recorded.",
|
||||
"recordingInProgressTitle": "Gravação em andamento",
|
||||
"rejoinNow": "Reingressar agora",
|
||||
"remoteControlAllowedMessage": "{{user}} aceitou o seu pedido de controlo remoto!",
|
||||
"remoteControlDeniedMessage": "{{user}} rejeitou o seu pedido de controlo remoto!",
|
||||
@@ -439,7 +480,10 @@
|
||||
"shareScreenWarningD2": "é necessário parar a partilha de áudio, iniciar a partilha de ecrã e verificar a opção \"partilhar áudio\".",
|
||||
"shareScreenWarningH1": "Se quiser partilhar apenas o seu ecrã:",
|
||||
"shareScreenWarningTitle": "Tem de parar a partilha de áudio antes de partilhar o seu ecrã",
|
||||
"shareVideoLinkError": "Por favor, forneça um link correcto do vídeo.",
|
||||
"shareVideoConfirmPlay": "Está prestes a abrir um site externo. Deseja continuar?",
|
||||
"shareVideoConfirmPlayTitle": "{{name}} partilhou um vídeo consigo.",
|
||||
"shareVideoLinkError": "Oops, este vídeo não pode ser reproduzido.",
|
||||
"shareVideoLinkStopped": "O vídeo de {{name}} foi interrompido.",
|
||||
"shareVideoTitle": "Partilhar vídeo",
|
||||
"shareYourScreen": "Partilhe o seu ecrã",
|
||||
"shareYourScreenDisabled": "Partilha de ecrã desactivada.",
|
||||
@@ -518,6 +562,21 @@
|
||||
"veryBad": "Muito má",
|
||||
"veryGood": "Muito boa"
|
||||
},
|
||||
"fileSharing": {
|
||||
"downloadFailedDescription": "Por favor, tente novamente.",
|
||||
"downloadFailedTitle": "Falha no descarregar",
|
||||
"downloadFile": "Descarregar",
|
||||
"dragAndDrop": "Arraste e solte os ficheiros aqui ou em qualquer lugar do ecrã",
|
||||
"fileAlreadyUploaded": "O ficheiro já foi carregado para esta reunião.",
|
||||
"fileTooLargeDescription": "Certifique-se de que o ficheiro não exceda {{ maxFileSize }}.",
|
||||
"fileTooLargeTitle": "O ficheiro selecionado é muito grande",
|
||||
"fileUploadProgress": "Progresso do envio do ficheiro",
|
||||
"fileUploadedSuccessfully": "Ficheiro carregado com sucesso",
|
||||
"removeFile": "Remover",
|
||||
"uploadFailedDescription": "Por favor, tente novamente.",
|
||||
"uploadFailedTitle": "Falha ao carregar",
|
||||
"uploadFile": "Partilhar ficheiro"
|
||||
},
|
||||
"filmstrip": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "Miniaturas de vídeo"
|
||||
@@ -638,6 +697,7 @@
|
||||
"on": "Iniciada a transmissão em direto",
|
||||
"onBy": "{{name}} iniciou a transmissão em direto",
|
||||
"pending": "Início da transmissão em direto…",
|
||||
"policyError": "Tentou iniciar uma transmissão ao vivo muito rapidamente. Por favor, tente novamente mais tarde!",
|
||||
"serviceName": "Serviço de Transmissão em Direto",
|
||||
"sessionAlreadyActive": "Esta sessão já está a ser gravada ou transmitida em direto.",
|
||||
"signIn": "Iniciar sessão com o Google",
|
||||
@@ -728,7 +788,10 @@
|
||||
"me": "eu",
|
||||
"notify": {
|
||||
"OldElectronAPPTitle": "Vulnerabilidade de segurança!",
|
||||
"allowAction": "Permitir",
|
||||
"allowAll": "Permitir tudo",
|
||||
"allowAudio": "Permitir áudio",
|
||||
"allowDesktop": "Permitir partilha de ecrã",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"allowedUnmute": "Pode ligar o seu microfone, ligar a sua câmara ou partilhar o seu ecrã.",
|
||||
"audioUnmuteBlockedDescription": "A operação de ligar o microfone foi temporariamente bloqueada devido aos limites do sistema.",
|
||||
"audioUnmuteBlockedTitle": "Ligar microfone bloqueado!",
|
||||
@@ -736,12 +799,15 @@
|
||||
"connectedOneMember": "{{name}} entrou na reunião",
|
||||
"connectedThreePlusMembers": "{{name}} e muitos outros entraram na reunião",
|
||||
"connectedTwoMembers": "{{first}} e {{second}} entraram na reunião",
|
||||
"connectionFailed": "Falha na ligação. Por favor, tente novamente mais tarde!",
|
||||
"dataChannelClosed": "A qualidade do vídeo pode ser afetada",
|
||||
"dataChannelClosedDescription": "O canal de ponte está em baixo e, por isso, a qualidade de vídeo pode estar limitada à sua definição mais baixa.",
|
||||
"dataChannelClosedDescriptionWithAudio": "O canal de ponte está em baixo, pelo que podem ocorrer interrupções no áudio e no vídeo.",
|
||||
"dataChannelClosedWithAudio": "A qualidade do áudio e do vídeo pode ser afetada",
|
||||
"desktopMutedRemotelyTitle": "A partilha do seu ecrã foi interrompida por {{participantDisplayName}}",
|
||||
"disabledIframe": "A incorporação destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos.",
|
||||
"disabledIframeSecondary": "A incorporação de {{domain}} destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos. Por favor, use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> para incorporação em produção!",
|
||||
"disabledIframeSecondaryNative": "A incorporação de {{domain}} destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos.",
|
||||
"disabledIframeSecondaryWeb": "A incorporação de {{domain}} destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos. Utilize <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> para incorporação em produção!",
|
||||
"disconnected": "desconectado",
|
||||
"displayNotifications": "Mostrar notificações para",
|
||||
"dontRemindMe": "Não me lembre",
|
||||
@@ -749,7 +815,10 @@
|
||||
"focusFail": "{{component}} não disponĩvel - tente em {{ms}} seg.",
|
||||
"gifsMenu": "GIPHY",
|
||||
"groupTitle": "Notificações",
|
||||
"hostAskedUnmute": "O moderador gostaria que você falasse",
|
||||
"hostAskedUnmute": "O moderador gostaria que participasse.",
|
||||
"invalidTenant": "Tenant inválido",
|
||||
"invalidTenantHyphenDescription": "O tenant que está a utilizar é inválido (começa ou termina com '-').",
|
||||
"invalidTenantLengthDescription": "O tenant que está a utilizar é demasiado longo.",
|
||||
"invitedOneMember": "{{displayName}} foi convidado",
|
||||
"invitedThreePlusMembers": "{{name}} e {{count}} outros foram convidados",
|
||||
"invitedTwoMembers": "{{first}} e {{second}} foram convidados",
|
||||
@@ -787,9 +856,9 @@
|
||||
"newDeviceAudioTitle": "Novo dispositivo de áudio detetado",
|
||||
"newDeviceCameraTitle": "Nova câmara detetada",
|
||||
"nextToSpeak": "É o próximo na fila para falar",
|
||||
"noiseSuppressionDesktopAudioDescription": "A supressão de ruído não pode ser ativada enquanto se partilha o áudio do ambiente de trabalho, por favor desative-o e tente novamente.",
|
||||
"noiseSuppressionFailedTitle": "Falha ao iniciar a supressão de ruído",
|
||||
"noiseSuppressionStereoDescription": "A supressão do ruído de áudio estéreo não é atualmente suportada.",
|
||||
"noiseSuppressionDesktopAudioDescription": "A supressão de ruído extra não pode ser ativada enquanto estiver a partilhar o áudio do ambiente de trabalho. Desative-a e tente novamente.",
|
||||
"noiseSuppressionFailedTitle": "Falha ao iniciar a supressão de ruído extra",
|
||||
"noiseSuppressionStereoDescription": "Atualmente, a supressão extra de ruído não é suportada com áudio estéreo.",
|
||||
"oldElectronClientDescription1": "Parece estar a utilizar uma versão antiga do cliente Jitsi Meet que tem vulnerabilidades de segurança conhecidas. Por favor, certifique-se de que actualiza a nossa ",
|
||||
"oldElectronClientDescription2": "compilação mais recente",
|
||||
"oldElectronClientDescription3": " agora!",
|
||||
@@ -798,7 +867,7 @@
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removido por outro participante",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) definido por outro participante",
|
||||
"raiseHandAction": "Levantar a mão",
|
||||
"raisedHand": "Gostaria de falar.",
|
||||
"raisedHand": "Gostaria de participar.",
|
||||
"raisedHands": "{{participantName}} e mais {{raisedHands}} pessoas",
|
||||
"reactionSounds": "Desactivar sons",
|
||||
"reactionSoundsForAll": "Desativar sons para todos",
|
||||
@@ -816,15 +885,17 @@
|
||||
"suggestRecordingDescription": "Gostaria de iniciar uma gravação?",
|
||||
"suggestRecordingTitle": "Gravar esta reunião",
|
||||
"unmute": "Ligar microfone",
|
||||
"unmuteScreen": "Iniciar partilha de ecrã",
|
||||
"unmuteVideo": "Ligar câmara",
|
||||
"videoMutedRemotelyDescription": "Pode sempre ligá-la novamente.",
|
||||
"videoMutedRemotelyTitle": "A sua câmara foi desligada pelo {{participantDisplayName}}.",
|
||||
"videoUnmuteBlockedDescription": "A operação de ligar a câmara e partilhar o ambiente de trabalho foi temporariamente bloqueada devido aos limites do sistema.",
|
||||
"videoUnmuteBlockedTitle": "Está bloqueado ligar a câmara e partilhar o ambiente de trabalho!",
|
||||
"viewLobby": "Ver sala de espera",
|
||||
"viewParticipants": "Ver participantes",
|
||||
"viewVisitors": "Ver visitantes",
|
||||
"viewVisitors": "Visualizar espectadores",
|
||||
"waitingParticipants": "{{waitingParticipants}} pessoas",
|
||||
"waitingVisitors": "Visitantes em fila de espera: {{waitingVisitors}}",
|
||||
"waitingVisitors": "Espectadores em fila de espera: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "A reunião ainda não está em direto!",
|
||||
"whiteboardLimitDescription": "Guarde o seu progresso, pois o limite de utilizadores será atingido em breve e o quadro branco será encerrado.",
|
||||
"whiteboardLimitTitle": "Utilização do quadro branco"
|
||||
@@ -833,14 +904,17 @@
|
||||
"actions": {
|
||||
"admit": "Aceitar",
|
||||
"admitAll": "Aceitar todos",
|
||||
"allow": "Permitir aos participantes:",
|
||||
"allow": "Permitir que os não moderadores:",
|
||||
"allowDesktop": "Permitir partilha de ecrã",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"askDesktop": "Pedir para partilhar o ecrã",
|
||||
"askUnmute": "Pedir para ligar o som",
|
||||
"audioModeration": "Ligar o microfone deles",
|
||||
"blockEveryoneMicCamera": "Bloquear o microfone e a câmara de todos",
|
||||
"breakoutRooms": "Salas simultâneas",
|
||||
"desktopModeration": "Iniciar partilha de ecrã",
|
||||
"goLive": "Aceder ao vivo",
|
||||
"invite": "Convidar alguém",
|
||||
"invite": "Convide alguém",
|
||||
"lowerAllHands": "Baixar todas as mãos",
|
||||
"lowerHand": "Baixar a mão",
|
||||
"moreModerationActions": "Mais opções de moderação",
|
||||
@@ -850,6 +924,8 @@
|
||||
"muteAll": "Silenciar todos",
|
||||
"muteEveryoneElse": "Silenciar todos os outros",
|
||||
"reject": "Rejeitar",
|
||||
"stopDesktop": "Parar a partilha de ecrã",
|
||||
"stopEveryonesDesktop": "Interromper a partilha de ecrã de todos",
|
||||
"stopEveryonesVideo": "Desligar a câmara de todos",
|
||||
"stopVideo": "Desligar a câmara",
|
||||
"unblockEveryoneMicCamera": "Desbloquear o microfone e a câmara de todos",
|
||||
@@ -859,12 +935,15 @@
|
||||
"headings": {
|
||||
"lobby": "Sala de espera ({{count}})",
|
||||
"participantsList": "Participantes da reunião ({{count}})",
|
||||
"viewerRequests": "Pedidos dos espectadores {{count}}",
|
||||
"visitorInQueue": " (à espera {{count}})",
|
||||
"visitorRequests": " (pedidos {{count}})",
|
||||
"visitors": "Visitantes ({{count}})",
|
||||
"visitors": "Espectadores ({{count}})",
|
||||
"visitorsList": "Espectadores ({{count}})",
|
||||
"waitingLobby": "Aguardam na sala de espera ({{count}})"
|
||||
},
|
||||
"search": "Pesquisar participantes",
|
||||
"searchDescription": "Comece a digitar para filtrar os participantes",
|
||||
"title": "Participantes"
|
||||
},
|
||||
"passwordDigitsOnly": "Até {{number}} dígitos",
|
||||
@@ -901,7 +980,7 @@
|
||||
},
|
||||
"results": {
|
||||
"changeVote": "Mudar o voto",
|
||||
"empty": "Ainda não há sondagens na reunião. Comece aqui uma sondagem!",
|
||||
"empty": "Ainda não há sondagens na reunião.",
|
||||
"hideDetailedResults": "Ocultar detalhes",
|
||||
"showDetailedResults": "Mostrar detalhes",
|
||||
"vote": "Voto"
|
||||
@@ -919,9 +998,11 @@
|
||||
"configuringDevices": "A configurar os dispositivos…",
|
||||
"connectedWithAudioQ": "Está ligado com áudio?",
|
||||
"connection": {
|
||||
"failed": "Falha no teste de ligação!",
|
||||
"good": "A sua ligação à Internet parece boa!",
|
||||
"nonOptimal": "A sua ligação à Internet não é óptima",
|
||||
"poor": "Tem uma má ligação à Internet"
|
||||
"poor": "Tem uma ligação à Internet fraca",
|
||||
"running": "A realizar teste de ligação..."
|
||||
},
|
||||
"connectionDetails": {
|
||||
"audioClipping": "Prevemos que o seu áudio tenha cortes.",
|
||||
@@ -930,6 +1011,7 @@
|
||||
"goodQuality": "Fantástico! A qualidade dos seus meios de comunicação vai ser óptima.",
|
||||
"noMediaConnectivity": "Não foi possível encontrar uma forma de estabelecer a conectividade dos meios de comunicação para este teste. Isto é tipicamente causado por uma firewall ou NAT.",
|
||||
"noVideo": "Prevemos que o seu vídeo seja terrível.",
|
||||
"testFailed": "O teste de ligação encontrou problemas inesperados, mas isso pode não afetar a sua experiência.",
|
||||
"undetectable": "Se mesmo assim não conseguir fazer chamadas no browser, recomendamos que se certifique de que os seus altifalantes, microfone e câmara estão devidamente configurados, que concedeu ao seu browser direitos de utilização do seu microfone e câmara, e que a versão do seu browser está actualizada. Se mesmo assim tiver problemas em telefonar, deverá contactar o criador da aplicação web.",
|
||||
"veryPoorConnection": "Prevemos que a qualidade da sua chamada seja realmente terrível.",
|
||||
"videoFreezing": "Prevemos que o seu vídeo congele, fique preto, e seja pixelizado.",
|
||||
@@ -958,7 +1040,7 @@
|
||||
"joinWithoutAudio": "Entrar sem áudio",
|
||||
"keyboardShortcuts": "Ativar os atalhos de teclado",
|
||||
"linkCopied": "Link copiado para a área de transferência",
|
||||
"lookGood": "Tudo está a funcionar corretamente",
|
||||
"lookGood": "Os seus dispositivos estão a funcionar corretamente",
|
||||
"or": "ou",
|
||||
"premeeting": "Pré-reunião",
|
||||
"proceedAnyway": "Continuar na mesma",
|
||||
@@ -1044,6 +1126,7 @@
|
||||
"onBy": "{{name}} iniciou a gravação",
|
||||
"onlyRecordSelf": "Gravar apenas as minhas transmissões áudio e vídeo",
|
||||
"pending": "Preparando para gravar a reunião…",
|
||||
"policyError": "Tentou iniciar uma gravação muito rapidamente. Por favor, tente novamente mais tarde!",
|
||||
"recordAudioAndVideo": "Gravar áudio e vídeo",
|
||||
"recordTranscription": "Gravar transcrições",
|
||||
"saveLocalRecording": "Guardar ficheiro de gravação localmente (Beta)",
|
||||
@@ -1087,11 +1170,13 @@
|
||||
"signedIn": "Atualmente a aceder a eventos de calendário por {{email}}. Clique no botão Desconectar abaixo para parar de aceder a eventos de calendário.",
|
||||
"title": "Calendário"
|
||||
},
|
||||
"chatWithPermissions": "O chat requer permissão",
|
||||
"desktopShareFramerate": "Taxa de fotogramas para partilha do ambiente de trabalho",
|
||||
"desktopShareHighFpsWarning": "Uma taxa de fotogramas mais elevada para a partilha do ambiente de trabalho pode afectar a sua largura de banda. É necessário reiniciar a partilha de ecrã para que as novas definições entrem em vigor.",
|
||||
"desktopShareWarning": "É necessário reiniciar a partilha do ecrã para que as novas definições entrem em vigor.",
|
||||
"devices": "Dispositivos",
|
||||
"followMe": "Todos me seguem",
|
||||
"followMeRecorder": "O gravador segue-me",
|
||||
"framesPerSecond": "fotogramas-por-segundo",
|
||||
"incomingMessage": "Receber uma mensagem",
|
||||
"language": "Idioma",
|
||||
@@ -1115,6 +1200,7 @@
|
||||
"selectMic": "Microfone",
|
||||
"selfView": "Autovisualização",
|
||||
"shortcuts": "Atalhos",
|
||||
"showSubtitlesOnStage": "Mostrar legendas",
|
||||
"speakers": "Altifalantes",
|
||||
"startAudioMuted": "Todos começam com microfone desligado",
|
||||
"startReactionsMuted": "Todos começam com os sons de reação desativados",
|
||||
@@ -1168,11 +1254,13 @@
|
||||
"fearful": "Temeroso",
|
||||
"happy": "Feliz",
|
||||
"hours": "{{count}}h",
|
||||
"labelTooltip": "Número de participantes: {{count}}",
|
||||
"minutes": "{{count}}m",
|
||||
"name": "Nome",
|
||||
"neutral": "Neutro",
|
||||
"sad": "Triste",
|
||||
"search": "Pesquisar",
|
||||
"searchDescription": "Comece a digitar para filtrar os participantes",
|
||||
"searchHint": "Pesquisar participantes",
|
||||
"seconds": "{{count}}s",
|
||||
"speakerStats": "Estatísticas dos Participantes",
|
||||
@@ -1209,6 +1297,7 @@
|
||||
"closeChat": "Fechar chat",
|
||||
"closeMoreActions": "Fechar menu de mais ações",
|
||||
"closeParticipantsPane": "Fechar painel de participantes",
|
||||
"closedCaptions": "Legendas ocultas",
|
||||
"collapse": "Colapsar",
|
||||
"document": "Mudar para documento partilhado",
|
||||
"documentClose": "Fechar documento partilhado",
|
||||
@@ -1238,6 +1327,7 @@
|
||||
"lobbyButton": "Ativar/desativar sala de espera",
|
||||
"localRecording": "Mudar os controlos locais de gravação",
|
||||
"lockRoom": "Mudar palavra-chave de reunião",
|
||||
"love": "Coração",
|
||||
"lowerHand": "Baixar a mão",
|
||||
"moreActions": "Mais ações",
|
||||
"moreActionsMenu": "Menu de mais ações",
|
||||
@@ -1248,13 +1338,14 @@
|
||||
"muteEveryoneElsesVideo": "Parar o vídeo de todos os outros",
|
||||
"muteEveryonesVideo": "Parar o vídeo de todos",
|
||||
"muteGUMPending": "A ligar o seu microfone",
|
||||
"noiseSuppression": "Supressão de ruído",
|
||||
"noiseSuppression": "Supressão extra de ruído (BETA)",
|
||||
"openChat": "Abrir chat",
|
||||
"participants": "Abrir painel de participantes",
|
||||
"participants": "Abrir painel de participantes. {{participantsCount}} participantes",
|
||||
"pip": "Mudar para o modo Picture-in-Picture",
|
||||
"privateMessage": "Enviar mensagem privada",
|
||||
"profile": "Editar o seu perfil",
|
||||
"raiseHand": "Levantar a mão",
|
||||
"react": "Reações às mensagens",
|
||||
"reactions": "Reações",
|
||||
"reactionsMenu": "Menu de reações",
|
||||
"recording": "Mudar gravação",
|
||||
@@ -1297,14 +1388,15 @@
|
||||
"closeChat": "Fechar chat",
|
||||
"closeParticipantsPane": "Fechar painel de participantes",
|
||||
"closeReactionsMenu": "Fechar menu de reações",
|
||||
"disableNoiseSuppression": "Desativar a supressão de ruído",
|
||||
"closedCaptions": "Legendas ocultas",
|
||||
"disableNoiseSuppression": "Desativar supressão de ruído extra (BETA)",
|
||||
"disableReactionSounds": "Pode desactivar os sons de reacção para esta reunião",
|
||||
"documentClose": "Fechar documento partilhado",
|
||||
"documentOpen": "Abrir documento partilhado",
|
||||
"download": "Descarregar as nossas aplicações",
|
||||
"e2ee": "Criptografia ponta a ponta",
|
||||
"embedMeeting": "Incorporar reunião",
|
||||
"enableNoiseSuppression": "Ativar a supressão de ruído",
|
||||
"enableNoiseSuppression": "Ativar supressão extra de ruído (BETA)",
|
||||
"endConference": "Terminar reunião para todos",
|
||||
"enterFullScreen": "Ver em ecrã completo",
|
||||
"enterTileView": "Ver em quadrícula",
|
||||
@@ -1326,6 +1418,7 @@
|
||||
"lobbyButtonEnable": "Ativar sala de espera",
|
||||
"login": "Iniciar sessão",
|
||||
"logout": "Terminar sessão",
|
||||
"love": "Coração",
|
||||
"lowerYourHand": "Baixar a mão",
|
||||
"moreActions": "Mais ações",
|
||||
"moreOptions": "Mais opções",
|
||||
@@ -1338,7 +1431,7 @@
|
||||
"noAudioSignalDialInDesc": "Também pode marcar usando:",
|
||||
"noAudioSignalDialInLinkDesc": "Números de marcação",
|
||||
"noAudioSignalTitle": "Não há nenhuma entrada vinda do seu microfone!",
|
||||
"noiseSuppression": "Supressão de ruído",
|
||||
"noiseSuppression": "Supressão extra de ruído (BETA)",
|
||||
"noisyAudioInputDesc": "Parece que o seu microfone está a fazer barulho, por favor considere silenciar ou mudar de dispositivo.",
|
||||
"noisyAudioInputTitle": "Seu microfone parece estar barulhento!",
|
||||
"openChat": "Abrir chat",
|
||||
@@ -1351,6 +1444,7 @@
|
||||
"raiseYourHand": "Levantar a mão",
|
||||
"reactionBoo": "Enviar reação de vaia",
|
||||
"reactionClap": "Enviar reação de aplausos",
|
||||
"reactionHeart": "Enviar reação com coração",
|
||||
"reactionLaugh": "Enviar reação de risos",
|
||||
"reactionLike": "Enviar reação de aprovado",
|
||||
"reactionSilence": "Enviar reação de silêncio",
|
||||
@@ -1384,15 +1478,19 @@
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Iniciar/parar legendas",
|
||||
"expandedLabel": "Transcrição ativada",
|
||||
"failedToStart": "Transcrição falhou ao iniciar",
|
||||
"labelToolTip": "A reunião esta sendo transcrita",
|
||||
"failed": "Falha na transcrição",
|
||||
"labelTooltip": "Esta reunião está a ser transcrita.",
|
||||
"labelTooltipExtra": "Além disso, uma transcrição estará disponível posteriormente.",
|
||||
"openClosedCaptions": "Abrir legendas ocultas",
|
||||
"original": "Original",
|
||||
"sourceLanguageDesc": "Atualmente a língua da reunião está definida para <b>{{sourceLanguage}}</b>. <br/> Pode alterá-la a partir ",
|
||||
"sourceLanguageHere": "daqui",
|
||||
"start": "Exibir legendas",
|
||||
"stop": "Não exibir legendas",
|
||||
"subtitles": "Legendas",
|
||||
"subtitlesOff": "Desligado",
|
||||
"tr": "TR"
|
||||
"tr": "TR",
|
||||
"translateTo": "Traduzir para"
|
||||
},
|
||||
"unpinParticipant": "{{participantName}} - Desafixar",
|
||||
"userMedia": {
|
||||
@@ -1424,7 +1522,7 @@
|
||||
"ldTooltip": "Ver vídeo em baixa definição",
|
||||
"lowDefinition": "Baixa definição (LD)",
|
||||
"performanceSettings": "Definições de desempenho",
|
||||
"recording": "Gravação em curso",
|
||||
"recording": "Esta reunião está a ser gravada.",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Ver vídeo em definição padrão",
|
||||
"standardDefinition": "Definição padrão",
|
||||
@@ -1432,8 +1530,10 @@
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Informações sobre a ligação",
|
||||
"demote": "Passar a visitante",
|
||||
"demote": "Passar a espectador",
|
||||
"domute": "Sem som",
|
||||
"domuteDesktop": "Parar a partilha de ecrã",
|
||||
"domuteDesktopOfOthers": "Interromper a partilha de ecrã para todos os outros",
|
||||
"domuteOthers": "Silenciar todos os outros",
|
||||
"domuteVideo": "Desativar a câmara",
|
||||
"domuteVideoOfOthers": "Desactivar a câmara de todos os outros",
|
||||
@@ -1484,21 +1584,21 @@
|
||||
"webAssemblyWarningDescription": "WebAssembly desactivado ou não suportado por este navegador"
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(visitante)",
|
||||
"chatIndicator": "(espectador)",
|
||||
"joinMeeting": {
|
||||
"description": "Atualmente, é um observador nesta conferência.",
|
||||
"description": "Atualmente, é um espectador nesta conferência.",
|
||||
"raiseHand": "Levantar a mão",
|
||||
"title": "Participar na reunião",
|
||||
"wishToSpeak": "Se deseja intervir, levante a mão e aguarde a aprovação do moderador."
|
||||
},
|
||||
"labelTooltip": "Número de visitantes: {{count}}",
|
||||
"labelTooltip": "Número de espectadores: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Enviado aqui pelo {{actor}}, levante a mão para participar",
|
||||
"noMainParticipantsDescription": "Um participante precisa de iniciar a reunião. Tente novamente daqui a pouco.",
|
||||
"noMainParticipantsTitle": "Esta reunião ainda não começou.",
|
||||
"noVisitorLobby": "Não é possível aderir enquanto houver uma sala de espera activada para a reunião.",
|
||||
"notAllowedPromotion": "É necessário que um participante autorize primeiro o seu pedido.",
|
||||
"title": "É um visitante na reunião"
|
||||
"title": "É um espectador na reunião"
|
||||
},
|
||||
"waitingMessage": "Participará na reunião assim que esta estiver em direto!"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -331,11 +331,11 @@
|
||||
"internalError": "Ett fel uppstod. Fel: {{error}}",
|
||||
"internalErrorTitle": "Internt fel",
|
||||
"kickMessage": "Du kan kontakta {{participantDisplayName}} för mer information.",
|
||||
"kickParticipantButton": "Sparka ut",
|
||||
"kickParticipantDialog": "Vill du sparka ut den här deltagaren?",
|
||||
"kickParticipantButton": "Ta bort från mötet",
|
||||
"kickParticipantDialog": "Vill du ta bort denna deltagaren från mötet?",
|
||||
"kickParticipantTitle": "Tysta deltagaren?",
|
||||
"kickSystemTitle": "Aj! du blev utsparkad ur mötet",
|
||||
"kickTitle": "Aj! {{participantDisplayName}} sparkade ut dig ur mötet",
|
||||
"kickSystemTitle": "Du har blivit borttagen från mötet",
|
||||
"kickTitle": "{{participantDisplayName}} tog bort dig från mötet",
|
||||
"linkMeeting": "Länka möte",
|
||||
"linkMeetingTitle": "Länka möte till Salesforce",
|
||||
"liveStreaming": "Streama",
|
||||
@@ -763,7 +763,7 @@
|
||||
"invitedThreePlusMembers": "{{name}} och {{count}} andra har bjudits in",
|
||||
"invitedTwoMembers": "{{first}} och {{second}} har bjudits in",
|
||||
"joinMeeting": "Delta",
|
||||
"kickParticipant": "{{kicked}} sparkades ut av {{kicker}}",
|
||||
"kickParticipant": "{{kicked}} togs bort av {{kicker}}",
|
||||
"leftOneMember": "{{name}} lämnade mötet",
|
||||
"leftThreePlusMembers": "{{name}} och många andra lämnade mötet",
|
||||
"leftTwoMembers": "{{first}} och {{second}} lämnade mötet",
|
||||
@@ -1245,7 +1245,7 @@
|
||||
"help": "Hjälp",
|
||||
"hideWhiteboard": "Dölj whiteboard",
|
||||
"invite": "Bjud in personer",
|
||||
"kick": "Sparka ut deltagare",
|
||||
"kick": "Ta bort deltagare",
|
||||
"laugh": "Skratta",
|
||||
"leaveConference": "Lämna mötet",
|
||||
"like": "Tummen upp",
|
||||
@@ -1459,7 +1459,7 @@
|
||||
"flip": "Vänd",
|
||||
"grantModerator": "Godkänn moderator",
|
||||
"hideSelfView": "Dölj självvyn",
|
||||
"kick": "Sparka ut",
|
||||
"kick": "Ta bort",
|
||||
"mirrorVideo": "Spegelvänd video",
|
||||
"moderator": "Moderator",
|
||||
"mute": "Deltagaren har avstängd mikrofon",
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"error": "Error: your message was not sent. Reason: {{error}}",
|
||||
"everyone": "Everyone",
|
||||
"fieldPlaceHolder": "Aa",
|
||||
"guestsChatIndicator": "(guest)",
|
||||
"lobbyChatMessageTo": "Lobby chat message to {{recipient}}",
|
||||
"message": "Message",
|
||||
"messageAccessibleTitle": "{{user}} says:",
|
||||
@@ -566,6 +567,7 @@
|
||||
"downloadFailedDescription": "Please try again.",
|
||||
"downloadFailedTitle": "Download failed",
|
||||
"downloadFile": "Download",
|
||||
"downloadStarted": "File download started",
|
||||
"dragAndDrop": "Drag and drop files here or anywhere on screen",
|
||||
"fileAlreadyUploaded": "File has already been uploaded to this meeting.",
|
||||
"fileTooLargeDescription": "Please make sure the file does not exceed {{ maxFileSize }}.",
|
||||
@@ -573,6 +575,7 @@
|
||||
"fileUploadProgress": "File upload progress",
|
||||
"fileUploadedSuccessfully": "File uploaded successfully",
|
||||
"removeFile": "Remove",
|
||||
"removeFileSuccess": "File removed successfully",
|
||||
"uploadFailedDescription": "Please try again.",
|
||||
"uploadFailedTitle": "Upload failed",
|
||||
"uploadFile": "Share file"
|
||||
@@ -862,6 +865,7 @@
|
||||
"oldElectronClientDescription1": "You appear to be using an old version of the Jitsi Meet client which has known security vulnerabilities. Please make sure you update to our ",
|
||||
"oldElectronClientDescription2": "latest build",
|
||||
"oldElectronClientDescription3": " now!",
|
||||
"openChat": "Open chat",
|
||||
"participantWantsToJoin": "Wants to join the meeting",
|
||||
"participantsWantToJoin": "Want to join the meeting",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
|
||||
@@ -1376,6 +1380,20 @@
|
||||
"videounmute": "Start camera"
|
||||
},
|
||||
"addPeople": "Add people to your call",
|
||||
"advancedAudioSettings": {
|
||||
"aec": {
|
||||
"label": "Acoustic echo cancellation"
|
||||
},
|
||||
"agc": {
|
||||
"label": "Automatic gain control"
|
||||
},
|
||||
"ns": {
|
||||
"label": "Noise suppression"
|
||||
},
|
||||
"stereo": {
|
||||
"label": "Stereo"
|
||||
}
|
||||
},
|
||||
"audioOnlyOff": "Disable low bandwidth mode",
|
||||
"audioOnlyOn": "Enable low bandwidth mode",
|
||||
"audioRoute": "Select the sound device",
|
||||
@@ -1447,6 +1465,7 @@
|
||||
"reactionHeart": "Send heart reaction",
|
||||
"reactionLaugh": "Send laugh reaction",
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionLove": "Send love reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"reactions": "Reactions",
|
||||
@@ -1598,6 +1617,8 @@
|
||||
"noMainParticipantsTitle": "This meeting hasn't started yet.",
|
||||
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
|
||||
"notAllowedPromotion": "A participant needs to allow your request first.",
|
||||
"requestToJoin": "Hand Raised",
|
||||
"requestToJoinDescription": "Your request was sent to the moderators. Hang tight!",
|
||||
"title": "You are a viewer in the meeting"
|
||||
},
|
||||
"waitingMessage": "You'll join the meeting as soon as it is live!"
|
||||
|
||||
@@ -138,7 +138,7 @@ import {
|
||||
ENDPOINT_TEXT_MESSAGE_NAME
|
||||
} from './constants';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('api:core');
|
||||
|
||||
/**
|
||||
* List of the available commands.
|
||||
|
||||
2
modules/API/external/functions.js
vendored
2
modules/API/external/functions.js
vendored
@@ -1,6 +1,6 @@
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('api:external');
|
||||
|
||||
/**
|
||||
* Returns Promise that resolves with result an list of available devices.
|
||||
|
||||
@@ -28,7 +28,7 @@ import EtherpadManager from './etherpad/Etherpad';
|
||||
import UIUtil from './util/UIUtil';
|
||||
import VideoLayout from './videolayout/VideoLayout';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('ui:core');
|
||||
|
||||
let etherpadManager;
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ import AudioLevels from '../audio_levels/AudioLevels';
|
||||
|
||||
import { VIDEO_CONTAINER_TYPE, VideoContainer } from './VideoContainer';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('ui:videolayout');
|
||||
|
||||
const DESKTOP_CONTAINER_TYPE = 'desktop';
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export const VIDEO_CONTAINER_TYPE = 'camera';
|
||||
// Corresponds to animation duration from the animatedFadeIn and animatedFadeOut CSS classes.
|
||||
const FADE_DURATION_MS = 300;
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('ui:VideoContainer');
|
||||
|
||||
/**
|
||||
* List of container events that we are going to process for the large video.
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import LargeVideoManager from './LargeVideoManager';
|
||||
import { VIDEO_CONTAINER_TYPE } from './VideoContainer';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
const logger = Logger.getLogger('ui:VideoLayout');
|
||||
let largeVideo;
|
||||
|
||||
const VideoLayout = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const logger = require('@jitsi/logger').getLogger(__filename);
|
||||
const logger = require('@jitsi/logger').getLogger('app:utils');
|
||||
|
||||
/**
|
||||
* Manages a queue of functions where the current function in progress will
|
||||
|
||||
182
package-lock.json
generated
182
package-lock.json
generated
@@ -20,7 +20,7 @@
|
||||
"@giphy/react-native-sdk": "4.1.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/rnnoise-wasm": "0.2.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",
|
||||
@@ -51,6 +51,7 @@
|
||||
"clipboard-copy": "4.0.1",
|
||||
"clsx": "1.1.1",
|
||||
"dayjs": "1.11.13",
|
||||
"dompurify": "3.2.6",
|
||||
"dropbox": "10.7.0",
|
||||
"focus-visible": "5.1.0",
|
||||
"glob": "11.0.3",
|
||||
@@ -65,7 +66,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/v2044.0.0+437abe32/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
@@ -103,6 +104,7 @@
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "124.0.4",
|
||||
"react-native-webview": "13.13.5",
|
||||
"react-native-worklets-core": "https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
"react-textarea-autosize": "8.3.0",
|
||||
@@ -124,6 +126,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.9",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
||||
"@babel/plugin-transform-private-methods": "7.25.9",
|
||||
"@babel/preset-env": "7.25.9",
|
||||
"@babel/preset-react": "7.25.9",
|
||||
@@ -1000,6 +1004,43 @@
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-proposal-nullish-coalescing-operator": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
|
||||
"integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
|
||||
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.18.6",
|
||||
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-proposal-optional-chaining": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
|
||||
"integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
|
||||
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.20.2",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
|
||||
"@babel/plugin-syntax-optional-chaining": "^7.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-proposal-private-property-in-object": {
|
||||
"version": "7.21.0-placeholder-for-preset-env.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
|
||||
@@ -1839,13 +1880,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-optional-chaining": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz",
|
||||
"integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
|
||||
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -4520,9 +4561,9 @@
|
||||
"integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ=="
|
||||
},
|
||||
"node_modules/@jitsi/logger": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
|
||||
"integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.1.1.tgz",
|
||||
"integrity": "sha512-adMtODSXvYJtqfRuDwxN1hNWPJ4gvp868G7QXII3ajSgPaE2mMcbLYRHy6mZZlQJGnxr0dDaivOrvV6CikHZNQ=="
|
||||
},
|
||||
"node_modules/@jitsi/precall-test": {
|
||||
"version": "1.0.6",
|
||||
@@ -7957,6 +7998,13 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
|
||||
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/unorm": {
|
||||
"version": "1.3.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/unorm/-/unorm-1.3.28.tgz",
|
||||
@@ -12437,6 +12485,15 @@
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
|
||||
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
@@ -18203,12 +18260,12 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2044.0.0+437abe32/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-LdYhBImyB3q7HV3KlJdww1Qw+Ke2vJOir32+8dYxJl9QdrQ5HedRXoF/+FF9+LdDIy5BG1o8M+To4/poYKLYTw==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-mmdvU3B7dYWV6RcfFW/BWJkaNR3LLCG3wHcBYT8WZKVbh00vu4SZJKwtjiVrw2aIP1FcXoD3T0RJ5+IaGROU3A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "2.4.6",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/precall-test": "1.0.6",
|
||||
"@jitsi/rtcstats": "9.7.0",
|
||||
"@testrtc/watchrtc-sdk": "1.38.2",
|
||||
@@ -18235,6 +18292,11 @@
|
||||
"ua-parser-js": "1.0.35"
|
||||
}
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/@jitsi/logger": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
|
||||
"integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/@jitsi/rtcstats": {
|
||||
"version": "9.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.7.0.tgz",
|
||||
@@ -22474,6 +22536,19 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-worklets-core": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "git+ssh://git@github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
|
||||
"integrity": "sha512-SW47DvuNLjhoj8PJK8haq0B/69//ChG+/3WyK9NkhwUSM1kqZLle1O7SqGeLFp2f37qyBXH5X+cXPDaabJ8CHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"string-hash-64": "^1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-youtube-iframe": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.3.0.tgz",
|
||||
@@ -24457,6 +24532,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/string-hash-64": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz",
|
||||
"integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-replace-to-array": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz",
|
||||
@@ -27652,6 +27733,27 @@
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
|
||||
"integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.18.6",
|
||||
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-optional-chaining": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
|
||||
"integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.20.2",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
|
||||
"@babel/plugin-syntax-optional-chaining": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-private-property-in-object": {
|
||||
"version": "7.21.0-placeholder-for-preset-env.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
|
||||
@@ -28136,12 +28238,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-optional-chaining": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz",
|
||||
"integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
|
||||
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-parameters": {
|
||||
@@ -29958,9 +30060,9 @@
|
||||
}
|
||||
},
|
||||
"@jitsi/logger": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
|
||||
"integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.1.1.tgz",
|
||||
"integrity": "sha512-adMtODSXvYJtqfRuDwxN1hNWPJ4gvp868G7QXII3ajSgPaE2mMcbLYRHy6mZZlQJGnxr0dDaivOrvV6CikHZNQ=="
|
||||
},
|
||||
"@jitsi/precall-test": {
|
||||
"version": "1.0.6",
|
||||
@@ -32389,6 +32491,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
|
||||
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
|
||||
},
|
||||
"@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"optional": true
|
||||
},
|
||||
"@types/unorm": {
|
||||
"version": "1.3.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/unorm/-/unorm-1.3.28.tgz",
|
||||
@@ -35531,6 +35639,14 @@
|
||||
"domelementtype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
|
||||
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
|
||||
"requires": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
@@ -39600,11 +39716,11 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2044.0.0+437abe32/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-LdYhBImyB3q7HV3KlJdww1Qw+Ke2vJOir32+8dYxJl9QdrQ5HedRXoF/+FF9+LdDIy5BG1o8M+To4/poYKLYTw==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-mmdvU3B7dYWV6RcfFW/BWJkaNR3LLCG3wHcBYT8WZKVbh00vu4SZJKwtjiVrw2aIP1FcXoD3T0RJ5+IaGROU3A==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.4.6",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/precall-test": "1.0.6",
|
||||
"@jitsi/rtcstats": "9.7.0",
|
||||
"@testrtc/watchrtc-sdk": "1.38.2",
|
||||
@@ -39630,6 +39746,11 @@
|
||||
"ua-parser-js": "1.0.35"
|
||||
}
|
||||
},
|
||||
"@jitsi/logger": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
|
||||
"integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
|
||||
},
|
||||
"@jitsi/rtcstats": {
|
||||
"version": "9.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.7.0.tgz",
|
||||
@@ -42634,6 +42755,14 @@
|
||||
"invariant": "2.2.4"
|
||||
}
|
||||
},
|
||||
"react-native-worklets-core": {
|
||||
"version": "git+ssh://git@github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
|
||||
"integrity": "sha512-SW47DvuNLjhoj8PJK8haq0B/69//ChG+/3WyK9NkhwUSM1kqZLle1O7SqGeLFp2f37qyBXH5X+cXPDaabJ8CHA==",
|
||||
"from": "react-native-worklets-core@https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
|
||||
"requires": {
|
||||
"string-hash-64": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"react-native-youtube-iframe": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.3.0.tgz",
|
||||
@@ -43991,6 +44120,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"string-hash-64": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz",
|
||||
"integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw=="
|
||||
},
|
||||
"string-replace-to-array": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@giphy/react-native-sdk": "4.1.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
"@jitsi/rnnoise-wasm": "0.2.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",
|
||||
@@ -57,6 +57,7 @@
|
||||
"clipboard-copy": "4.0.1",
|
||||
"clsx": "1.1.1",
|
||||
"dayjs": "1.11.13",
|
||||
"dompurify": "3.2.6",
|
||||
"dropbox": "10.7.0",
|
||||
"focus-visible": "5.1.0",
|
||||
"glob": "11.0.3",
|
||||
@@ -71,7 +72,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/v2044.0.0+437abe32/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
@@ -109,6 +110,7 @@
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "124.0.4",
|
||||
"react-native-webview": "13.13.5",
|
||||
"react-native-worklets-core": "https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
"react-textarea-autosize": "8.3.0",
|
||||
@@ -130,6 +132,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.9",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
||||
"@babel/plugin-transform-private-methods": "7.25.9",
|
||||
"@babel/preset-env": "7.25.9",
|
||||
"@babel/preset-react": "7.25.9",
|
||||
|
||||
@@ -124,12 +124,11 @@ repositories {
|
||||
|
||||
|
||||
dependencies {
|
||||
//noinspection GradleDynamicVersion
|
||||
// noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+"
|
||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
// From node_modules
|
||||
// From node_modules
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
|
||||
@@ -22,7 +22,6 @@ public class JitsiMeetReactNativePackage implements ReactPackage {
|
||||
new AppInfoModule(reactContext),
|
||||
new AudioModeModule(reactContext),
|
||||
new JMOngoingConferenceModule(reactContext),
|
||||
new JavaScriptSandboxModule(reactContext),
|
||||
new LocaleDetector(reactContext),
|
||||
new LogBridgeModule(reactContext),
|
||||
new PictureInPictureModule(reactContext),
|
||||
|
||||
@@ -85,7 +85,12 @@
|
||||
"react-native-video": "0.0.0",
|
||||
"react-native-watch-connectivity": "0.0.0",
|
||||
"react-native-webrtc": "0.0.0",
|
||||
"react-native-webview": "0.0.0"
|
||||
"react-native-webview": "0.0.0",
|
||||
"react-native-worklets-core": "0.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "0.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "0.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node sdk_instructions.js",
|
||||
|
||||
8
react-native-sdk/prepare_sdk.js
vendored
8
react-native-sdk/prepare_sdk.js
vendored
@@ -100,10 +100,6 @@ fs.copyFileSync(
|
||||
`${iosSrcPath}/InfoPlistUtil.h`,
|
||||
`${iosDestPath}/InfoPlistUtil.h`
|
||||
);
|
||||
fs.copyFileSync(
|
||||
`${iosSrcPath}/JavaScriptSandbox.m`,
|
||||
`${iosDestPath}/JavaScriptSandbox.m`
|
||||
);
|
||||
fs.copyFileSync(
|
||||
`${iosSrcPath}/JitsiAudioSession.m`,
|
||||
`${iosDestPath}/JitsiAudioSession.m`
|
||||
@@ -184,10 +180,6 @@ fs.copyFileSync(
|
||||
`${androidSourcePath}/ConnectionService.java`,
|
||||
`${androidTargetPath}/ConnectionService.java`
|
||||
);
|
||||
fs.copyFileSync(
|
||||
`${androidSourcePath}/JavaScriptSandboxModule.java`,
|
||||
`${androidTargetPath}/JavaScriptSandboxModule.java`
|
||||
);
|
||||
fs.copyFileSync(
|
||||
`${androidSourcePath}/LocaleDetector.java`,
|
||||
`${androidTargetPath}/LocaleDetector.java`
|
||||
|
||||
18
react-native-sdk/update_dependencies.js
vendored
18
react-native-sdk/update_dependencies.js
vendored
@@ -60,12 +60,12 @@ This is now set on your end.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
packageJSON.devDependencies = packageJSON.devDependencies || {};
|
||||
|
||||
packageJSON.overrides = packageJSON.overrides || {};
|
||||
|
||||
for (const key in RNSDKpackageJSON.overrides) {
|
||||
if (!packageJSON.overrides.hasOwnProperty(key)) {
|
||||
packageJSON.overrides[key] = RNSDKpackageJSON.overrides[key];
|
||||
for (const key in RNSDKpackageJSON.devDependencies) {
|
||||
if (!packageJSON.devDependencies.hasOwnProperty(key)) {
|
||||
packageJSON.devDependencies[key] = RNSDKpackageJSON.devDependencies[key];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,14 @@ This is now set on your end.`
|
||||
|
||||
return item;
|
||||
}, {});
|
||||
|
||||
packageJSON.devDependencies = Object.keys(packageJSON.devDependencies)
|
||||
.sort()
|
||||
.reduce((item, itemKey) => {
|
||||
item[itemKey] = packageJSON.devDependencies[itemKey];
|
||||
|
||||
return item;
|
||||
}, {});
|
||||
|
||||
fs.writeFileSync(pathToPackageJSON, JSON.stringify(packageJSON, null, 2));
|
||||
|
||||
|
||||
7
react-native-sdk/update_sdk_dependencies.js
vendored
7
react-native-sdk/update_sdk_dependencies.js
vendored
@@ -26,6 +26,13 @@ function mergeDependencyVersions() {
|
||||
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Updates SDK dev dependencies(used by react-native-worklets-core lib. babel plugin)
|
||||
for (const key in packageJSON.devDependencies) {
|
||||
if (SDKPackageJSON.devDependencies.hasOwnProperty(key)) {
|
||||
SDKPackageJSON.devDependencies[key] = packageJSON.devDependencies[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Set RN peer dependency.
|
||||
const rnVersion = semver.parse(packageJSON.dependencies['react-native']);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/analytics');
|
||||
export default getLogger('app:analytics');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/app');
|
||||
export default getLogger('app:core');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/authentication');
|
||||
export default getLogger('app:authentication');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/app');
|
||||
export default getLogger('app:base-app');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/audio-only');
|
||||
export default getLogger('app:audio-only');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/conference');
|
||||
export default getLogger('app:base-conference');
|
||||
|
||||
@@ -26,7 +26,7 @@ import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEff
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { overwriteConfig } from '../config/actions';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, CONNECTION_WILL_CONNECT } from '../connection/actionTypes';
|
||||
import { connectionDisconnected, disconnect } from '../connection/actions';
|
||||
import { connect, connectionDisconnected, disconnect, setPreferVisitor } from '../connection/actions';
|
||||
import { validateJwt } from '../jwt/functions';
|
||||
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
@@ -78,6 +78,11 @@ import { IConferenceMetadata } from './reducer';
|
||||
*/
|
||||
let beforeUnloadHandler: ((e?: any) => void) | undefined;
|
||||
|
||||
/**
|
||||
* A simple flag to avoid retrying more than once to join as a visitor when hitting max occupants reached.
|
||||
*/
|
||||
let retryAsVisitorOnMaxError = true;
|
||||
|
||||
/**
|
||||
* Implements the middleware of the feature base/conference.
|
||||
*
|
||||
@@ -202,11 +207,20 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
break;
|
||||
}
|
||||
case JitsiConferenceErrors.CONFERENCE_MAX_USERS: {
|
||||
dispatch(showErrorNotification({
|
||||
hideErrorSupportLink: true,
|
||||
descriptionKey: 'dialog.maxUsersLimitReached',
|
||||
titleKey: 'dialog.maxUsersLimitReachedTitle'
|
||||
}));
|
||||
let retryAsVisitor = false;
|
||||
|
||||
if (error.params?.length && error.params[0]?.visitorsSupported) {
|
||||
// visitors are supported, so let's try joining that way
|
||||
retryAsVisitor = true;
|
||||
}
|
||||
|
||||
if (!retryAsVisitor) {
|
||||
dispatch(showErrorNotification({
|
||||
hideErrorSupportLink: true,
|
||||
descriptionKey: 'dialog.maxUsersLimitReached',
|
||||
titleKey: 'dialog.maxUsersLimitReachedTitle'
|
||||
}));
|
||||
}
|
||||
|
||||
// In case of max users(it can be from a visitor node), let's restore
|
||||
// oldConfig if any as we will be back to the main prosody.
|
||||
@@ -220,6 +234,18 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
.then(() => dispatch(disconnect()));
|
||||
}
|
||||
|
||||
if (retryAsVisitor && !newConfig && retryAsVisitorOnMaxError) {
|
||||
retryAsVisitorOnMaxError = false;
|
||||
|
||||
logger.info('On max user reached will retry joining as a visitor');
|
||||
|
||||
dispatch(disconnect(true)).then(() => {
|
||||
dispatch(setPreferVisitor(true));
|
||||
|
||||
return dispatch(connect());
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
@@ -300,6 +326,8 @@ function _conferenceJoined({ dispatch, getState }: IStore, next: Function, actio
|
||||
requireDisplayName
|
||||
} = getState()['features/base/config'];
|
||||
|
||||
retryAsVisitorOnMaxError = true;
|
||||
|
||||
dispatch(removeLobbyChatParticipant(true));
|
||||
|
||||
pendingSubjectChange && dispatch(setSubject(pendingSubjectChange));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ToolbarButton } from '../../toolbox/types';
|
||||
import { ILoggingConfig } from '../logging/types';
|
||||
import { IAudioSettings } from '../settings/reducer';
|
||||
import { DesktopSharingSourceType } from '../tracks/types';
|
||||
|
||||
type ButtonsWithNotifyClick = 'camera' |
|
||||
@@ -191,6 +192,7 @@ export interface IConfig {
|
||||
appId?: string;
|
||||
audioLevelsInterval?: number;
|
||||
audioQuality?: {
|
||||
enableAdvancedAudioSettings?: boolean;
|
||||
opusMaxAverageBitrate?: number | null;
|
||||
stereo?: boolean;
|
||||
};
|
||||
@@ -237,6 +239,7 @@ export interface IConfig {
|
||||
inactiveDisabled?: boolean;
|
||||
};
|
||||
constraints?: {
|
||||
audio?: IAudioSettings;
|
||||
video?: {
|
||||
height?: {
|
||||
ideal?: number;
|
||||
@@ -521,7 +524,6 @@ export interface IConfig {
|
||||
preCallTestEnabled?: boolean;
|
||||
preCallTestICEUrl?: string;
|
||||
};
|
||||
prejoinPageEnabled?: boolean;
|
||||
raisedHands?: {
|
||||
disableLowerHandByModerator?: boolean;
|
||||
disableLowerHandNotification?: boolean;
|
||||
|
||||
@@ -202,7 +202,6 @@ export default [
|
||||
'prejoinConfig.enabled',
|
||||
'prejoinConfig.hideDisplayName',
|
||||
'prejoinConfig.hideExtraJoinButtons',
|
||||
'prejoinPageEnabled',
|
||||
'raisedHands',
|
||||
'recordingService',
|
||||
'requireDisplayName',
|
||||
|
||||
@@ -6,6 +6,7 @@ import { safeJsonParse } from '@jitsi/js-utils/json';
|
||||
import { isEmpty, mergeWith, pick } from 'lodash-es';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { browser } from '../lib-jitsi-meet';
|
||||
import { getLocalParticipant } from '../participants/functions';
|
||||
import { isEmbedded } from '../util/embedUtils';
|
||||
import { parseURLParams } from '../util/parseURLParams';
|
||||
@@ -256,6 +257,17 @@ export function isDisplayNameVisible(state: IReduxState): boolean {
|
||||
return !state['features/base/config'].hideDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for determining if the advanced audio settings are enabled.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isAdvancedAudioSettingsEnabled(state: IReduxState): boolean {
|
||||
|
||||
return !browser.isWebKitBased() && Boolean(state['features/base/config']?.audioQuality?.enableAdvancedAudioSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a Jitsi Meet config.js from {@code localStorage} if it was
|
||||
* previously downloaded from a specific {@code baseURL} and stored with
|
||||
@@ -375,8 +387,7 @@ export function setConfigFromURLParams(
|
||||
|
||||
// When not in an iframe, start without media if the pre-join page is not enabled.
|
||||
if (!isEmbedded()
|
||||
&& ('config.prejoinConfig.enabled' in params || 'config.prejoinPageEnabled' in params)
|
||||
&& (config.prejoinConfig?.enabled === false || config.prejoinPageEnabled === false)) {
|
||||
&& 'config.prejoinConfig.enabled' in params && config.prejoinConfig?.enabled === false) {
|
||||
logger.warn('Using prejoinConfig.enabled config URL overwrite implies starting without media.');
|
||||
config.disableInitialGUM = true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/config');
|
||||
export default getLogger('app:config');
|
||||
|
||||
@@ -405,11 +405,6 @@ function _translateLegacyConfig(oldValue: IConfig) {
|
||||
newValue.welcomePage.disabled = !oldValue.enableWelcomePage;
|
||||
}
|
||||
|
||||
newValue.prejoinConfig = oldValue.prejoinConfig || {};
|
||||
if (oldValue.hasOwnProperty('prejoinPageEnabled')) {
|
||||
newValue.prejoinConfig.enabled = oldValue.prejoinPageEnabled;
|
||||
}
|
||||
|
||||
newValue.disabledSounds = newValue.disabledSounds || [];
|
||||
|
||||
if (oldValue.disableJoinLeaveSounds) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/connection');
|
||||
export default getLogger('app:connection');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/devices');
|
||||
export default getLogger('app:devices');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/dialog');
|
||||
export default getLogger('app:dialog');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/i18n');
|
||||
export default getLogger('app:i18n');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/jitsi-local-storage');
|
||||
export default getLogger('app:jitsi-local-storage');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/jwt');
|
||||
export default getLogger('app:jwt');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/lastn');
|
||||
export default getLogger('app:lastn');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-ignore
|
||||
import { safeJsonParse } from '@jitsi/js-utils/json';
|
||||
import { NativeModules } from 'react-native';
|
||||
// @ts-ignore
|
||||
import { Worklets } from 'react-native-worklets-core';
|
||||
|
||||
import { loadScript } from '../util/loadScript.native';
|
||||
|
||||
@@ -8,7 +9,12 @@ import logger from './logger';
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
const { JavaScriptSandbox } = NativeModules;
|
||||
|
||||
/**
|
||||
* Worklet context usefull for running small tasks off the JS thread.
|
||||
*/
|
||||
export const workletContext = Worklets.createContext('ConfigParser');
|
||||
|
||||
|
||||
/**
|
||||
* Loads config.js from a specific remote server.
|
||||
@@ -18,14 +24,46 @@ const { JavaScriptSandbox } = NativeModules;
|
||||
*/
|
||||
export async function loadConfig(url: string): Promise<Object> {
|
||||
try {
|
||||
const configTxt = await loadScript(url, 10 * 1000 /* Timeout in ms */, true /* skipeval */);
|
||||
const configJson = await JavaScriptSandbox.evaluate(`${configTxt}\nJSON.stringify(config);`);
|
||||
const config = safeJsonParse(configJson);
|
||||
const configTxt = await loadScript(url, 10 * 1000, true);
|
||||
|
||||
if (typeof config !== 'object') {
|
||||
throw new Error('config is not an object');
|
||||
const parseConfigAsync = workletContext.createRunAsync(function parseConfig(configText: string): string {
|
||||
'worklet';
|
||||
try {
|
||||
// Used IIFE wrapper to capture config object from config.js
|
||||
const configObj = eval(
|
||||
'(function(){\n'
|
||||
+ configText
|
||||
+ '\n; return (typeof config !== "undefined" ? config : globalThis.config); })()'
|
||||
);
|
||||
|
||||
if (configObj == void 0) {
|
||||
return 'Worklet_Error: config is undefined after eval()';
|
||||
}
|
||||
|
||||
if (typeof configObj !== 'object') {
|
||||
return 'Worklet_Error: config is not an object';
|
||||
}
|
||||
|
||||
return JSON.stringify(configObj);
|
||||
} catch (err) {
|
||||
return 'Worklet_Error:' + ((err as Error)?.message ?? String(err));
|
||||
}
|
||||
});
|
||||
|
||||
const workletConfig = await parseConfigAsync(configTxt);
|
||||
|
||||
if (typeof workletConfig !== 'string') {
|
||||
throw new Error('Worklet error: workletConfig is not a string');
|
||||
}
|
||||
|
||||
if (workletConfig.startsWith('Worklet_Error:')) {
|
||||
const msg = workletConfig.slice('Worklet_Error:'.length);
|
||||
|
||||
throw new Error(`Worklet error: ${msg}`);
|
||||
}
|
||||
|
||||
const config = safeJsonParse(workletConfig);
|
||||
|
||||
logger.info(`Config loaded from ${url}`);
|
||||
|
||||
return config;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/lib-jitsi-meet');
|
||||
export default getLogger('app:lib-jitsi-meet');
|
||||
|
||||
@@ -17,8 +17,8 @@ const DEFAULT_LOGGING_CONFIG: ILoggingConfig = {
|
||||
loggers: {
|
||||
// The following are too verbose in their logging with the
|
||||
// {@link #defaultLogLevel}:
|
||||
'modules/RTC/TraceablePeerConnection': 'info',
|
||||
'modules/xmpp/strophe.util': 'log'
|
||||
'rtc:TraceablePeerConnection': 'info',
|
||||
'xmpp:strophe.util': 'log'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,10 +41,10 @@ const DEFAULT_STATE = {
|
||||
// Reduce default verbosity on mobile, it kills performance.
|
||||
if (navigator.product === 'ReactNative') {
|
||||
const RN_LOGGERS: { [key: string]: LogLevel; } = {
|
||||
'modules/sdp/SDPUtil': 'info',
|
||||
'modules/xmpp/ChatRoom': 'warn',
|
||||
'modules/xmpp/JingleSessionPC': 'info',
|
||||
'modules/xmpp/strophe.jingle': 'info'
|
||||
'sdp:SDPUtils': 'info',
|
||||
'xmpp:ChatRoom': 'warn',
|
||||
'xmpp:JingleSessionPC': 'info',
|
||||
'xmpp:strophe.jingle': 'info'
|
||||
};
|
||||
|
||||
DEFAULT_STATE.config.loggers = {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/media');
|
||||
export default getLogger('app:media');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/net-info');
|
||||
export default getLogger('app:net-info');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/participants');
|
||||
export default getLogger('app:participants');
|
||||
|
||||
@@ -24,12 +24,14 @@ import { hideNotification, showNotification } from '../../notifications/actions'
|
||||
import {
|
||||
LOCAL_RECORDING_NOTIFICATION_ID,
|
||||
NOTIFICATION_TIMEOUT_TYPE,
|
||||
RAISE_HAND_NOTIFICATION_ID
|
||||
RAISE_HAND_NOTIFICATION_ID,
|
||||
VISITOR_ASKED_TO_JOIN_NOTIFICATION_ID
|
||||
} from '../../notifications/constants';
|
||||
import { open as openParticipantsPane } from '../../participants-pane/actions';
|
||||
import { CALLING, INVITED } from '../../presence-status/constants';
|
||||
import { RAISE_HAND_SOUND_ID } from '../../reactions/constants';
|
||||
import { RECORDING_OFF_SOUND_ID, RECORDING_ON_SOUND_ID } from '../../recording/constants';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app/actionTypes';
|
||||
import { CONFERENCE_JOINED, CONFERENCE_WILL_JOIN } from '../conference/actionTypes';
|
||||
import { forEachConference, getCurrentConference } from '../conference/functions';
|
||||
@@ -167,7 +169,10 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case LOCAL_PARTICIPANT_RAISE_HAND: {
|
||||
const { raisedHandTimestamp } = action;
|
||||
const localId = getLocalParticipant(store.getState())?.id;
|
||||
const localParticipant = getLocalParticipant(store.getState());
|
||||
const localId = localParticipant?.id;
|
||||
const _iAmVisitor = iAmVisitor(store.getState());
|
||||
const isHandRaised = hasRaisedHand(localParticipant);
|
||||
|
||||
store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
@@ -186,6 +191,18 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
raisedHandTimestamp
|
||||
}));
|
||||
|
||||
if (_iAmVisitor) {
|
||||
const notifyAction = isHandRaised
|
||||
? hideNotification(VISITOR_ASKED_TO_JOIN_NOTIFICATION_ID)
|
||||
: showNotification({
|
||||
titleKey: 'visitors.notification.requestToJoin',
|
||||
descriptionKey: 'visitors.notification.requestToJoinDescription',
|
||||
uid: VISITOR_ASKED_TO_JOIN_NOTIFICATION_ID
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY);
|
||||
|
||||
store.dispatch(notifyAction);
|
||||
}
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.API.notifyRaiseHandUpdated(localId, raisedHandTimestamp);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/premeeting');
|
||||
export default getLogger('app:premeeting');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/react');
|
||||
export default getLogger('app:react');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/redux');
|
||||
export default getLogger('app:redux');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/settings');
|
||||
export default getLogger('app:settings');
|
||||
|
||||
@@ -51,8 +51,15 @@ const DEFAULT_STATE: ISettingsState = {
|
||||
userSelectedMicDeviceLabel: undefined
|
||||
};
|
||||
|
||||
export interface IAudioSettings {
|
||||
autoGainControl?: boolean;
|
||||
channelCount?: 1 | 2;
|
||||
echoCancellation?: boolean;
|
||||
noiseSuppression?: boolean;
|
||||
}
|
||||
export interface ISettingsState {
|
||||
audioOutputDeviceId?: string;
|
||||
audioSettings?: IAudioSettings;
|
||||
audioSettingsVisible?: boolean;
|
||||
avatarURL?: string;
|
||||
cameraDeviceId?: string | boolean;
|
||||
@@ -66,6 +73,7 @@ export interface ISettingsState {
|
||||
localFlipX?: boolean;
|
||||
maxStageParticipants?: number;
|
||||
micDeviceId?: string | boolean;
|
||||
previewAudioTrack?: any | null;
|
||||
serverURL?: string;
|
||||
showSubtitlesOnStage?: boolean;
|
||||
soundsIncomingMessage?: boolean;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/sounds');
|
||||
export default getLogger('app:sounds');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/testing');
|
||||
export default getLogger('app:testing');
|
||||
|
||||
@@ -164,6 +164,7 @@ export function createLocalTracksA(options: ITrackOptions = {}) {
|
||||
= createLocalTracksF(
|
||||
{
|
||||
cameraDeviceId: options.cameraDeviceId,
|
||||
constraints: options?.constraints,
|
||||
devices: [ device ],
|
||||
facingMode:
|
||||
options.facingMode || getCameraFacingMode(state),
|
||||
|
||||
@@ -12,6 +12,7 @@ import { setScreenAudioShareState, setScreenshareAudioTrack } from '../../screen
|
||||
import { isAudioOnlySharing, isScreenVideoShared } from '../../screen-share/functions';
|
||||
import { toggleScreenshotCaptureSummary } from '../../screenshot-capture/actions';
|
||||
import { isScreenshotCaptureEnabled } from '../../screenshot-capture/functions';
|
||||
import { setAudioSettings } from '../../settings/actions.web';
|
||||
import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { notifyCameraError, notifyMicError } from '../devices/actions.web';
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
} from '../media/constants';
|
||||
import { IGUMPendingState } from '../media/types';
|
||||
import { updateSettings } from '../settings/actions';
|
||||
import { IAudioSettings } from '../settings/reducer';
|
||||
|
||||
import { addLocalTrack, replaceLocalTrack } from './actions.any';
|
||||
import AllowToggleCameraDialog from './components/web/AllowToggleCameraDialog';
|
||||
@@ -37,6 +39,7 @@ import {
|
||||
getLocalVideoTrack,
|
||||
isToggleCameraEnabled
|
||||
} from './functions';
|
||||
import { applyAudioConstraints, getLocalJitsiAudioTrackSettings } from './functions.web';
|
||||
import logger from './logger';
|
||||
import { ICreateInitialTracksOptions, IInitialTracksErrors, IShareOptions, IToggleScreenSharingOptions } from './types';
|
||||
|
||||
@@ -329,7 +332,6 @@ export function setGUMPendingStateOnFailedTracks(tracks: Array<any>, dispatch: I
|
||||
export function createAndAddInitialAVTracks(devices: Array<MediaType>) {
|
||||
return async (dispatch: IStore['dispatch']) => {
|
||||
dispatch(gumPending(devices, IGUMPendingState.PENDING_UNMUTE));
|
||||
|
||||
const { tracks, errors } = await dispatch(createInitialAVTracks({ devices }));
|
||||
|
||||
setGUMPendingStateOnFailedTracks(tracks, dispatch);
|
||||
@@ -541,3 +543,21 @@ export function toggleCamera() {
|
||||
await dispatch(replaceLocalTrack(null, newVideoTrack));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the audio settings.
|
||||
*
|
||||
* @param {IAudioSettings} settings - The settings to apply.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleUpdateAudioSettings(settings: IAudioSettings) {
|
||||
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
|
||||
await applyAudioConstraints(state, settings);
|
||||
|
||||
const updatedSettings = getLocalJitsiAudioTrackSettings(state) as IAudioSettings;
|
||||
|
||||
dispatch(setAudioSettings(updatedSettings));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -201,6 +201,45 @@ export function getLocalJitsiAudioTrack(state: IReduxState) {
|
||||
return track?.jitsiTrack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns audio settings from the local Jitsi audio track.
|
||||
*
|
||||
* @param {IReduxState} state - The Redux state.
|
||||
* @returns {IAudioSettings} The extracted audio settings.
|
||||
*/
|
||||
export function getLocalJitsiAudioTrackSettings(state: IReduxState) {
|
||||
const jitsiTrack = getLocalJitsiAudioTrack(state);
|
||||
|
||||
if (!jitsiTrack) {
|
||||
const config = state['features/base/config'];
|
||||
const disableAP = Boolean(config?.disableAP);
|
||||
const disableAGC = Boolean(config?.disableAGC);
|
||||
const disableAEC = Boolean(config?.disableAEC);
|
||||
const disableNS = Boolean(config?.disableNS);
|
||||
const stereo = Boolean(config?.audioQuality?.stereo);
|
||||
|
||||
return {
|
||||
autoGainControl: !disableAP && !disableAGC,
|
||||
channelCount: stereo ? 2 : 1,
|
||||
echoCancellation: !disableAP && !disableAEC,
|
||||
noiseSuppression: !disableAP && !disableNS
|
||||
};
|
||||
}
|
||||
|
||||
const hasAudioMixerEffect = Boolean(typeof jitsiTrack._streamEffect?.setMuted === 'function' && jitsiTrack._streamEffect?._originalTrack);
|
||||
|
||||
const track = hasAudioMixerEffect ? jitsiTrack._streamEffect._originalTrack : jitsiTrack.getTrack();
|
||||
|
||||
const { autoGainControl, channelCount, echoCancellation, noiseSuppression } = track.getSettings();
|
||||
|
||||
return {
|
||||
autoGainControl,
|
||||
channelCount,
|
||||
echoCancellation,
|
||||
noiseSuppression
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns track of specified media type for specified participant.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { IStore } from '../../app/types';
|
||||
import { IStateful } from '../app/types';
|
||||
import { isAdvancedAudioSettingsEnabled } from '../config/functions.any';
|
||||
import { isMobileBrowser } from '../environment/utils';
|
||||
import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
|
||||
import { gumPending, setAudioMuted } from '../media/actions';
|
||||
@@ -11,9 +12,10 @@ import {
|
||||
getUserSelectedCameraDeviceId,
|
||||
getUserSelectedMicDeviceId
|
||||
} from '../settings/functions.web';
|
||||
import { IAudioSettings } from '../settings/reducer';
|
||||
import { getJitsiMeetGlobalNSConnectionTimes } from '../util/helpers';
|
||||
|
||||
import { getCameraFacingMode } from './functions.any';
|
||||
import { getCameraFacingMode, getLocalJitsiAudioTrack, getLocalJitsiAudioTrackSettings } from './functions.any';
|
||||
import loadEffects from './loadEffects';
|
||||
import logger from './logger';
|
||||
import { ITrackOptions } from './types';
|
||||
@@ -62,7 +64,13 @@ export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore,
|
||||
desktopSharingFrameRate,
|
||||
resolution
|
||||
} = state['features/base/config'];
|
||||
const constraints = options.constraints ?? state['features/base/config'].constraints;
|
||||
|
||||
const constraints = options.constraints ?? state['features/base/config'].constraints ?? {};
|
||||
|
||||
if (isAdvancedAudioSettingsEnabled(state) && typeof APP !== 'undefined') {
|
||||
constraints.audio = state['features/settings'].audioSettings ?? getLocalJitsiAudioTrackSettings(state);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
loadEffects(store).then((effectsArray: Object[]) => {
|
||||
@@ -214,3 +222,32 @@ export function isToggleCameraEnabled(stateful: IStateful) {
|
||||
|
||||
return isMobileBrowser() && Number(videoInput?.length) > 1;
|
||||
}
|
||||
/**
|
||||
* Applies audio constraints to the local Jitsi audio track.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @param {IAudioSettings} settings - The audio settings to apply.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function applyAudioConstraints(stateful: IStateful, settings: IAudioSettings) {
|
||||
const state = toState(stateful);
|
||||
const track = getLocalJitsiAudioTrack(state);
|
||||
|
||||
if (!track) {
|
||||
logger.debug('No local audio track found');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAdvancedAudioSettingsEnabled(state)) {
|
||||
logger.debug('Advanced audio settings disabled');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await track.applyConstraints(settings);
|
||||
} catch (error) {
|
||||
logger.error('Failed to apply audio constraints ', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/tracks');
|
||||
export default getLogger('app:tracks');
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IStore } from '../../app/types';
|
||||
import { _RESET_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import {
|
||||
SET_AUDIO_MUTED,
|
||||
SET_CAMERA_FACING_MODE,
|
||||
SET_SCREENSHARE_MUTED,
|
||||
SET_VIDEO_MUTED,
|
||||
@@ -46,15 +45,6 @@ import './subscriber';
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case SET_AUDIO_MUTED:
|
||||
if (!action.muted
|
||||
&& isUserInteractionRequiredForUnmute(store.getState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setMuted(store, action, MEDIA_TYPE.AUDIO);
|
||||
break;
|
||||
|
||||
case SET_CAMERA_FACING_MODE: {
|
||||
// XXX The camera facing mode of a MediaStreamTrack can be specified
|
||||
// only at initialization time and then it can only be toggled. So in
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { IStore } from '../../app/types';
|
||||
import { SET_AUDIO_MUTED } from '../media/actionTypes';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
VIDEO_TYPE
|
||||
@@ -8,8 +10,11 @@ import {
|
||||
TRACK_UPDATED
|
||||
} from './actionTypes';
|
||||
import {
|
||||
toggleScreensharing
|
||||
createLocalTracksA,
|
||||
toggleScreensharing,
|
||||
trackMuteUnmuteFailed
|
||||
} from './actions.native';
|
||||
import { getLocalTrack, setTrackMuted } from './functions.any';
|
||||
|
||||
import './middleware.any';
|
||||
|
||||
@@ -23,11 +28,15 @@ import './middleware.any';
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case SET_AUDIO_MUTED: {
|
||||
_setMuted(store, action);
|
||||
break;
|
||||
}
|
||||
case TRACK_UPDATED: {
|
||||
const { jitsiTrack, local } = action.track;
|
||||
|
||||
if (local && jitsiTrack.isMuted()
|
||||
&& jitsiTrack.type === MEDIA_TYPE.VIDEO && jitsiTrack.videoType === VIDEO_TYPE.DESKTOP) {
|
||||
&& jitsiTrack.type === MEDIA_TYPE.VIDEO && jitsiTrack.videoType === VIDEO_TYPE.DESKTOP) {
|
||||
store.dispatch(toggleScreensharing(false));
|
||||
}
|
||||
break;
|
||||
@@ -36,3 +45,32 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Mutes or unmutes a local track with a specific media type.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified action is dispatched.
|
||||
* @param {Action} action - The redux action dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _setMuted(store: IStore, { ensureTrack, muted }: {
|
||||
ensureTrack: boolean; muted: boolean; }) {
|
||||
const { dispatch, getState } = store;
|
||||
const state = getState();
|
||||
const localTrack = getLocalTrack(state['features/base/tracks'], MEDIA_TYPE.AUDIO, /* includePending */ true);
|
||||
|
||||
if (localTrack) {
|
||||
// The `jitsiTrack` property will have a value only for a localTrack for which `getUserMedia` has already
|
||||
// completed. If there's no `jitsiTrack`, then the `muted` state will be applied once the `jitsiTrack` is
|
||||
// created.
|
||||
const { jitsiTrack } = localTrack;
|
||||
|
||||
if (jitsiTrack) {
|
||||
setTrackMuted(jitsiTrack, muted, state, dispatch)
|
||||
.catch(() => dispatch(trackMuteUnmuteFailed(localTrack, muted)));
|
||||
}
|
||||
} else if (!muted && ensureTrack) {
|
||||
dispatch(createLocalTracksA({ devices: [ MEDIA_TYPE.AUDIO ] }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,15 @@ import { AnyAction } from 'redux';
|
||||
import { IStore } from '../../app/types';
|
||||
import { hideNotification } from '../../notifications/actions';
|
||||
import { isPrejoinPageVisible } from '../../prejoin/functions';
|
||||
import { setAudioSettings } from '../../settings/actions.web';
|
||||
import { getAvailableDevices } from '../devices/actions.web';
|
||||
import { setScreenshareMuted } from '../media/actions';
|
||||
import { SET_AUDIO_MUTED } from '../media/actionTypes';
|
||||
import { gumPending, setScreenshareMuted } from '../media/actions';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
VIDEO_TYPE
|
||||
} from '../media/constants';
|
||||
import { IGUMPendingState } from '../media/types';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
|
||||
import {
|
||||
@@ -20,14 +23,19 @@ import {
|
||||
TRACK_UPDATED
|
||||
} from './actionTypes';
|
||||
import {
|
||||
createLocalTracksA,
|
||||
showNoDataFromSourceVideoError,
|
||||
toggleScreensharing,
|
||||
trackMuteUnmuteFailed,
|
||||
trackNoDataFromSourceNotificationInfoChanged
|
||||
} from './actions.web';
|
||||
import {
|
||||
getTrackByJitsiTrack, logTracksForParticipant
|
||||
getLocalJitsiAudioTrackSettings,
|
||||
getLocalTrack,
|
||||
getTrackByJitsiTrack, isUserInteractionRequiredForUnmute, logTracksForParticipant,
|
||||
setTrackMuted
|
||||
} from './functions.web';
|
||||
import { ITrack } from './types';
|
||||
import { ITrack, ITrackOptions } from './types';
|
||||
|
||||
import './middleware.any';
|
||||
|
||||
@@ -138,7 +146,15 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
return result;
|
||||
}
|
||||
case SET_AUDIO_MUTED: {
|
||||
if (!action.muted
|
||||
&& isUserInteractionRequiredForUnmute(store.getState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setMuted(store, action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
@@ -207,3 +223,47 @@ function _removeNoDataFromSourceNotification({ getState, dispatch }: IStore, tra
|
||||
dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, undefined));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutes or unmutes a local track with a specific media type.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified action is
|
||||
* dispatched.
|
||||
* @param {Action} action - The redux action dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _setMuted(store: IStore, { ensureTrack, muted }: {
|
||||
ensureTrack: boolean; muted: boolean; }) {
|
||||
const { dispatch, getState } = store;
|
||||
const state = getState();
|
||||
const localTrack = getLocalTrack(state['features/base/tracks'], MEDIA_TYPE.AUDIO, /* includePending */ true);
|
||||
|
||||
if (localTrack) {
|
||||
// The `jitsiTrack` property will have a value only for a localTrack for which `getUserMedia` has already
|
||||
// completed. If there's no `jitsiTrack`, then the `muted` state will be applied once the `jitsiTrack` is
|
||||
// created.
|
||||
const { jitsiTrack } = localTrack;
|
||||
|
||||
if (jitsiTrack) {
|
||||
setTrackMuted(jitsiTrack, muted, state, dispatch)
|
||||
.catch(() => {
|
||||
dispatch(trackMuteUnmuteFailed(localTrack, muted));
|
||||
});
|
||||
}
|
||||
} else if (!muted && ensureTrack) {
|
||||
// TODO(saghul): reconcile these 2 types.
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO ], IGUMPendingState.PENDING_UNMUTE));
|
||||
|
||||
const createTrackOptions: ITrackOptions = {
|
||||
devices: [ MEDIA_TYPE.AUDIO ],
|
||||
};
|
||||
|
||||
dispatch(createLocalTracksA(createTrackOptions)).then(() => {
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO ], IGUMPendingState.NONE));
|
||||
const updatedSettings = getLocalJitsiAudioTrackSettings(getState());
|
||||
|
||||
dispatch(setAudioSettings(updatedSettings));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { MediaType } from '../media/constants';
|
||||
import { IAudioSettings } from '../settings/reducer';
|
||||
|
||||
export interface ITrackOptions {
|
||||
cameraDeviceId?: string | null;
|
||||
constraints?: {
|
||||
audio?: IAudioSettings;
|
||||
video?: {
|
||||
height?: {
|
||||
ideal?: number;
|
||||
|
||||
@@ -17,6 +17,7 @@ interface ITabProps {
|
||||
icon?: Function;
|
||||
id: string;
|
||||
label?: string;
|
||||
title?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -126,26 +127,34 @@ const Tabs = ({
|
||||
aria-label = { accessibilityLabel }
|
||||
className = { cx(classes.container, className) }
|
||||
role = 'tablist'>
|
||||
{tabs.map((tab, index) => (
|
||||
<button
|
||||
aria-controls = { tab.controlsId }
|
||||
aria-label = { tab.accessibilityLabel }
|
||||
aria-selected = { selected === tab.id }
|
||||
className = { cx(classes.tab, selected === tab.id && 'selected', isMobile && 'is-mobile') }
|
||||
disabled = { tab.disabled }
|
||||
id = { tab.id }
|
||||
key = { tab.id }
|
||||
onClick = { onClick(tab.id) }
|
||||
onKeyDown = { onKeyDown(index) }
|
||||
role = 'tab'
|
||||
tabIndex = { selected === tab.id ? undefined : -1 }>
|
||||
{tab.icon && <Icon
|
||||
className = { classes.icon }
|
||||
src = { tab.icon } />}
|
||||
{tab.label}
|
||||
{tab.countBadge && <span className = { classes.badge }>{tab.countBadge}</span>}
|
||||
</button>
|
||||
))}
|
||||
{
|
||||
tabs.map((tab, index) => (
|
||||
<button
|
||||
aria-controls = { tab.controlsId }
|
||||
aria-label = { tab.accessibilityLabel }
|
||||
aria-selected = { selected === tab.id }
|
||||
className = { cx(classes.tab, selected === tab.id && 'selected', isMobile && 'is-mobile') }
|
||||
disabled = { tab.disabled }
|
||||
id = { tab.id }
|
||||
key = { tab.id }
|
||||
onClick = { onClick(tab.id) }
|
||||
onKeyDown = { onKeyDown(index) }
|
||||
role = 'tab'
|
||||
tabIndex = { selected === tab.id ? undefined : -1 }
|
||||
title = { tab.title }>
|
||||
{
|
||||
tab.icon && <Icon
|
||||
className = { classes.icon }
|
||||
src = { tab.icon } />
|
||||
}
|
||||
{ tab.label }
|
||||
{
|
||||
tab.countBadge && <span className = { classes.badge }>
|
||||
{ tab.countBadge }
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/util');
|
||||
export default getLogger('app:util');
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
import { FEATURE_KEY } from './constants';
|
||||
|
||||
export default getLogger(FEATURE_KEY);
|
||||
export default getLogger('app:breakout-rooms');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/calendar-sync');
|
||||
export default getLogger('app:calendar-sync');
|
||||
|
||||
@@ -170,7 +170,7 @@ export const microsoftCalendarApi = {
|
||||
|
||||
popupAuthWindow = window.open(
|
||||
authUrl,
|
||||
'Auth M$',
|
||||
`Auth M$-${Date.now()}`,
|
||||
`width=${w}, height=${h}, top=${
|
||||
(screen.height / 2) - (h / 2)}, left=${
|
||||
(screen.width / 2) - (w / 2)}`);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { IParticipant } from '../base/participants/types';
|
||||
import { navigate }
|
||||
from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { navigate } from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../mobile/navigation/routes';
|
||||
|
||||
import { OPEN_CHAT } from './actionTypes';
|
||||
@@ -18,7 +17,7 @@ export * from './actions.any';
|
||||
* type: OPEN_CHAT
|
||||
* }}
|
||||
*/
|
||||
export function openChat(participant: IParticipant | undefined | Object, disablePolls?: boolean) {
|
||||
export function openChat(participant?: IParticipant | undefined | Object, disablePolls?: boolean) {
|
||||
if (disablePolls) {
|
||||
navigate(screen.conference.chat);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { getParticipantDisplayName, isLocalParticipantModerator } from '../../base/participants/functions';
|
||||
import { getVisitorDisplayName } from '../../visitors/functions';
|
||||
import { getDisplayName } from '../../visitors/functions';
|
||||
import { setLobbyChatActiveState, setPrivateMessageRecipient } from '../actions.any';
|
||||
import { isVisitorChatParticipant } from '../functions';
|
||||
|
||||
@@ -84,7 +84,7 @@ export function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
|
||||
if (privateMessageRecipient) {
|
||||
_privateMessageRecipient = _isVisitor
|
||||
? getVisitorDisplayName(state, privateMessageRecipient.name)
|
||||
? getDisplayName(state, privateMessageRecipient.name)
|
||||
: getParticipantDisplayName(state, privateMessageRecipient.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { isGifEnabled, isGifMessage } from '../../../gifs/functions.native';
|
||||
import { CHAR_LIMIT, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import {
|
||||
getCanReplyToMessage,
|
||||
getDisplayNameSuffix,
|
||||
getFormattedTimestamp,
|
||||
getMessageText,
|
||||
getPrivateNoticeMessage,
|
||||
@@ -122,17 +123,17 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
* @returns {React.ReactElement<*> | null}
|
||||
*/
|
||||
_renderDisplayName() {
|
||||
const { message, showDisplayName, t } = this.props;
|
||||
const { message, showDisplayName } = this.props;
|
||||
|
||||
if (!showDisplayName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { displayName, isFromVisitor } = message;
|
||||
const { displayName } = message;
|
||||
|
||||
return (
|
||||
<Text style = { styles.senderDisplayName }>
|
||||
{ `${displayName}${isFromVisitor ? ` ${t('visitors.chatIndicator')}` : ''}` }
|
||||
{ `${displayName}${getDisplayNameSuffix(message)}` }
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,32 +78,6 @@ interface IProps extends AbstractProps {
|
||||
*/
|
||||
_nbUnreadPolls: number;
|
||||
|
||||
/**
|
||||
* Function to send a text message.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
_onSendMessage: Function;
|
||||
|
||||
/**
|
||||
* Function to toggle the chat window.
|
||||
*/
|
||||
_onToggleChat: Function;
|
||||
|
||||
/**
|
||||
* Function to display the chat tab.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
_onToggleChatTab: Function;
|
||||
|
||||
/**
|
||||
* Function to display the polls tab.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
_onTogglePollsTab: Function;
|
||||
|
||||
/**
|
||||
* Whether or not to block chat access with a nickname input form.
|
||||
*/
|
||||
@@ -244,10 +218,6 @@ const Chat = ({
|
||||
_messages,
|
||||
_nbUnreadMessages,
|
||||
_nbUnreadPolls,
|
||||
_onSendMessage,
|
||||
_onToggleChat,
|
||||
_onToggleChatTab,
|
||||
_onTogglePollsTab,
|
||||
_showNamePrompt,
|
||||
_width,
|
||||
dispatch,
|
||||
@@ -512,7 +482,8 @@ const Chat = ({
|
||||
_focusedTab !== ChatTabs.CHAT && _nbUnreadMessages > 0 ? _nbUnreadMessages : undefined,
|
||||
id: ChatTabs.CHAT,
|
||||
controlsId: `${ChatTabs.CHAT}-panel`,
|
||||
icon: IconMessage
|
||||
icon: IconMessage,
|
||||
title: t('chat.tabs.chat')
|
||||
}
|
||||
];
|
||||
|
||||
@@ -522,7 +493,8 @@ const Chat = ({
|
||||
countBadge: _focusedTab !== ChatTabs.POLLS && _nbUnreadPolls > 0 ? _nbUnreadPolls : undefined,
|
||||
id: ChatTabs.POLLS,
|
||||
controlsId: `${ChatTabs.POLLS}-panel`,
|
||||
icon: IconInfo
|
||||
icon: IconInfo,
|
||||
title: t('chat.tabs.polls')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -532,7 +504,8 @@ const Chat = ({
|
||||
countBadge: undefined,
|
||||
id: ChatTabs.CLOSED_CAPTIONS,
|
||||
controlsId: `${ChatTabs.CLOSED_CAPTIONS}-panel`,
|
||||
icon: IconSubtitles
|
||||
icon: IconSubtitles,
|
||||
title: t('chat.tabs.closedCaptions')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -542,7 +515,8 @@ const Chat = ({
|
||||
countBadge: undefined,
|
||||
id: ChatTabs.FILE_SHARING,
|
||||
controlsId: `${ChatTabs.FILE_SHARING}-panel`,
|
||||
icon: IconShareDoc
|
||||
icon: IconShareDoc,
|
||||
title: t('chat.tabs.fileSharing')
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getParticipantById, getParticipantDisplayName, isPrivateChatEnabled } f
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
import Message from '../../../base/react/components/web/Message';
|
||||
import { MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import { getFormattedTimestamp, getMessageText, getPrivateNoticeMessage } from '../../functions';
|
||||
import { getDisplayNameSuffix, getFormattedTimestamp, getMessageText, getPrivateNoticeMessage } from '../../functions';
|
||||
import { IChatMessageProps } from '../../types';
|
||||
|
||||
import MessageMenu from './MessageMenu';
|
||||
@@ -226,13 +226,13 @@ const ChatMessage = ({
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
function _renderDisplayName() {
|
||||
const { displayName, isFromVisitor = false } = message;
|
||||
const { displayName } = message;
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-hidden = { true }
|
||||
className = { cx('display-name', classes.displayName) }>
|
||||
{`${displayName}${isFromVisitor ? ` ${t('visitors.chatIndicator')}` : ''}`}
|
||||
{`${displayName}${getDisplayNameSuffix(message)}`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -387,7 +387,8 @@ const ChatMessage = ({
|
||||
</div>
|
||||
{shouldDisplayMenuOnRight && (
|
||||
<div className = { classes.sideBySideContainer }>
|
||||
{!message.privateMessage && !message.lobbyChat && <div>
|
||||
{!message.privateMessage && !message.lobbyChat
|
||||
&& !message.isReaction && <div>
|
||||
<div className = { classes.optionsButtonContainer }>
|
||||
{isHovered && <ReactButton
|
||||
messageId = { message.messageId }
|
||||
|
||||
@@ -265,3 +265,21 @@ export function isVisitorChatParticipant(
|
||||
): participant is IVisitorChatParticipant {
|
||||
return Boolean(participant && 'isVisitor' in participant && participant.isVisitor === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a suffix to be appended to the display name based on the message origin.
|
||||
*
|
||||
* @param {IMessage} message - The message.
|
||||
* @returns {string} The suffix, if any.
|
||||
*/
|
||||
export function getDisplayNameSuffix(message: IMessage): string {
|
||||
let suffix = '';
|
||||
|
||||
if (message.isFromVisitor) {
|
||||
suffix = ` ${i18next.t('visitors.chatIndicator')}`;
|
||||
} else if (message.isFromGuest) {
|
||||
suffix = ` ${i18next.t('chat.guestsChatIndicator')}`;
|
||||
}
|
||||
|
||||
return suffix;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import { pushReactions } from '../reactions/actions.any';
|
||||
import { ENDPOINT_REACTION_NAME } from '../reactions/constants';
|
||||
import { getReactionMessageFromBuffer, isReactionsEnabled } from '../reactions/functions.any';
|
||||
import { showToolbox } from '../toolbox/actions';
|
||||
import { getVisitorDisplayName } from '../visitors/functions';
|
||||
import { getDisplayName } from '../visitors/functions';
|
||||
|
||||
import {
|
||||
ADD_MESSAGE,
|
||||
@@ -51,8 +51,9 @@ import {
|
||||
clearMessages,
|
||||
closeChat,
|
||||
notifyPrivateRecipientsChanged,
|
||||
openChat,
|
||||
setPrivateMessageRecipient
|
||||
} from './actions.any';
|
||||
} from './actions';
|
||||
import { ChatPrivacyDialog } from './components';
|
||||
import {
|
||||
ChatTabs,
|
||||
@@ -63,7 +64,12 @@ import {
|
||||
MESSAGE_TYPE_REMOTE,
|
||||
MESSAGE_TYPE_SYSTEM
|
||||
} from './constants';
|
||||
import { getUnreadCount, isSendGroupChatDisabled, isVisitorChatParticipant } from './functions';
|
||||
import {
|
||||
getDisplayNameSuffix,
|
||||
getUnreadCount,
|
||||
isSendGroupChatDisabled,
|
||||
isVisitorChatParticipant
|
||||
} from './functions';
|
||||
import { INCOMING_MSG_SOUND_FILE } from './sounds';
|
||||
import './subscriber';
|
||||
|
||||
@@ -366,7 +372,7 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
|
||||
JitsiConferenceEvents.MESSAGE_RECEIVED,
|
||||
/* eslint-disable max-params */
|
||||
(participantId: string, message: string, timestamp: number,
|
||||
displayName: string, isFromVisitor: boolean, messageId: string) => {
|
||||
displayName: string, isFromVisitor: boolean, messageId: string, source: string) => {
|
||||
/* eslint-enable max-params */
|
||||
_onConferenceMessageReceived(store, {
|
||||
// in case of messages coming from visitors we can have unknown id
|
||||
@@ -376,7 +382,9 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
|
||||
displayName,
|
||||
isFromVisitor,
|
||||
messageId,
|
||||
privateMessage: false });
|
||||
source,
|
||||
privateMessage: false
|
||||
});
|
||||
|
||||
if (isSendGroupChatDisabled(store.getState()) && participantId) {
|
||||
const participant = getParticipantById(store, participantId);
|
||||
@@ -426,9 +434,9 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onConferenceMessageReceived(store: IStore,
|
||||
{ displayName, isFromVisitor, message, messageId, participantId, privateMessage, timestamp }: {
|
||||
{ displayName, isFromVisitor, message, messageId, participantId, privateMessage, timestamp, source }: {
|
||||
displayName?: string; isFromVisitor?: boolean; message: string; messageId?: string;
|
||||
participantId: string; privateMessage: boolean; timestamp: number; }
|
||||
participantId: string; privateMessage: boolean; source?: string; timestamp: number; }
|
||||
) {
|
||||
|
||||
const isGif = isGifEnabled(store.getState()) && isGifMessage(message);
|
||||
@@ -447,7 +455,8 @@ function _onConferenceMessageReceived(store: IStore,
|
||||
privateMessage,
|
||||
lobbyChat: false,
|
||||
timestamp,
|
||||
messageId
|
||||
messageId,
|
||||
source
|
||||
}, true, isGif);
|
||||
}
|
||||
|
||||
@@ -556,9 +565,9 @@ function getLobbyChatDisplayName(state: IReduxState, participantId: string) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
{ displayName, isFromVisitor, lobbyChat, message, messageId, participantId, privateMessage, timestamp }: {
|
||||
{ displayName, isFromVisitor, lobbyChat, message, messageId, participantId, privateMessage, source, timestamp }: {
|
||||
displayName?: string; isFromVisitor?: boolean; lobbyChat: boolean; message: string;
|
||||
messageId?: string; participantId: string; privateMessage: boolean; timestamp: number; },
|
||||
messageId?: string; participantId: string; privateMessage: boolean; source?: string; timestamp: number; },
|
||||
shouldPlaySound = true,
|
||||
isReaction = false
|
||||
) {
|
||||
@@ -571,35 +580,33 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
dispatch(playSound(INCOMING_MSG_SOUND_ID));
|
||||
}
|
||||
|
||||
// Provide a default for the case when a message is being
|
||||
// backfilled for a participant that has left the conference.
|
||||
const participant = getParticipantById(state, participantId) || { local: undefined };
|
||||
const participant = getParticipantById(state, participantId);
|
||||
|
||||
const localParticipant = getLocalParticipant(getState);
|
||||
let _displayName, displayNameToShow;
|
||||
let _displayName;
|
||||
|
||||
if (lobbyChat) {
|
||||
displayNameToShow = _displayName = getLobbyChatDisplayName(state, participantId);
|
||||
_displayName = getLobbyChatDisplayName(state, participantId);
|
||||
} else if (isFromVisitor) {
|
||||
_displayName = getVisitorDisplayName(state, displayName);
|
||||
displayNameToShow = `${_displayName} ${i18next.t('visitors.chatIndicator')}`;
|
||||
_displayName = getDisplayName(state, displayName);
|
||||
} else if (!participant) {
|
||||
_displayName = getDisplayName(state, displayName);
|
||||
} else {
|
||||
displayNameToShow = _displayName = getParticipantDisplayName(state, participantId);
|
||||
_displayName = getParticipantDisplayName(state, participantId);
|
||||
}
|
||||
|
||||
const hasRead = participant.local || isChatOpen;
|
||||
const hasRead = participant?.local || isChatOpen;
|
||||
const timestampToDate = timestamp ? new Date(timestamp) : new Date();
|
||||
const millisecondsTimestamp = timestampToDate.getTime();
|
||||
|
||||
// skip message notifications on join (the messages having timestamp - coming from the history)
|
||||
const shouldShowNotification = userSelectedNotifications?.['notify.chatMessages']
|
||||
&& !hasRead && !isReaction && (!timestamp || lobbyChat);
|
||||
|
||||
dispatch(addMessage({
|
||||
const newMessage = {
|
||||
displayName: _displayName,
|
||||
hasRead,
|
||||
participantId,
|
||||
messageType: participant.local ? MESSAGE_TYPE_LOCAL : MESSAGE_TYPE_REMOTE,
|
||||
messageType: participant?.local ? MESSAGE_TYPE_LOCAL : MESSAGE_TYPE_REMOTE,
|
||||
message,
|
||||
privateMessage,
|
||||
lobbyChat,
|
||||
@@ -607,13 +614,29 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
timestamp: millisecondsTimestamp,
|
||||
messageId,
|
||||
isReaction,
|
||||
isFromVisitor
|
||||
}));
|
||||
isFromVisitor,
|
||||
isFromGuest: source === 'guest'
|
||||
};
|
||||
|
||||
dispatch(addMessage(newMessage));
|
||||
|
||||
let notificationDisplayName = _displayName;
|
||||
|
||||
// source can be 'token' or 'guest'. When it is 'guest', we append a guest indicator
|
||||
// to the display name to notify users that this message can be anything, while the token one is coming
|
||||
// from a trusted source (the jwt token, the name can be locked from the backend if the
|
||||
// token has the feature 'name-readonly')
|
||||
if (isFromVisitor || (!participant && source === 'guest')) {
|
||||
// @ts-ignore
|
||||
notificationDisplayName = `${_displayName} ${getDisplayNameSuffix(newMessage)}`;
|
||||
}
|
||||
|
||||
if (shouldShowNotification) {
|
||||
dispatch(showMessageNotification({
|
||||
title: displayNameToShow,
|
||||
description: message
|
||||
title: notificationDisplayName,
|
||||
description: message,
|
||||
customActionNameKey: [ 'notify.openChat' ],
|
||||
customActionHandler: [ () => dispatch(openChat()) ]
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
||||
}
|
||||
|
||||
@@ -623,7 +646,7 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
APP.API.notifyReceivedChatMessage({
|
||||
body: message,
|
||||
from: participantId,
|
||||
nick: displayNameToShow,
|
||||
nick: notificationDisplayName,
|
||||
privateMessage,
|
||||
ts: timestamp
|
||||
});
|
||||
@@ -665,7 +688,7 @@ function _persistSentPrivateMessage({ dispatch, getState }: IStore, recipient: I
|
||||
|
||||
const recipientName
|
||||
= recipient.isVisitor
|
||||
? getVisitorDisplayName(state, recipient.name)
|
||||
? getDisplayName(state, recipient.name)
|
||||
: (isLobbyPrivateMessage
|
||||
? lobbyMessageRecipient?.name
|
||||
: getParticipantDisplayName(getState, recipient?.id));
|
||||
|
||||
@@ -68,6 +68,7 @@ ReducerRegistry.register<IChatState>('features/chat', (state = DEFAULT_STATE, ac
|
||||
const newMessage: IMessage = {
|
||||
displayName: action.displayName,
|
||||
error: action.error,
|
||||
isFromGuest: Boolean(action.isFromGuest),
|
||||
isFromVisitor: Boolean(action.isFromVisitor),
|
||||
participantId: action.participantId,
|
||||
isReaction: action.isReaction,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IStore } from '../app/types';
|
||||
export interface IMessage {
|
||||
displayName: string;
|
||||
error?: Object;
|
||||
isFromGuest?: boolean;
|
||||
isFromVisitor?: boolean;
|
||||
isReaction: boolean;
|
||||
lobbyChat: boolean;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/chrome-banner');
|
||||
export default getLogger('app:chrome-banner');
|
||||
|
||||
@@ -579,7 +579,7 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
const { startCarMode } = state['features/base/settings'];
|
||||
const { enabled: audioOnlyEnabled } = state['features/base/audio-only'];
|
||||
const brandingStyles = backgroundColor ? {
|
||||
backgroundColor
|
||||
background: backgroundColor
|
||||
} : undefined;
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/conference');
|
||||
export default getLogger('app:conference');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/desktop-picker');
|
||||
export default getLogger('app:desktop-picker');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import { createDeviceChangedEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
import { IStore } from '../app/types';
|
||||
@@ -7,8 +9,12 @@ import {
|
||||
} from '../base/devices/actions';
|
||||
import { getDeviceLabelById, setAudioOutputDeviceId } from '../base/devices/functions';
|
||||
import { updateSettings } from '../base/settings/actions';
|
||||
import { toggleUpdateAudioSettings } from '../base/tracks/actions.web';
|
||||
import { getLocalJitsiAudioTrack } from '../base/tracks/functions.any';
|
||||
import { toggleNoiseSuppression } from '../noise-suppression/actions';
|
||||
import { setScreenshareFramerate } from '../screen-share/actions';
|
||||
import { setScreenshareFramerate } from '../screen-share/actions.web';
|
||||
import { setAudioSettings } from '../settings/actions.web';
|
||||
import { disposePreviewAudioTrack } from '../settings/functions.web';
|
||||
|
||||
import { getAudioDeviceSelectionDialogProps, getVideoDeviceSelectionDialogProps } from './functions';
|
||||
import logger from './logger';
|
||||
@@ -22,16 +28,21 @@ import logger from './logger';
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function submitAudioDeviceSelectionTab(newState: any, isDisplayedOnWelcomePage: boolean) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const currentState = getAudioDeviceSelectionDialogProps(getState(), isDisplayedOnWelcomePage);
|
||||
const isSelectedAudioInputIdChanged = newState.selectedAudioInputId
|
||||
&& newState.selectedAudioInputId !== currentState.selectedAudioInputId;
|
||||
|
||||
if (newState.selectedAudioInputId && newState.selectedAudioInputId !== currentState.selectedAudioInputId) {
|
||||
if (isSelectedAudioInputIdChanged) {
|
||||
dispatch(updateSettings({
|
||||
userSelectedMicDeviceId: newState.selectedAudioInputId,
|
||||
userSelectedMicDeviceLabel:
|
||||
getDeviceLabelById(getState(), newState.selectedAudioInputId, 'audioInput')
|
||||
}));
|
||||
|
||||
await disposePreviewAudioTrack(getState());
|
||||
|
||||
dispatch(setAudioSettings(newState.audioSettings));
|
||||
dispatch(setAudioInputDevice(newState.selectedAudioInputId));
|
||||
}
|
||||
|
||||
@@ -58,6 +69,21 @@ export function submitAudioDeviceSelectionTab(newState: any, isDisplayedOnWelcom
|
||||
if (newState.noiseSuppressionEnabled !== currentState.noiseSuppressionEnabled) {
|
||||
dispatch(toggleNoiseSuppression());
|
||||
}
|
||||
|
||||
if (!isEqual(newState.audioSettings, currentState.audioSettings) && !isSelectedAudioInputIdChanged && !newState.noiseSuppressionEnabled) {
|
||||
const state = getState();
|
||||
const jitsiTrack = getLocalJitsiAudioTrack(state);
|
||||
|
||||
if (!jitsiTrack) {
|
||||
logger.debug('No local audio track found');
|
||||
dispatch(setAudioSettings(newState.audioSettings));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await disposePreviewAudioTrack(getState());
|
||||
dispatch(toggleUpdateAudioSettings(newState.audioSettings));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
import { Theme } from '@mui/material';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { withStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { isAdvancedAudioSettingsEnabled } from '../../base/config/functions.any';
|
||||
import { getAvailableDevices } from '../../base/devices/actions.web';
|
||||
import AbstractDialogTab, {
|
||||
type IProps as AbstractDialogTabProps
|
||||
} from '../../base/dialog/components/web/AbstractDialogTab';
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import { createLocalTrack } from '../../base/lib-jitsi-meet/functions.web';
|
||||
import { IAudioSettings } from '../../base/settings/reducer';
|
||||
import Checkbox from '../../base/ui/components/web/Checkbox';
|
||||
import { setPreviewAudioTrack } from '../../settings/actions.web';
|
||||
import { disposeTrack } from '../../settings/functions.web';
|
||||
import { iAmVisitor as iAmVisitorCheck } from '../../visitors/functions';
|
||||
import logger from '../logger';
|
||||
|
||||
import AudioInputPreview from './AudioInputPreview';
|
||||
import AudioOutputPreview from './AudioOutputPreview';
|
||||
import AudioInputPreview from './AudioInputPreview.web';
|
||||
import AudioOutputPreview from './AudioOutputPreview.web';
|
||||
import DeviceHidContainer from './DeviceHidContainer.web';
|
||||
import DeviceSelector from './DeviceSelector.web';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AudioDevicesSelection}.
|
||||
*/
|
||||
interface IProps extends AbstractDialogTabProps, WithTranslation {
|
||||
|
||||
/**
|
||||
* The audio local track settings.
|
||||
*/
|
||||
audioSettings?: IAudioSettings;
|
||||
|
||||
/**
|
||||
* All known audio and video devices split by type. This prop comes from
|
||||
* the app state.
|
||||
@@ -98,11 +109,21 @@ interface IProps extends AbstractDialogTabProps, WithTranslation {
|
||||
*/
|
||||
iAmVisitor: boolean;
|
||||
|
||||
/**
|
||||
* Whether the advanced audio settings are enabled from config.
|
||||
*/
|
||||
isAdvancedAudioSettingsConfigEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether noise suppression is on or not.
|
||||
*/
|
||||
noiseSuppressionEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The audio track that is used for previewing the audio input.
|
||||
*/
|
||||
previewAudioTrack: any | null;
|
||||
|
||||
/**
|
||||
* The id of the audio input device to preview.
|
||||
*/
|
||||
@@ -114,19 +135,20 @@ interface IProps extends AbstractDialogTabProps, WithTranslation {
|
||||
selectedAudioOutputId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link AudioDevicesSelection}.
|
||||
*/
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* The JitsiTrack to use for previewing audio input.
|
||||
*/
|
||||
previewAudioTrack?: any | null;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) => {
|
||||
return {
|
||||
checkbox: {
|
||||
width: 'max-content'
|
||||
},
|
||||
|
||||
checkboxGrid: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'auto auto',
|
||||
gap: theme.spacing(3),
|
||||
margin: `${theme.spacing(3)} 0`
|
||||
},
|
||||
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column' as const,
|
||||
@@ -134,32 +156,33 @@ const styles = (theme: Theme) => {
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
hidden: {
|
||||
display: 'none'
|
||||
},
|
||||
|
||||
inputContainer: {
|
||||
marginBottom: theme.spacing(3)
|
||||
},
|
||||
|
||||
outputContainer: {
|
||||
margin: `${theme.spacing(5)} 0`,
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end'
|
||||
alignItems: 'flex-end',
|
||||
margin: `${theme.spacing(5)} 0`
|
||||
},
|
||||
|
||||
outputButton: {
|
||||
marginLeft: theme.spacing(3)
|
||||
},
|
||||
|
||||
noiseSuppressionContainer: {
|
||||
marginBottom: theme.spacing(5)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* React {@code Component} for previewing audio and video input/output devices.
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
class AudioDevicesSelection extends AbstractDialogTab<IProps, {}> {
|
||||
|
||||
/**
|
||||
* Whether current component is mounted or not.
|
||||
@@ -181,9 +204,6 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
previewAudioTrack: null
|
||||
};
|
||||
this._unMounted = true;
|
||||
}
|
||||
|
||||
@@ -224,9 +244,29 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
*/
|
||||
override componentWillUnmount() {
|
||||
this._unMounted = true;
|
||||
this._disposeAudioInputPreview();
|
||||
disposeTrack(this.props.previewAudioTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the audio settings based on the input change event and updates the state.
|
||||
*
|
||||
* @param {Object} e - The key event to handle.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleAudioSettings = ({ target: { checked, name } }: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { audioSettings } = this.props;
|
||||
|
||||
const newValue = name === 'channelCount' ? (checked ? 2 : 1) : checked;
|
||||
|
||||
super._onChange({
|
||||
audioSettings: {
|
||||
...audioSettings,
|
||||
[name]: newValue
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -234,11 +274,13 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
*/
|
||||
override render() {
|
||||
const {
|
||||
audioSettings,
|
||||
hasAudioPermission,
|
||||
hideAudioInputPreview,
|
||||
hideAudioOutputPreview,
|
||||
hideDeviceHIDContainer,
|
||||
hideNoiseSuppression,
|
||||
isAdvancedAudioSettingsConfigEnabled,
|
||||
iAmVisitor,
|
||||
noiseSuppressionEnabled,
|
||||
selectedAudioOutputId,
|
||||
@@ -248,6 +290,13 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
const isAudioSettingsEnabled = Boolean(audioSettings?.autoGainControl || audioSettings?.channelCount === 2 || audioSettings?.echoCancellation || audioSettings?.noiseSuppression);
|
||||
|
||||
const shouldDisplayNoiseSuppressionCheckbox = !hideNoiseSuppression && !iAmVisitor;
|
||||
const shouldDisplayAdvancedAudioSettingsCheckboxes = !hideNoiseSuppression && !iAmVisitor && isAdvancedAudioSettingsConfigEnabled && Boolean(audioSettings);
|
||||
|
||||
const shouldDisabledNoiseSupressionCheckbox = shouldDisplayAdvancedAudioSettingsCheckboxes && (isAudioSettingsEnabled && !noiseSuppressionEnabled);
|
||||
|
||||
return (
|
||||
<div className = { classes.container }>
|
||||
{!iAmVisitor && <div
|
||||
@@ -255,9 +304,60 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
className = { classes.inputContainer }>
|
||||
{this._renderSelector(audioInput)}
|
||||
</div>}
|
||||
|
||||
{!hideAudioInputPreview && hasAudioPermission && !iAmVisitor
|
||||
&& <AudioInputPreview
|
||||
track = { this.state.previewAudioTrack } />}
|
||||
&& <AudioInputPreview
|
||||
track = { this.props.previewAudioTrack } />}
|
||||
|
||||
<fieldset className = { classes.checkboxGrid }>
|
||||
<Checkbox
|
||||
checked = { Boolean(audioSettings?.echoCancellation) }
|
||||
className = { clsx(classes.checkbox, {
|
||||
[classes.hidden]: !shouldDisplayAdvancedAudioSettingsCheckboxes,
|
||||
}) }
|
||||
disabled = { noiseSuppressionEnabled }
|
||||
label = { t('toolbar.advancedAudioSettings.aec.label') }
|
||||
name = { 'echoCancellation' }
|
||||
onChange = { this._onToggleAudioSettings } />
|
||||
<Checkbox
|
||||
checked = { Boolean(audioSettings?.channelCount === 2) }
|
||||
className = { clsx(classes.checkbox, {
|
||||
[classes.hidden]: !shouldDisplayAdvancedAudioSettingsCheckboxes,
|
||||
}) }
|
||||
disabled = { noiseSuppressionEnabled }
|
||||
label = { t('toolbar.advancedAudioSettings.stereo.label') }
|
||||
name = { 'channelCount' }
|
||||
onChange = { this._onToggleAudioSettings } />
|
||||
<Checkbox
|
||||
checked = { Boolean(audioSettings?.autoGainControl) }
|
||||
className = { clsx(classes.checkbox, {
|
||||
[classes.hidden]: !shouldDisplayAdvancedAudioSettingsCheckboxes,
|
||||
}) }
|
||||
disabled = { noiseSuppressionEnabled }
|
||||
label = { t('toolbar.advancedAudioSettings.agc.label') }
|
||||
name = { 'autoGainControl' }
|
||||
onChange = { this._onToggleAudioSettings } />
|
||||
<Checkbox
|
||||
checked = { Boolean(audioSettings?.noiseSuppression) }
|
||||
className = { clsx(classes.checkbox, {
|
||||
[classes.hidden]: !shouldDisplayAdvancedAudioSettingsCheckboxes,
|
||||
}) }
|
||||
disabled = { noiseSuppressionEnabled }
|
||||
label = { t('toolbar.advancedAudioSettings.ns.label') }
|
||||
name = { 'noiseSuppression' }
|
||||
onChange = { this._onToggleAudioSettings } />
|
||||
<Checkbox
|
||||
checked = { noiseSuppressionEnabled }
|
||||
className = { clsx(classes.checkbox, {
|
||||
[classes.hidden]: !shouldDisplayNoiseSuppressionCheckbox,
|
||||
}) }
|
||||
disabled = { shouldDisabledNoiseSupressionCheckbox }
|
||||
label = { t('toolbar.enableNoiseSuppression') }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onChange = { () => super._onChange({
|
||||
noiseSuppressionEnabled: !noiseSuppressionEnabled
|
||||
}) } />
|
||||
</fieldset>
|
||||
<div
|
||||
aria-live = 'polite'
|
||||
className = { classes.outputContainer }>
|
||||
@@ -267,17 +367,7 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
className = { classes.outputButton }
|
||||
deviceId = { selectedAudioOutputId } />}
|
||||
</div>
|
||||
{!hideNoiseSuppression && !iAmVisitor && (
|
||||
<div className = { classes.noiseSuppressionContainer }>
|
||||
<Checkbox
|
||||
checked = { noiseSuppressionEnabled }
|
||||
label = { t('toolbar.enableNoiseSuppression') }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onChange = { () => super._onChange({
|
||||
noiseSuppressionEnabled: !noiseSuppressionEnabled
|
||||
}) } />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!hideDeviceHIDContainer && !iAmVisitor
|
||||
&& <DeviceHidContainer />}
|
||||
</div>
|
||||
@@ -292,13 +382,13 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_createAudioInputTrack(deviceId: string) {
|
||||
const { hideAudioInputPreview } = this.props;
|
||||
const { hideAudioInputPreview, previewAudioTrack } = this.props;
|
||||
|
||||
if (hideAudioInputPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this._disposeAudioInputPreview()
|
||||
return disposeTrack(previewAudioTrack)
|
||||
.then(() => createLocalTrack('audio', deviceId, 5000))
|
||||
.then(jitsiLocalTrack => {
|
||||
if (this._unMounted) {
|
||||
@@ -306,29 +396,14 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
|
||||
return;
|
||||
}
|
||||
this.props.dispatch(setPreviewAudioTrack(jitsiLocalTrack));
|
||||
|
||||
this.setState({
|
||||
previewAudioTrack: jitsiLocalTrack
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState({
|
||||
previewAudioTrack: null
|
||||
});
|
||||
this.props.dispatch(setPreviewAudioTrack(null));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for disposing the current audio input preview.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_disposeAudioInputPreview(): Promise<any> {
|
||||
return this.state.previewAudioTrack
|
||||
? this.state.previewAudioTrack.dispose() : Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DeviceSelector instance based on the passed in configuration.
|
||||
*
|
||||
@@ -362,8 +437,8 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
id: 'audioInput',
|
||||
label: 'settings.selectMic',
|
||||
onSelect: (selectedAudioInputId: string) => super._onChange({ selectedAudioInputId }),
|
||||
selectedDeviceId: this.state.previewAudioTrack
|
||||
? this.state.previewAudioTrack.getDeviceId() : this.props.selectedAudioInputId
|
||||
selectedDeviceId: this.props.previewAudioTrack
|
||||
? this.props.previewAudioTrack.getDeviceId() : this.props.selectedAudioInputId
|
||||
};
|
||||
let audioOutput;
|
||||
|
||||
@@ -389,7 +464,9 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
const mapStateToProps = (state: IReduxState) => {
|
||||
return {
|
||||
availableDevices: state['features/base/devices'].availableDevices ?? {},
|
||||
iAmVisitor: iAmVisitorCheck(state)
|
||||
isAdvancedAudioSettingsConfigEnabled: isAdvancedAudioSettingsEnabled(state),
|
||||
iAmVisitor: iAmVisitorCheck(state),
|
||||
previewAudioTrack: state['features/settings'].previewAudioTrack
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
getUserSelectedMicDeviceId,
|
||||
getUserSelectedOutputDeviceId
|
||||
} from '../base/settings/functions.web';
|
||||
import { getLocalJitsiAudioTrackSettings } from '../base/tracks/functions.any';
|
||||
import { isNoiseSuppressionEnabled } from '../noise-suppression/functions';
|
||||
import { isPrejoinPageVisible } from '../prejoin/functions';
|
||||
import { SS_DEFAULT_FRAME_RATE, SS_SUPPORTED_FRAMERATES } from '../settings/constants';
|
||||
@@ -50,6 +51,7 @@ export function getAudioDeviceSelectionDialogProps(stateful: IStateful, isDispla
|
||||
const deviceHidSupported = isDeviceHidSupported() && getWebHIDFeatureConfig(state);
|
||||
const noiseSuppressionEnabled = isNoiseSuppressionEnabled(state);
|
||||
const hideNoiseSuppression = isPrejoinPageVisible(state) || isDisplayedOnWelcomePage;
|
||||
const audioSettings = state['features/settings'].audioSettings ?? getLocalJitsiAudioTrackSettings(state);
|
||||
|
||||
// When the previews are disabled we don't need multiple audio input support in order to change the mic. This is the
|
||||
// case for Safari on iOS.
|
||||
@@ -71,6 +73,7 @@ export function getAudioDeviceSelectionDialogProps(stateful: IStateful, isDispla
|
||||
// we fill the device selection dialog with the devices that are currently
|
||||
// used or if none are currently used with what we have in settings(user selected)
|
||||
return {
|
||||
audioSettings,
|
||||
disableAudioInputChange,
|
||||
disableDeviceChange: !JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(),
|
||||
hasAudioPermission: permissions.audio,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/device-selection');
|
||||
export default getLogger('app:device-selection');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/dropbox');
|
||||
export default getLogger('app:dropbox');
|
||||
|
||||
@@ -2,6 +2,7 @@ import { IReduxState } from '../app/types';
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { toState } from '../base/redux/functions';
|
||||
|
||||
import { cleanSvg } from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
@@ -82,7 +83,7 @@ export const fetchCustomIcons = async (customIcons: Record<string, string>) => {
|
||||
if (response.ok) {
|
||||
const svgXml = await response.text();
|
||||
|
||||
localCustomIcons[key] = svgXml;
|
||||
localCustomIcons[key] = cleanSvg(svgXml);
|
||||
} else {
|
||||
logger.error(`Failed to fetch ${url}. Status: ${response.status}`);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user