mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-22 03:27:47 +00:00
Compare commits
21 Commits
5405
...
new-update
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63754f01a5 | ||
|
|
dcf677bdf0 | ||
|
|
eaea2cdef0 | ||
|
|
bcad87e894 | ||
|
|
d500493649 | ||
|
|
8ad3ef0022 | ||
|
|
d0664d9ea0 | ||
|
|
45c40d7afc | ||
|
|
e33edfd2a5 | ||
|
|
96b2a07172 | ||
|
|
0e8c01af75 | ||
|
|
3049ad6dbf | ||
|
|
9a30ad87a7 | ||
|
|
11c05560c4 | ||
|
|
d92fa59c74 | ||
|
|
4eb1d16faa | ||
|
|
91adc70724 | ||
|
|
15b083c182 | ||
|
|
26fdaf7b8d | ||
|
|
64a9874d63 | ||
|
|
753cffc458 |
@@ -26,5 +26,5 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=21.4.0
|
||||
sdkVersion=3.10.0
|
||||
appVersion=21.4.1
|
||||
sdkVersion=3.10.2
|
||||
|
||||
@@ -296,6 +296,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
config = in.readBundle();
|
||||
featureFlags = in.readBundle();
|
||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||
byte tmpAudioMuted = in.readByte();
|
||||
}
|
||||
|
||||
Bundle asProps() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
|
||||
public class NotificationChannels {
|
||||
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
|
||||
static final String ONGOING_CONFERNCE_CHANNEL_NAME = "Ongoing Conference Notifications";
|
||||
|
||||
public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
|
||||
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERNCE_CHANNEL_NAME;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
@@ -65,7 +66,7 @@ class OngoingNotification {
|
||||
return;
|
||||
}
|
||||
|
||||
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_action_unmute), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, ONGOING_CONFERNCE_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false);
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
<string name="ongoing_notification_action_hang_up">Hang up</string>
|
||||
<string name="ongoing_notification_action_mute">Mute</string>
|
||||
<string name="ongoing_notification_action_unmute">Unmute</string>
|
||||
<string name="ongoing_notification_channel_name">Ongoing Conference Notifications</string>
|
||||
</resources>
|
||||
|
||||
@@ -129,8 +129,7 @@ import {
|
||||
initPrejoin,
|
||||
isPrejoinPageEnabled,
|
||||
isPrejoinPageVisible,
|
||||
makePrecallTest,
|
||||
setJoiningInProgress
|
||||
makePrecallTest
|
||||
} from './react/features/prejoin';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
||||
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
||||
@@ -157,15 +156,6 @@ let connection;
|
||||
*/
|
||||
let _connectionPromise;
|
||||
|
||||
/**
|
||||
* We are storing the resolve function of a Promise that waits for the _connectionPromise to be created. This is needed
|
||||
* when the prejoin button was pressed before the conference object was initialized and the _connectionPromise has not
|
||||
* been initialized when we tried to execute prejoinStart. In this case in prejoinStart we create a new Promise, assign
|
||||
* the resolve function to this variable and wait for the promise to resolve before we continue. The
|
||||
* _onConnectionPromiseCreated will be called once the _connectionPromise is created.
|
||||
*/
|
||||
let _onConnectionPromiseCreated;
|
||||
|
||||
/**
|
||||
* This promise is used for chaining mutePresenterVideo calls in order to avoid calling GUM multiple times if it takes
|
||||
* a while to finish.
|
||||
@@ -804,10 +794,6 @@ export default {
|
||||
return c;
|
||||
});
|
||||
|
||||
if (_onConnectionPromiseCreated) {
|
||||
_onConnectionPromiseCreated();
|
||||
}
|
||||
|
||||
APP.store.dispatch(makePrecallTest(this._getConferenceOptions()));
|
||||
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
@@ -851,26 +837,12 @@ export default {
|
||||
* Joins conference after the tracks have been configured in the prejoin screen.
|
||||
*
|
||||
* @param {Object[]} tracks - An array with the configured tracks
|
||||
* @returns {void}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async prejoinStart(tracks) {
|
||||
if (!_connectionPromise) {
|
||||
// The conference object isn't initialized yet. Wait for the promise to initialise.
|
||||
await new Promise(resolve => {
|
||||
_onConnectionPromiseCreated = resolve;
|
||||
});
|
||||
_onConnectionPromiseCreated = undefined;
|
||||
}
|
||||
const con = await _connectionPromise;
|
||||
|
||||
let con;
|
||||
|
||||
try {
|
||||
con = await _connectionPromise;
|
||||
this.startConference(con, tracks);
|
||||
} catch (error) {
|
||||
logger.error(`An error occurred while trying to join a meeting from the prejoin screen: ${error}`);
|
||||
APP.store.dispatch(setJoiningInProgress(false));
|
||||
}
|
||||
return this.startConference(con, tracks);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1531,14 +1503,14 @@ export default {
|
||||
*
|
||||
* @param {boolean} didHaveVideo indicates if there was a camera video being
|
||||
* used, before switching to screen sharing.
|
||||
* @param {boolean} ignoreDidHaveVideo indicates if the camera video should be
|
||||
* ignored when switching screen sharing off.
|
||||
* @param {boolean} wasVideoMuted indicates if the video was muted, before
|
||||
* switching to screen sharing.
|
||||
* @return {Promise} resolved after the screen sharing is turned off, or
|
||||
* rejected with some error (no idea what kind of error, possible GUM error)
|
||||
* in case it fails.
|
||||
* @private
|
||||
*/
|
||||
async _turnScreenSharingOff(didHaveVideo, ignoreDidHaveVideo) {
|
||||
async _turnScreenSharingOff(didHaveVideo) {
|
||||
this._untoggleScreenSharing = null;
|
||||
this.videoSwitchInProgress = true;
|
||||
|
||||
@@ -1582,7 +1554,7 @@ export default {
|
||||
|
||||
APP.store.dispatch(setScreenAudioShareState(false));
|
||||
|
||||
if (didHaveVideo && !ignoreDidHaveVideo) {
|
||||
if (didHaveVideo) {
|
||||
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
|
||||
.then(([ stream ]) => {
|
||||
logger.debug(`_turnScreenSharingOff using ${stream} for useVideoStream`);
|
||||
@@ -1633,10 +1605,9 @@ export default {
|
||||
* @param {Array<string>} [options.desktopSharingSources] - Array with the
|
||||
* sources that have to be displayed in the desktop picker window ('screen',
|
||||
* 'window', etc.).
|
||||
* @param {boolean} ignoreDidHaveVideo - if true ignore if video was on when sharing started.
|
||||
* @return {Promise.<T>}
|
||||
*/
|
||||
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}, ignoreDidHaveVideo) {
|
||||
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
|
||||
logger.debug(`toggleScreenSharing: ${toggle}`);
|
||||
if (this.videoSwitchInProgress) {
|
||||
return Promise.reject('Switch in progress.');
|
||||
@@ -1662,7 +1633,7 @@ export default {
|
||||
}
|
||||
|
||||
return this._untoggleScreenSharing
|
||||
? this._untoggleScreenSharing(ignoreDidHaveVideo)
|
||||
? this._untoggleScreenSharing()
|
||||
: Promise.resolve();
|
||||
},
|
||||
|
||||
@@ -2505,8 +2476,8 @@ export default {
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.TOGGLE_SCREENSHARING, ({ enabled, audioOnly, ignoreDidHaveVideo }) => {
|
||||
this.toggleScreenSharing(enabled, { audioOnly }, ignoreDidHaveVideo);
|
||||
UIEvents.TOGGLE_SCREENSHARING, ({ enabled, audioOnly }) => {
|
||||
this.toggleScreenSharing(enabled, { audioOnly });
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
17
config.js
17
config.js
@@ -545,19 +545,6 @@ var config = {
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// Holds values related to toolbar visibility control.
|
||||
// toolbarConfig: {
|
||||
// // Moved from interfaceConfig.INITIAL_TOOLBAR_TIMEOUT
|
||||
// // The initial numer of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// initialTimeout: 20000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_TIMEOUT
|
||||
// // Number of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// timeout: 4000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_ALWAYS_VISIBLE
|
||||
// // Whether toolbar should be always visible or should hide after x miliseconds.
|
||||
// alwaysVisible: false
|
||||
// },
|
||||
|
||||
// Toolbar buttons which have their click event exposed through the API on
|
||||
// `toolbarButtonClicked` event instead of executing the normal click routine.
|
||||
// buttonsWithNotifyClick: [
|
||||
@@ -909,10 +896,6 @@ var config = {
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
// When true the user cannot add more images to be used as virtual background.
|
||||
// Only the default ones from will be available.
|
||||
// disableAddingBackgroundImages: false,
|
||||
|
||||
// Sets the background transparency level. '0' is fully transparent, '1' is opaque.
|
||||
// backgroundAlpha: 1,
|
||||
|
||||
|
||||
@@ -213,7 +213,6 @@
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
-ms-overflow-style: none;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
@@ -222,13 +221,8 @@
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
scrollbar-width: none;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#usermsg:hover {
|
||||
|
||||
@@ -75,7 +75,3 @@
|
||||
top: 0;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.shift-right .details-container {
|
||||
margin-left: calc(#{$sidebarWidth} / 2);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ var interfaceConfig = {
|
||||
|
||||
CLOSE_PAGE_GUEST_HINT: false, // A html text to be shown to guests on the close page, false disables it
|
||||
|
||||
// Connection indicators (
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT,
|
||||
// CONNECTION_INDICATOR_DISABLED) got moved to config.js.
|
||||
|
||||
DEFAULT_BACKGROUND: '#474747',
|
||||
DEFAULT_LOCAL_DISPLAY_NAME: 'me',
|
||||
DEFAULT_LOGO_URL: 'images/watermark.svg',
|
||||
@@ -34,6 +39,9 @@ var interfaceConfig = {
|
||||
|
||||
DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
|
||||
|
||||
// Deprecated. Please use disableModeratorIndicator from config.js
|
||||
// DISABLE_FOCUS_INDICATOR: false,
|
||||
|
||||
/**
|
||||
* If true, notifications regarding joining/leaving are no longer displayed.
|
||||
*/
|
||||
@@ -89,6 +97,7 @@ var interfaceConfig = {
|
||||
*/
|
||||
HIDE_INVITE_MORE_HEADER: false,
|
||||
|
||||
INITIAL_TOOLBAR_TIMEOUT: 20000,
|
||||
JITSI_WATERMARK_LINK: 'https://jitsi.org',
|
||||
|
||||
LANG_DETECTION: true, // Allow i18n to detect the system language
|
||||
@@ -174,6 +183,16 @@ var interfaceConfig = {
|
||||
*/
|
||||
SUPPORT_URL: 'https://community.jitsi.org/',
|
||||
|
||||
TOOLBAR_ALWAYS_VISIBLE: false,
|
||||
|
||||
/**
|
||||
* DEPRECATED!
|
||||
* This config was moved to config.js as `toolbarButtons`.
|
||||
*/
|
||||
// TOOLBAR_BUTTONS: [],
|
||||
|
||||
TOOLBAR_TIMEOUT: 4000,
|
||||
|
||||
// Browsers, in addition to those which do not fully support WebRTC, that
|
||||
// are not supported and should show the unsupported browser page.
|
||||
UNSUPPORTED_BROWSERS: [],
|
||||
@@ -238,28 +257,6 @@ var interfaceConfig = {
|
||||
PHONE_NUMBER_REGEX
|
||||
*/
|
||||
|
||||
// -----------------DEPRECATED CONFIGS BELOW THIS LINE-----------------------------
|
||||
|
||||
// Connection indicators (
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT,
|
||||
// CONNECTION_INDICATOR_DISABLED) got moved to config.js.
|
||||
|
||||
// Please use disableModeratorIndicator from config.js
|
||||
// DISABLE_FOCUS_INDICATOR: false,
|
||||
|
||||
// Moved to config.js as `toolbarConfig.initialTimeout`.
|
||||
// INITIAL_TOOLBAR_TIMEOUT: 20000,
|
||||
|
||||
// Moved to config.js as `toolbarConfig.alwaysVisible`.
|
||||
// TOOLBAR_ALWAYS_VISIBLE: false,
|
||||
|
||||
// This config was moved to config.js as `toolbarButtons`.
|
||||
// TOOLBAR_BUTTONS: [],
|
||||
|
||||
// Moved to config.js as `toolbarConfig.timeout`.
|
||||
// TOOLBAR_TIMEOUT: 4000,
|
||||
|
||||
// Allow all above example options to include a trailing comma and
|
||||
// prevent fear when commenting out the last value.
|
||||
// eslint-disable-next-line sort-keys
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
platform :ios, '12.0'
|
||||
platform :ios, '11.0'
|
||||
workspace 'jitsi-meet'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
install! 'cocoapods', :deterministic_uuids => false
|
||||
@@ -87,7 +87,7 @@ post_install do |installer|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['ENABLE_BITCODE'] = 'YES'
|
||||
config.build_settings['SUPPORTS_MACCATALYST'] = 'NO'
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -605,6 +605,6 @@ SPEC CHECKSUMS:
|
||||
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
|
||||
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
|
||||
|
||||
PODFILE CHECKSUM: 3cc305fd6ee83fff506c10c4805471fa72b61c9a
|
||||
PODFILE CHECKSUM: e830b1b5a46d340e22689b146b55dcf24664c6f1
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
@@ -996,7 +996,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1049,7 +1049,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.4.0</string>
|
||||
<string>21.4.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.4.0</string>
|
||||
<string>21.4.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,6 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Availability.h>
|
||||
|
||||
@import CoreSpotlight;
|
||||
@import MobileCoreServices;
|
||||
@import Intents; // Needed for NSUserActivity suggestedInvocationPhrase
|
||||
@@ -55,29 +58,34 @@
|
||||
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
|
||||
|
||||
// Register a NSUserActivity for this conference so it can be invoked as a
|
||||
// Siri shortcut.
|
||||
NSUserActivity *userActivity
|
||||
= [[NSUserActivity alloc] initWithActivityType:JitsiMeetConferenceActivityType];
|
||||
// Siri shortcut. This is only supported in iOS >= 12.
|
||||
#ifdef __IPHONE_12_0
|
||||
if (@available(iOS 12.0, *)) {
|
||||
NSUserActivity *userActivity
|
||||
= [[NSUserActivity alloc] initWithActivityType:JitsiMeetConferenceActivityType];
|
||||
|
||||
NSString *urlStr = data[@"url"];
|
||||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
NSString *conference = [url.pathComponents lastObject];
|
||||
NSString *urlStr = data[@"url"];
|
||||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
NSString *conference = [url.pathComponents lastObject];
|
||||
|
||||
userActivity.title = [NSString stringWithFormat:@"Join %@", conference];
|
||||
userActivity.suggestedInvocationPhrase = @"Join my Jitsi meeting";
|
||||
userActivity.userInfo = @{@"url": urlStr};
|
||||
[userActivity setEligibleForSearch:YES];
|
||||
[userActivity setEligibleForPrediction:YES];
|
||||
[userActivity setPersistentIdentifier:urlStr];
|
||||
userActivity.title = [NSString stringWithFormat:@"Join %@", conference];
|
||||
userActivity.suggestedInvocationPhrase = @"Join my Jitsi meeting";
|
||||
userActivity.userInfo = @{@"url": urlStr};
|
||||
[userActivity setEligibleForSearch:YES];
|
||||
[userActivity setEligibleForPrediction:YES];
|
||||
[userActivity setPersistentIdentifier:urlStr];
|
||||
|
||||
// Subtitle
|
||||
CSSearchableItemAttributeSet *attributes
|
||||
= [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
|
||||
attributes.contentDescription = urlStr;
|
||||
userActivity.contentAttributeSet = attributes;
|
||||
// Subtitle
|
||||
CSSearchableItemAttributeSet *attributes
|
||||
= [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
|
||||
attributes.contentDescription = urlStr;
|
||||
userActivity.contentAttributeSet = attributes;
|
||||
|
||||
self.userActivity = userActivity;
|
||||
[userActivity becomeCurrent];
|
||||
}
|
||||
#endif
|
||||
|
||||
self.userActivity = userActivity;
|
||||
[userActivity becomeCurrent];
|
||||
}
|
||||
|
||||
- (void)conferenceTerminated:(NSDictionary *)data {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.4.0</string>
|
||||
<string>21.4.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.4.0</string>
|
||||
<string>21.4.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -520,7 +520,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -576,7 +576,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.10.0</string>
|
||||
<string>3.10.4</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
}
|
||||
|
||||
- (void)destroyReactNativeBridge {
|
||||
[_bridgeWrapper invalidate];
|
||||
_bridgeWrapper = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,6 @@
|
||||
|
||||
@property (nonatomic, readonly, strong) RCTBridge *bridge;
|
||||
|
||||
- (void)invalidate;
|
||||
|
||||
@end
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)invalidate {
|
||||
[_bridge invalidate];
|
||||
}
|
||||
|
||||
#pragma mark helper methods for getting the packager URL
|
||||
|
||||
#if DEBUG
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"copyInvite": "انسخ دعوةً للاجتماع",
|
||||
"copyLink": "انسخ رابط الاجتماع",
|
||||
"copyStream": "انسخ رابط البث المباشر",
|
||||
"contacts": "contacts",
|
||||
"countryNotSupported": "لا ندعم هذه الوجهة حاليًا.",
|
||||
"countryReminder": "أتريد الاتصال بمن هو خارج الولايات المتحدة؟ تأكد من الابتداء برمز الدولة أولًا!",
|
||||
"defaultEmail": "بريدك الافتراضي",
|
||||
@@ -17,14 +16,18 @@
|
||||
"inviteMoreMailSubject": "ضم {{appName}} للاجتماع",
|
||||
"inviteMorePrompt": "ادعُ أشخاصًا آخرين",
|
||||
"linkCopied": "نُسِخ الرابط",
|
||||
"loading": "البحث عن أشخاص وأرقام هواتف",
|
||||
"loadingNumber": "التحقق من رقم الهاتف",
|
||||
"loadingPeople": "البحث عن أشخاص لإضافتهم",
|
||||
"noResults": "لم يُعثَر على أي نتيجة بحث متطابقة",
|
||||
"noValidNumbers": "ادخل رقم هاتف، رجاءً",
|
||||
"outlookEmail": "بريد مايكروسوفت",
|
||||
"phoneNumbers": "أضف ارقام هواتف",
|
||||
"search": "ابحث عن أشخاص",
|
||||
"searchNumbers": "أضف ارقام هواتف",
|
||||
"searchPeople": "ابحث عن أشخاص",
|
||||
"searchPeopleAndNumbers": "ابحث عن أشخاص أو أضف أرقام هواتفهم",
|
||||
"shareInvite": "شارك دعوةً للاجتماع",
|
||||
"shareLink": "شارك رابط الاجتماع لدعوة الأخرين",
|
||||
"shareStream": "شارك رابط البث المباشر للاجتماع",
|
||||
"sipAddresses": "sip عنوان",
|
||||
"telephone": "رقم الهاتف: {{number}}",
|
||||
"title": "ادعُ أحدًا لهذا الاجتماع",
|
||||
"yahooEmail": "بريد ياهوو"
|
||||
@@ -58,7 +61,6 @@
|
||||
"today": "اليوم"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "أدخل الغرفة",
|
||||
"error": "خطأ: لم تُرسَل رسالتك. السبب: {{error}}",
|
||||
"fieldPlaceHolder": "اكتب رسالتك هنا",
|
||||
"messagebox": "اكتب رسالة",
|
||||
@@ -70,14 +72,6 @@
|
||||
"titleWithPolls": "اكتب لقبًا لاعتماده في المحادثة"
|
||||
},
|
||||
"privateNotice": "أرسل رسالة خاصة إلى {{recipient}}",
|
||||
"message": "رسالة",
|
||||
"messageAccessibleTitle": "{{user}} مقولة:",
|
||||
"messageAccessibleTitleMe": "أنا أقول:",
|
||||
"smileysPanel": "واجهة الإيموجي",
|
||||
"tabs": {
|
||||
"chat": "دردشة",
|
||||
"polls": "تصويت"
|
||||
},
|
||||
"title": "محادثة",
|
||||
"titleWithPolls": "محادثة",
|
||||
"you": "أنت"
|
||||
@@ -85,8 +79,7 @@
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": "نزِّل الإضافة للدمج مع رزنامة غوغل ورزنامة أوفيس 365",
|
||||
"buttonText": "نزِّل إضافة متصفح كروم",
|
||||
"dontShowAgain": "لا ترني هذه مرة أخرى",
|
||||
"close": "إغلق"
|
||||
"dontShowAgain": "لا ترني هذه مرة أخرى"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "قيد وصلك بالإجتماع..."
|
||||
@@ -108,7 +101,6 @@
|
||||
},
|
||||
"connectionindicator": {
|
||||
"address": "العنوان:",
|
||||
"audio_ssrc": "مصدر الصوت:",
|
||||
"bandwidth": "مُعدَّل البيانات المتبادلة:",
|
||||
"bitrate": "مُعدَّل البث:",
|
||||
"bridgeCount": "عدد المخدِّمات: ",
|
||||
@@ -136,12 +128,9 @@
|
||||
"remoteport": "المنفذ البعيد:",
|
||||
"remoteport_plural": "المنافذ البعيدة:",
|
||||
"resolution": "الدقة:",
|
||||
"savelogs": "خزن الوقوعات",
|
||||
"participant_id": "معرّف المشترك:",
|
||||
"status": "الاتصال:",
|
||||
"transport": "النقل:",
|
||||
"transport_plural": "النواقل:",
|
||||
"video_ssrc": "مصدر الصورة:"
|
||||
"transport_plural": "النواقل:"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "سابقًا",
|
||||
@@ -183,7 +172,6 @@
|
||||
"alreadySharedVideoMsg": "يشارك أحد الحضور الفيديو حاليًا، ولا يسمح هذا الإجتماع سوى بمشاركة فيديو واحد في آن واحد",
|
||||
"alreadySharedVideoTitle": "لا يُسمَح سوى بفيديو مشارك واحد على الأكثر في آن واحد.",
|
||||
"applicationWindow": "نافذة التطبيق",
|
||||
"authenticationRequired": "يستلزم التوثيق",
|
||||
"Back": "عد للخلف",
|
||||
"cameraConstraintFailedError": "لا تتوافق كاميرتك مع بعض الشروط المطلوبة.",
|
||||
"cameraNotFoundError": "لم يُعثَر على اي كاميرا",
|
||||
@@ -191,7 +179,6 @@
|
||||
"cameraNotSendingDataTitle": "يصعب الوصول الوصول إلى كاميرتك",
|
||||
"cameraPermissionDeniedError": "لم تُمنَح إذن استعمال الكاميرا. لا يزال بإمكانك حضور المؤتمر ولكن لن يراك الآخرون. استعمال زر الكاميرا في شريط العنوان لمحاول إصلاح المشكلة.",
|
||||
"cameraUnknownError": "يصعب استعمال الكاميرا لأسباب مجهولة.",
|
||||
"cameraTimeoutError": "تعذر بدء مصدر الفيديو. انتهت المهلة!",
|
||||
"cameraUnsupportedResolutionError": "لا تدعم كاميرتك دقة الفيديو المطلوبة.",
|
||||
"Cancel": "ِألغ",
|
||||
"close": "أغلق",
|
||||
@@ -209,20 +196,16 @@
|
||||
"copied": "نُسِخ!",
|
||||
"copy": "انسخ",
|
||||
"dismiss": "تجاهل",
|
||||
"displayNameRequired": "السلام عليكم! ما اسمك؟",
|
||||
"displayNameRequired": "السلام عليك! ما اسمك؟",
|
||||
"done": "اُنجِز",
|
||||
"e2eeDescription": "<p>عملية التعمية من طرف لطرف <strong>قيد التجريب</strong> حاليًا. زر رجاءً <a href='https://jitsi.org/blog/e2ee/' target='_blank'>هذا المنشور</a> لمزيد من التفاصيل.</p><br/><p>ضع في ذهنك أن تشغيل عملية التعمية من طرف لطرف ستعطل عمل بعض الخدمات التي يقدمها المُخدِّم مثل: التسجيل، والبث الحي، الاشتراك عبر الهاتف. أضف إلى ذلك أن الاجتماع سيعمل مع الأشخاص المنضمين من المتصفح التي تدعم قابلية الدخول إلى البث.</p>",
|
||||
"e2eeLabel": "المفتاح",
|
||||
"e2eeDisabledDueToMaxModeDescription": "لا يمكن تمكين التشفير من طرف إلى طرف بسبب العدد الكبير من المشاركين في المؤتمر.",
|
||||
"e2eeWarning": "تحذير: لا يبدو أن جميع المشاركين في هذا الاجتماع لديهم دعم للتشفير من طرف إلى طرف. إذا قمت بتمكينه فلن يتمكنوا من رؤيتك أو سماعك.",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "تحذير: سيتم تعطيل التشفير من طرف إلى طرف تلقائيًا إذا انضم المزيد من المشاركين إلى المؤتمر.",
|
||||
"e2eeWarning": "<br /><p><strong>تحذير:</strong>لا يبدو أن جميع الحاضرين في هذا الاجتماع يدعمون عملية التعمية طرف لطرف. إن فعَّلت العملية، فلن يتمكنوا من رؤيتك ولا سماعك.</p>",
|
||||
"enterDisplayName": "أدخل اسمك هنا، رجاءً",
|
||||
"embedMeeting": "تضمين الاجتماع",
|
||||
"error": "خطأ",
|
||||
"gracefulShutdown": "خدمتنا متوقفة حاليًا لعمليات الصيانة. جرب مرة أخرى في وقت لاحق.",
|
||||
"grantModeratorDialog": "أما زلت راغب بجعل هذا المشارك رئيس الجلسة؟",
|
||||
"grantModeratorTitle": "اجعله رئيس الجلسة",
|
||||
"hideShareAudioHelper": "لا تظهر هذا الحوار مرة أخرى",
|
||||
"IamHost": "أنا المضيف",
|
||||
"incorrectRoomLockPassword": "كلمة مرور خطأ",
|
||||
"incorrectPassword": "اسم المستخدم أو كلمة المرور خطأ",
|
||||
@@ -232,15 +215,15 @@
|
||||
"kickParticipantButton": "اطرد",
|
||||
"kickParticipantDialog": "أمتأكد من طرد هذا المشارك؟",
|
||||
"kickParticipantTitle": "أتريد طرد هذا المشارك؟",
|
||||
"kickTitle": "عذرًا! تم طردك {{participantDisplayName}} من الاجتماع",
|
||||
"kickTitle": "عذرًا! طردك {{participantDisplayName}} من الاجتماع",
|
||||
"liveStreaming": "البث المباشر الحي",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "غير ممكن أثناء التسجيل",
|
||||
"liveStreamingDisabledForGuestTooltip": "لا يمكن للضيوف بدء بث حي مباشر.",
|
||||
"liveStreamingDisabledTooltip": "بدء بثٍ حيٍّ مُعطَّل",
|
||||
"lockMessage": "فشل جعل المؤتمر مغلقًا.",
|
||||
"lockRoom": "أضف الاجتماع $t(lockRoomPasswordUppercase)",
|
||||
"lockTitle": "فشلت عملية القفل والإغلاق",
|
||||
"logoutQuestion": "أمتأكد من رغبتك في الخروج وإيقاف المؤتمر؟",
|
||||
"login": "تسجيل الدخول",
|
||||
"logoutTitle": "الخروج",
|
||||
"maxUsersLimitReached": "وصل عدد المشاركين إلى الحد الأقصى. المؤتمر مكتمل الحضور. اتصل رجاءً براعي المؤتمر أو جرب مرة أخرى لاحقًا.",
|
||||
"maxUsersLimitReachedTitle": "وصل عدد المشاركين إلى الحد الأقصى",
|
||||
@@ -249,46 +232,28 @@
|
||||
"micNotSendingData": "اذهب إلى إعدادات حاسوبك لإلغاء كتم المجهار (المايكروفون) وضبط مستواه",
|
||||
"micNotSendingDataTitle": "كتم ضبط الإعدادات لديك المجهار (المايكروفون)",
|
||||
"micPermissionDeniedError": "لم تعط إذن استعمال المجهار (المايكروفون) الخاص بك. ما يزال بإمكانك الإنضمام إلى المؤتمر ولكن دون أن يسمعك الآخرون. جرب استعمال زر الكاميرا في شريط العنوان لإصلاح هذه المشكلة.",
|
||||
"micTimeoutError": "Could not start audio source. Timeout occured!",
|
||||
"micUnknownError": "Cannot use microphone for an unknown reason.",
|
||||
"moderationAudioLabel": "Allow attendees to unmute themselves",
|
||||
"moderationVideoLabel": "Allow attendees to start their video",
|
||||
"micUnknownError": "يصعب استعمال المجهار (المايكروفون) الخاص بك لأسباب مجهولة.",
|
||||
"muteEveryoneElseDialog": "بمجرد أن تكتمهم، لن تتمكن نم إلغاء الكتم هذا، ولكن يمكنهم إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteEveryoneElseTitle": "أتريد كتم الجميع باستثناء {{whom}}?",
|
||||
"muteEveryoneDialog": "أمتأكد من رغبتك بكتم الجميع؟ لن تتمكن من إلغاء الكتم ولكن يمكنهم إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteEveryoneDialogModerationOn": "The participants can send a request to speak at any time.",
|
||||
"muteEveryoneTitle": "أتريد كتم الجميع؟",
|
||||
"muteEveryoneElsesVideoDialog": "Once the camera is disabled, you won't be able to turn it back on, but they can turn it back on at any time.",
|
||||
"muteEveryoneElsesVideoTitle": "Stop everyone's video except {{whom}}?",
|
||||
"muteEveryonesVideoDialog": "The participants can turn on their video at any time.",
|
||||
"muteEveryonesVideoDialogModerationOn": "The participants can send a request to turn on their video at any time.",
|
||||
"muteEveryonesVideoDialogOk": "Disable",
|
||||
"muteEveryonesVideoTitle": "Stop everyone's video?",
|
||||
"muteEveryoneSelf": "اكتم نفسك أيضًا",
|
||||
"muteEveryoneStartMuted": "البدء مع كتم الجميع من الآن فصاعدًا",
|
||||
"muteParticipantBody": "لن تتكمن من إلغاء كتمهم، ولكن يمكنهم إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteParticipantButton": "كتم",
|
||||
"muteParticipantDialog": "أمتأكد من رغبتك لهذا المشارك؟ لن تتمكن من إلغاء الكتم ولكن يمكنه إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteParticipantsVideoDialog": "هل أنت متأكد أنك تريد إيقاف تشغيل كاميرا هذا المشارك؟ لن تتمكن من إعادة تشغيل الكاميرا ، ولكن يمكنهم إعادة تشغيلها في أي وقت.",
|
||||
"muteParticipantTitle": "أتريد كتم هذا المشارك؟",
|
||||
"muteParticipantsVideoButton": "وقف الكاميرا",
|
||||
"muteParticipantsVideoTitle": "تعطيل الكاميرا لهذا المشارك؟",
|
||||
"muteParticipantsVideoBody": "لن تتمكن من إعادة تشغيل الكاميرا ، ولكن يمكنهم إعادة تشغيلها في أي وقت.",
|
||||
"noDropboxToken": "لا يوجد رمز مميز صالح لـ Dropbox",
|
||||
"Ok": "تمام",
|
||||
"password": "Password",
|
||||
"passwordLabel": "جعل عضو ما هذا الاجتماع مغلقًا. أدخل رجاءً $t(lockRoomPassword) للإنضمام.",
|
||||
"passwordNotSupported": "ضبط اجتماع $t(lockRoomPassword) غير مدعوم.",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) غير مدعوم",
|
||||
"passwordRequired": "يُطلَب $t(lockRoomPasswordUppercase)",
|
||||
"permissionErrorTitle": "الإذن مطلوب",
|
||||
"permissionCameraRequiredError": "مطلوب إذن الكاميرا للمشاركة في المؤتمرات بالفيديو. يرجى منحه في الإعدادات",
|
||||
"permissionMicRequiredError": "مطلوب إذن الميكروفون للمشاركة في المؤتمرات مع الصوت. يرجى منحه في الإعدادات",
|
||||
"popupError": "يمنع متصفحك النوافذ المنبثقة من هذا الموقع. فعِّل رجاءً النوافذ المنبثقة في المتصفح من إعدادات الحماية وحاول مرة أخرى.",
|
||||
"popupErrorTitle": "النوافذ المنبثقة محجوبة.",
|
||||
"readMore": "أكثر",
|
||||
"readMore": "more",
|
||||
"recording": "قيد التسجيل",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "ليس بالإمكان ما دمت قيد البث المباشر",
|
||||
"recordingDisabledForGuestTooltip": "لا يمكن للضيوف بدء عملية التسجيل.",
|
||||
"recordingDisabledTooltip": "عملية التسجيل معطلة.",
|
||||
"rejoinNow": "انضم مجدَّدًا الآن",
|
||||
"remoteControlAllowedMessage": "قبل {{user}} طلب إمكانية التحكم البعيد!",
|
||||
@@ -315,27 +280,15 @@
|
||||
"sendPrivateMessageTitle": "أترسل ردًا خاصًا؟",
|
||||
"serviceUnavailable": "الخدمة غير متاحة",
|
||||
"sessTerminated": "أُنهيَت المكالمة",
|
||||
"sessionRestarted": "إعادة الاتصال من الجسر",
|
||||
"Share": "شارك",
|
||||
"shareAudio": "استمر",
|
||||
"shareAudioTitle" : "How to share audio",
|
||||
"shareAudioWarningTitle": "You need to stop screen sharing before sharing audio",
|
||||
"shareAudioWarningH1": "If you want to share just audio:",
|
||||
"shareAudioWarningD1": "you need to stop screen sharing before sharing your audio.",
|
||||
"shareAudioWarningD2": "you need to restart your screen sharing and check the \"share audio\" option.",
|
||||
"shareMediaWarningGenericH2": "If you want to share your screen and audio",
|
||||
"shareVideoLinkError": "زودنا برابط يوتيوب صحيح",
|
||||
"shareVideoTitle": "شارك فيديو",
|
||||
"shareYourScreen": "شارك شاشتك",
|
||||
"shareYourScreenDisabled": "مشاركة الشاشة مُعطَّلة",
|
||||
"shareYourScreenDisabledForGuest": "لا يمكن للضيوف مشاركة شاشتهم",
|
||||
"startLiveStreaming": "ابدأ بثًا حيًا",
|
||||
"startRecording": "ابدأ التسجيل",
|
||||
"startRemoteControlErrorMessage": "حصل خطأٌ أثناء محاولة بدء جلسة تحكم بعيد!",
|
||||
"shareScreenWarningTitle": "تحتاج إلى إيقاف مشاركة الصوت قبل مشاركة شاشتك",
|
||||
"shareScreenWarningH1": "إذا كنت تريد مشاركة شاشتك فقط:",
|
||||
"shareScreenWarningD1": "تحتاج إلى إيقاف مشاركة الصوت قبل مشاركة شاشتك.",
|
||||
"shareScreenWarningD2": "you need to stop audio sharing, start screen sharing and check the \"share audio\" option.",
|
||||
"sharedVideoLinkPlaceholder": "رابط اليوتيوب او رابط الفيديو المباشر",
|
||||
"stopLiveStreaming": "أوقف البث الحي",
|
||||
"stopRecording": "أوقف التسجيل",
|
||||
"stopRecordingWarning": "أمتأكد من رغبتك بإيقاف التسجيل؟",
|
||||
@@ -348,20 +301,12 @@
|
||||
"tokenAuthFailedTitle": "فشلت عملية الاستيثاق",
|
||||
"transcribing": "يذاع",
|
||||
"unlockRoom": "إزل الاجتماع $t(lockRoomPassword)",
|
||||
"user": "مستخدم",
|
||||
"userIdentifier": "معرف المستخدم",
|
||||
"userPassword": "كلمة مرور المستخدم",
|
||||
"videoLink": "رابط الفيديو",
|
||||
"viewUpgradeOptions": "عرض خيارات الترقية",
|
||||
"viewUpgradeOptionsContent": "للحصول على وصول غير محدود إلى الميزات المتميزة مثل التسجيل والنسخ وتدفق RTMP والمزيد ، ستحتاج إلى ترقية خطتك.",
|
||||
"viewUpgradeOptionsTitle": "لقد اكتشفت ميزة مميزة!",
|
||||
"WaitForHostMsg": "لم يبدأ المؤتمر <b>{{room}}</b> بعد. إن كنت المضيف والراعي، فنرجو تأكيد ذلك عبر الاستيثاق أو انتظر وصول المضيف رجاءً. ",
|
||||
"WaitForHostMsgWOk": "لم يبدأ المؤتمر <b>{{room}}</b> بعد. إن كنت المضيف والراعي، فاضغط على «تمام» للاستيثاق أو انتظر وصول المضيف رجاءً.",
|
||||
"WaitingForHost": "ننتظر وصول المضيف ...",
|
||||
"Yes": "نعم",
|
||||
"yourEntireScreen": "شاشتك كاملةً",
|
||||
"remoteUserControls": "ضوابط المستخدم عن بعد لـ {{username}}",
|
||||
"localUserControls": "ضوابط المستخدم المحلي"
|
||||
"yourEntireScreen": "شاشتك كاملةً"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "{{status}} الآن"
|
||||
@@ -375,29 +320,6 @@
|
||||
"embedMeeting": {
|
||||
"title": "ضمِّن هذا الاجتماع"
|
||||
},
|
||||
"virtualBackground": {
|
||||
"apply": "تطبيق",
|
||||
"title": "خلفيات افتراضية",
|
||||
"blur": "ضبابية",
|
||||
"slightBlur": "طمس طفيف",
|
||||
"removeBackground": "إزالة خلفية",
|
||||
"addBackground": "أضف خلفية",
|
||||
"pleaseWait": "يرجى الانتظار...",
|
||||
"none": "بدون",
|
||||
"uploadedImage": "تحميل الصورة {{index}}",
|
||||
"deleteImage": "حذف صورة",
|
||||
"image1" : "Beach",
|
||||
"image2" : "جدار أبيض محايد",
|
||||
"image3" : "غرفة بيضاء فارغة",
|
||||
"image4" : "مصباح أرضي أسود",
|
||||
"image5" : "جبل",
|
||||
"image6" : "غابة",
|
||||
"image7" : "شروق الشمس",
|
||||
"desktopShareError": "تعذر إنشاء مشاركة سطح المكتب",
|
||||
"desktopShare":"مشاركة سطح المكتب",
|
||||
"webAssemblyWarning": "WebAssembly غير مدعوم",
|
||||
"backgroundEffectError": "فشل تطبيق تأثير الخلفية."
|
||||
},
|
||||
"feedback": {
|
||||
"average": "المتوسط",
|
||||
"bad": "سيئة",
|
||||
@@ -405,8 +327,7 @@
|
||||
"good": "جيدة",
|
||||
"rateExperience": "قيِّم تجربتك مع هذا الإجتماع",
|
||||
"veryBad": "سيئة للغاية",
|
||||
"veryGood": "ممتازة",
|
||||
"star": "نجمة"
|
||||
"veryGood": "ممتازة"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "أجب",
|
||||
@@ -423,7 +344,6 @@
|
||||
"country": "البلد",
|
||||
"dialANumber": "إن أردت الإنضمام إلى الإجتماع، اتصل بأحد الأرقام التالية ثم أدخل رمز المرور",
|
||||
"dialInConferenceID": "رمز المرور (PIN):",
|
||||
"copyNumber":"إنسخ الرقم",
|
||||
"dialInNotSupported": "عذرًا، الاتصال غير مدعوم حاليًا.",
|
||||
"dialInNumber": "اتصل:",
|
||||
"dialInSummaryError": "خطأ في تحصيل معلومات الاتصال. جرب مرة أخرى لاحقًا.",
|
||||
@@ -432,11 +352,6 @@
|
||||
"inviteLiveStream": "لمشاهدة البث الحي لهذا الإجتماع، اضط على هذا الرابط: {{url}}",
|
||||
"invitePhone": "للإنضمام من الهاتف، استعمل: {{number}},,{{conferenceID}}#\n",
|
||||
"invitePhoneAlternatives": "أتبحث عن رقم اتصال مختلف؟\nأنظر أرقام الوصول إلى هذا الإجتماع: {{url}}\n\n\nإن كنت أيضًا تتصل عبر غرفة اتصال (room phone)، انضم دون الاتصال بالصوت: {{silentUrl}}",
|
||||
"inviteSipEndpoint": "To join using the SIP address, enter this: {{sipUri}}",
|
||||
"inviteTextiOSPersonal": "{{name}} is inviting you to a meeting.",
|
||||
"inviteTextiOSJoinSilent": "If you are dialing-in through a room phone, use this link to join without connecting to audio: {{silentUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Click the following link to join: {{inviteUrl}}.",
|
||||
"inviteTextiOSPhone": "To join via phone, use this number: {{number}},,{{conferenceID}}#. If you are looking for a different number, this is the full list: {{didUrl}}.",
|
||||
"inviteURLFirstPartGeneral": "دُعيِت للإنضمام إلى اجتماع",
|
||||
"inviteURLFirstPartPersonal": "دعاك {{name}} لاجتماع.\n",
|
||||
"inviteURLSecondPart": "\nانضم للاجتماع:\n{{url}}\n",
|
||||
@@ -447,7 +362,6 @@
|
||||
"noRoom": "لم تُخصَّص غرفة للاتصال إليها",
|
||||
"numbers": "أرقام الاتصال",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"sip": "SIP عنوان",
|
||||
"title": "شارك",
|
||||
"tooltip": "شارك رابط وتفاصيل الاتصال لهذا الاجتماع",
|
||||
"label": "تفاصيل الاجتماع"
|
||||
@@ -466,7 +380,6 @@
|
||||
"support": "الدعم",
|
||||
"supportMsg": "إن تكرَّر حصول هذا، تواصل مع"
|
||||
},
|
||||
"jitsiHome": "{{logo}} Logo, links to Homepage",
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "ركز على الفيديو الخاص بك",
|
||||
"focusRemote": "ركز على فيديو مشارك آخر",
|
||||
@@ -479,10 +392,10 @@
|
||||
"showSpeakerStats": "اظهر حالة مكبر الصوت",
|
||||
"toggleChat": "افتح/أغلق المحادثة",
|
||||
"toggleFilmstrip": "أظهِر/اخفِ إطار الفيديو المُصغَّر",
|
||||
"toggleParticipantsPane": "إظهار أو إخفاء قائمة المشاركين",
|
||||
"toggleScreensharing": "بدِّل بين الكاميرا ومشاركة الشاشة",
|
||||
"toggleShortcuts": "أظهِر/اخفِ اختصارات لوحة المفاتيح",
|
||||
"videoMute": "ابدأ/أوقف كاميرتك"
|
||||
"videoMute": "ابدأ/أوقف كاميرتك",
|
||||
"videoQuality": "اضبط جودة المكالمة"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"limitNotificationDescriptionWeb": "نظرًا للضغط الكبير، سيقيَّد البث إلى {{limit}} د، ولكن إن أردت إجراء عملية بث غير محدودة، جرِّب <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
@@ -551,15 +464,13 @@
|
||||
"lockRoomPasswordUppercase": "كلمة المرور",
|
||||
"me": "أنا",
|
||||
"notify": {
|
||||
"allowAction": "Allow",
|
||||
"allowedUnmute": "You can unmute your microphone, start your camera or share your screen.",
|
||||
"connectedOneMember": "انضم {{name}} للاجتماع",
|
||||
"connectedThreePlusMembers": "انضم {{name}} وعدد {{count}} غيره إلى الاجتماع",
|
||||
"connectedTwoMembers": "انضم {{first}} و {{second}} إلى الاجتماع",
|
||||
"disconnected": "انقطع الاتصال",
|
||||
"focus": "التركيز على المؤتمر",
|
||||
"focusFail": "إنَّ {{component}} غير متاح. ستعاد المحاولة مرة أخرى خلال {{ms}} ثانية.",
|
||||
"hostAskedUnmute": "The moderator would like you to speak",
|
||||
"grantedTo": "مُنِحت صلاحية رئيس الجلسة إلى {{to}}!",
|
||||
"invitedOneMember": "دُعِي {{name}}",
|
||||
"invitedThreePlusMembers": "دُعِي {{name}} وعدد {{count}} آخرين",
|
||||
"invitedTwoMembers": "دُعِي {{first}} و {{second}}",
|
||||
@@ -573,10 +484,6 @@
|
||||
"passwordRemovedRemotely": "أزال أحد المشاركين {{participantDisplayName}}",
|
||||
"passwordSetRemotely": "ضبط أحد المشاركين $t(lockRoomPasswordUppercase)",
|
||||
"raisedHand": "يريد {{name}} التحدث",
|
||||
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudioTitle": "Couldn't share system audio!",
|
||||
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudioTitle": "Couldn't share system audio!",
|
||||
"somebody": "شخص ما",
|
||||
"startSilentTitle": "انضممت دون مخرج للصوت!",
|
||||
"startSilentDescription": "أنضم مجدَّدًا للاجتماع لتفعيل الصوت",
|
||||
@@ -589,76 +496,10 @@
|
||||
"OldElectronAPPTitle": "ثغرة أمنية!",
|
||||
"oldElectronClientDescription1": "يبدو أنَّك تستعمل إصدارًا قديمًا من جيتسي يحوي ثغرة أمنية. تأكد رجاءً من أنَّك حدَّثته إلى ",
|
||||
"oldElectronClientDescription2": "أحدث إصدار",
|
||||
"oldElectronClientDescription3": " الآن!",
|
||||
"moderationInEffectDescription": "Please raise hand if you want to speak.",
|
||||
"moderationInEffectCSDescription": "Please raise hand if you want to share your screen.",
|
||||
"moderationInEffectVideoDescription": "Please raise your hand if you want to start your camera.",
|
||||
"moderationInEffectTitle": "Your microphone is muted by the moderator",
|
||||
"moderationInEffectCSTitle": "Screen sharing is blocked by the moderator",
|
||||
"moderationInEffectVideoTitle": "Your camera is blocked by the moderator",
|
||||
"moderationRequestFromModerator": "The host would like you to unmute",
|
||||
"moderationRequestFromParticipant": "Wants to speak",
|
||||
"moderationStartedTitle": "Moderation started",
|
||||
"moderationStoppedTitle": "Moderation stopped",
|
||||
"moderationToggleDescription": "by {{participantDisplayName}}",
|
||||
"raiseHandAction": "Raise hand",
|
||||
"reactionSounds": "Disable sounds",
|
||||
"groupTitle": "Notifications"
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "Close",
|
||||
"header": "Participants",
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "المشاركون في الاجتماع({{count}})",
|
||||
"waitingLobby": "Waiting in lobby ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "السماح للحاضرين بـ:",
|
||||
"allowVideo": "السماح بالفيديو",
|
||||
"audioModeration": "إعادة صوت أنفسهم",
|
||||
"blockEveryoneMicCamera": "حظر ميكروفون وكاميرا الجميع",
|
||||
"invite": "قم بدعوة شخص ما",
|
||||
"askUnmute": "اطلب إعادة الصوت",
|
||||
"mute": "كتم الصوت",
|
||||
"muteAll": "كتم الكل",
|
||||
"muteEveryoneElse": "كتم صوت الآخرين",
|
||||
"stopEveryonesVideo": "أوقف فيديو الجميع",
|
||||
"stopVideo": "أوقف الفيديو",
|
||||
"unblockEveryoneMicCamera": "قم بإلغاء حظر ميكروفون وكاميرا الجميع",
|
||||
"videoModeration": "ابدأ الفيديو الخاص بهم"
|
||||
}
|
||||
"oldElectronClientDescription3": " الآن!"
|
||||
},
|
||||
"passwordSetRemotely": "ضبطها مشارك آخر",
|
||||
"passwordDigitsOnly": "حتى {{number}} عدد",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "Add option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
"create": "Create a poll",
|
||||
"cancel": "Cancel",
|
||||
"pollOption" : "Poll option {{index}}",
|
||||
"pollQuestion" : "Poll Question",
|
||||
"questionPlaceholder": "Ask a question",
|
||||
"removeOption": "Remove option",
|
||||
"send": "Send"
|
||||
},
|
||||
"answer": {
|
||||
"skip": "Skip",
|
||||
"submit": "Submit"
|
||||
},
|
||||
"results": {
|
||||
"vote": "Vote",
|
||||
"changeVote": "Change vote",
|
||||
"empty": "There are no polls in the meeting yet. Start a poll here!",
|
||||
"hideDetailedResults": "Hide details",
|
||||
"showDetailedResults": "Show details"
|
||||
},
|
||||
"notification": {
|
||||
"title": "A new poll was added to this meeting",
|
||||
"description": "Open polls tab to vote"
|
||||
}
|
||||
},
|
||||
"poweredby": "مُشغَّل بوساطة",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "خطأ في الصوت أو الفيديو:",
|
||||
@@ -699,7 +540,6 @@
|
||||
"errorDialOutFailed": "فشلت عملية الاتصال، للأسف.",
|
||||
"errorDialOutStatus": "خطأ في معرفة حالة الاتصال",
|
||||
"errorMissingName": "أدخل اسمك للدخول للاجتماع",
|
||||
"errorNoPermissions": "You need to enable microphone and camera access",
|
||||
"errorStatusCode": "فشل الاتصال برمز خطأ: {{status}}",
|
||||
"errorValidation": "فشل التحقق من الرقم",
|
||||
"iWantToDialIn": "أريد الاتصال",
|
||||
@@ -713,7 +553,6 @@
|
||||
"premeeting": "ما قبل الاجتماع",
|
||||
"showScreen": "تفعيل واجهة ما قبل الاجتماع",
|
||||
"startWithPhone": "البدء مع جهاز الصوت من الجوال",
|
||||
"keyboardShortcuts" : "تفعيل اختصارات لوحة المفاتيح",
|
||||
"screenSharingError": "خطأ في مشاركة الشاشة:",
|
||||
"videoOnlyError": "خطأ في الفيديو:",
|
||||
"videoTrackError": "لم نتمكن من إنشاء ملف الفيديو",
|
||||
@@ -734,7 +573,6 @@
|
||||
"ringing": "يرن..."
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "avatar",
|
||||
"setDisplayNameLabel": "حدد اسمك المراد عرضه",
|
||||
"setEmailInput": "أدخل بريدك الإلكتروني",
|
||||
"setEmailLabel": "حدد الصورة الرمزية (gravatar) لبريدك الإلكتروني",
|
||||
@@ -749,15 +587,12 @@
|
||||
"beta": "تجريبي",
|
||||
"busy": "نعمل على تحرير موارد التسجيل. حاول مرة أخرى بعد بضعة دقائق.",
|
||||
"busyTitle": "جميع المسجلين مشغولون حاليًا",
|
||||
"copyLink": "Copy Link",
|
||||
"error": "فشل التسجيل. حاول مرة أخرى.",
|
||||
"errorFetchingLink": "Error fetching recording link.",
|
||||
"expandedOff": "أوقٍف التسجيل",
|
||||
"expandedOn": "يُسجَّل الاجتماع الآن",
|
||||
"expandedPending": "بدء التسجيل...",
|
||||
"failedToStart": "فشل بدء التسجيل",
|
||||
"fileSharingdescription": "شارك التسجيل مع المشاركين للاجتماع",
|
||||
"linkGenerated": "We have generated a link to your recording.",
|
||||
"live": "مباشر",
|
||||
"loggedIn": "مُسجَّل باسم {{userName}}",
|
||||
"off": "أوقِف التسجيل",
|
||||
@@ -767,13 +602,11 @@
|
||||
"pending": "التحضير لتسجيل الاجتماع...",
|
||||
"rec": "تسجيل",
|
||||
"serviceDescription": "ستحفظ خدمة التسجيل الفيديو المستجل",
|
||||
"serviceDescriptionCloud": "Cloud recording",
|
||||
"serviceName": "خدمة التسجيل",
|
||||
"signIn": "دخول",
|
||||
"signOut": "خروج",
|
||||
"unavailable": "عجبًا! {{serviceName}} غير متاحة حاليًا. نعمل على حل المشكلة. حاول مرة أخرى لاحقًا.",
|
||||
"unavailableTitle": "التسجيل غير متاح",
|
||||
"uploadToCloud": "Upload to the cloud"
|
||||
"unavailableTitle": "التسجيل غير متاح"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "اسحب للتحديث"
|
||||
@@ -792,13 +625,8 @@
|
||||
"signedIn": "يجري الوصول إلى أحداث الرزنامة حاليًا التي تخص {{email}}. اضغط على زر قطع الاتصال بالأسفل لإيقاف الوصول إلى أحداث المسجلة في الرزنامة.",
|
||||
"title": "الرزنامة"
|
||||
},
|
||||
"desktopShareFramerate": "معدل إطارات مشاركة سطح المكتب",
|
||||
"desktopShareWarning": "تحتاج إلى إعادة تشغيل مشاركة الشاشة حتى تدخل الإعدادات الجديدة حيز التنفيذ. تحتاج إلى إعادة تشغيل مشاركة الشاشة حتى يتم تفعيل الإعدادات الجديدة.",
|
||||
"desktopShareHighFpsWarning": "قد يؤثر معدل الإطارات الأعلى لمشاركة سطح المكتب على النطاق الترددي الخاص بك. تحتاج إلى إعادة تشغيل مشاركة الشاشة حتى تدخل الإعدادات الجديدة حيز التنفيذ.",
|
||||
"devices": "الأجهزة",
|
||||
"followMe": "كل من يتابعني",
|
||||
"framesPerSecond": "إطار في الثانية",
|
||||
"incomingMessage": "رسالة واردة",
|
||||
"language": "اللغة",
|
||||
"loggedIn": "الدخول باسم {{name}}",
|
||||
"microphones": "المجهار (المايكروفون)",
|
||||
@@ -806,19 +634,12 @@
|
||||
"more": "المزيد",
|
||||
"name": "الاسم",
|
||||
"noDevice": "لا يوجد",
|
||||
"participantJoined": "انضم مشارك",
|
||||
"participantLeft": "غادر المشارك",
|
||||
"playSounds": "تشغيل الصوت عند:",
|
||||
"reactions": "ردود فعل الاجتماع",
|
||||
"sameAsSystem": "مثل النظام ({{label}})",
|
||||
"selectAudioOutput": "خرج الصوت",
|
||||
"selectCamera": "الكاميرا",
|
||||
"selectMic": "المجهار (المايكروفون)",
|
||||
"sounds": "اصوات",
|
||||
"speakers": "المذياع (مكبر الصوت)",
|
||||
"startAudioMuted": "بدء الجميع مكتومي الصوت",
|
||||
"startVideoMuted": "بدء الجميع دون فيديو",
|
||||
"talkWhileMuted": "تحدث أثناء كتم الصوت",
|
||||
"title": "الإعدادات"
|
||||
},
|
||||
"settingsView": {
|
||||
@@ -849,7 +670,6 @@
|
||||
},
|
||||
"speaker": "المتحدث",
|
||||
"speakerStats": {
|
||||
"search": "Search",
|
||||
"hours": "{{count}}س",
|
||||
"minutes": "{{count}}د",
|
||||
"name": "الاسم",
|
||||
@@ -859,7 +679,6 @@
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"genericTitle": "The meeting needs to use your microphone and camera.",
|
||||
"title": "يريد {{app}} استعمال المجهار والكاميرا خاصَّتك."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
@@ -871,11 +690,9 @@
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "بدل الصوت فقط",
|
||||
"audioRoute": "اختر جهاز الصوت",
|
||||
"boo": "Boo",
|
||||
"callQuality": "اضبط دقة الفيديو",
|
||||
"cc": "اظهِر/اخفِ الترجمة",
|
||||
"chat": "اظهِر/اخفِ نافذة الدردشة",
|
||||
"clap": "Clap",
|
||||
"document": "اظهِر/اخفِ الملف المشارك",
|
||||
"download": "نزِّل التطبيق",
|
||||
"embedMeeting": "ضمِّن الاجتماع",
|
||||
@@ -886,8 +703,6 @@
|
||||
"help": "مساعدة",
|
||||
"invite": "ادعُ آخرين",
|
||||
"kick": "اطرد مشاركًا",
|
||||
"laugh": "Laugh",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButton": "فعِّل/عطِّل وضع غرفة الانتظار",
|
||||
"localRecording": "اظهِر/اخفِ التحكم بالتسجيل المحلي",
|
||||
"lockRoom": "اظهِر/اخفِ كلمة مرور الاجتماع",
|
||||
@@ -896,52 +711,34 @@
|
||||
"moreOptions": "اظهر المزيد من الخيارت",
|
||||
"mute": "اظهِر/اخفِ كتم الصوت",
|
||||
"muteEveryone": "كتم الجميع",
|
||||
"muteEveryoneElse": "كتم صوت الآخرين",
|
||||
"muteEveryonesVideo": "تعطيل كاميرا الجميع",
|
||||
"muteEveryoneElsesVideo": "تعطيل كاميرا الآخرين",
|
||||
"participants": "مشاركون",
|
||||
"pip": "استعمل/اخرج من وضع صورة-في-صورة",
|
||||
"privateMessage": "أرسل رسالة خاصة",
|
||||
"profile": "عدِّل ملفك الشخصي",
|
||||
"raiseHand": "اظهِر/اخفِ رفع اليد",
|
||||
"reactionsMenu": "فتح / إغلاق قائمة التفاعلات",
|
||||
"recording": "اظهِر/اخفِ التسجيل",
|
||||
"remoteMute": "اكتم مشاركًا",
|
||||
"remoteVideoMute": "تعطيل كاميرا المشارك",
|
||||
"security": "خيارات الحماية",
|
||||
"Settings": "اظهِر/اخفِ الإعدادات",
|
||||
"shareaudio": "مشاركة الصوت",
|
||||
"sharedvideo": "اظهِر/اخفِ مشاركة فيديو يوتيوب",
|
||||
"shareRoom": "ادعُ أحدًا",
|
||||
"shareYourScreen": "بدِّل وضع مشاركة الشاشة",
|
||||
"shortcuts": "اظهِر/اخفِ الاختصارات",
|
||||
"show": "اظهر على المنصة",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "اظهِر/اخفِ حالة المتحدث",
|
||||
"surprised": "Surprised",
|
||||
"tileView": "اظهِر/اخفِ عرض العنوان",
|
||||
"toggleCamera": "بدِّل الكاميرا",
|
||||
"toggleFilmstrip": "بدِّل وضع الشريط السينمائي (filmstrip)",
|
||||
"videomute": "بدِّل وضع اخفاء الفيديو",
|
||||
"videoblur": "استعمل/اخرج من وضع تغبيش خلفية الفيديو",
|
||||
"selectBackground": "اختر الخلفية",
|
||||
"expand": "وسّع",
|
||||
"collapse": "قلّص"
|
||||
"videoblur": "استعمل/اخرج من وضع تغبيش خلفية الفيديو"
|
||||
},
|
||||
"addPeople": "اضف أحدًا إلى المكالمة",
|
||||
"audioSettings": "إعدادات الصوت",
|
||||
"videoSettings": "اعدادات الفيديو",
|
||||
"audioOnlyOff": "عطِّل وضع معدَّل نقل البيانات المنخفص",
|
||||
"audioOnlyOn": "فعِّل وضع نقل البيانات المنخفض",
|
||||
"audioRoute": "اختر جهاز الصوت",
|
||||
"authenticate": "اَجرِ عملية الاستيثاق",
|
||||
"boo": "Boo",
|
||||
"callQuality": "اضبط دقة الفيديو",
|
||||
"chat": "افتح / أغلق الشاشة",
|
||||
"clap": "Clap",
|
||||
"closeChat": "أغلق الدردشة",
|
||||
"closeReactionsMenu": "إغلاق قائمة ردود الفعل",
|
||||
"disableReactionSounds": "يمكنك تعطيل أصوات ردود الفعل لهذا الاجتماع",
|
||||
"documentClose": "أغلق الملف المشارك",
|
||||
"documentOpen": "افتح الملف المشارك",
|
||||
"download": "نزِّل التطبيق",
|
||||
@@ -955,8 +752,6 @@
|
||||
"hangup": "غادر",
|
||||
"help": "مساعدة",
|
||||
"invite": "ادعُ أحدًا",
|
||||
"laugh": "Laugh",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButtonDisable": "عطِّل وضع غرفة الانتظار",
|
||||
"lobbyButtonEnable": "فعِّل وضع غرفة الانتظار",
|
||||
"login": "ادخل",
|
||||
@@ -966,7 +761,6 @@
|
||||
"moreOptions": "المزيد من الخيارات",
|
||||
"mute": "اكتم / ألغ الكتم",
|
||||
"muteEveryone": "اكتم الجميع",
|
||||
"muteEveryonesVideo": "تعطيل كاميرا الجميع",
|
||||
"noAudioSignalTitle": "لا يصلنا شيئًا من المجهار (المايكروفون) الخاص بك",
|
||||
"noAudioSignalDesc": "إن لم تكتمه عمدًا من إعدادات النظام لديك أو من الجهاز نفسه، بدِّله إلى جهاز آخر.",
|
||||
"noAudioSignalDescSuggestion": "إن لم تكتمه عمدًا من إعدادات النظام لديك أو من الجهاز نفسه، بدِّله إلى الجهاز الموصى به.",
|
||||
@@ -975,39 +769,28 @@
|
||||
"noisyAudioInputTitle": "يبدو أنَّ المجهار (المايكروفون) لديك يصدر الكثير من الضجة!",
|
||||
"noisyAudioInputDesc": "يبدو أنَّ المجهار (المايكروفون) لديك يصدر الكثير من الضجة، اكتمه رجاءً أو استعمل مجهارًا آخر.",
|
||||
"openChat": "فتح الدرشة",
|
||||
"openReactionsMenu": "افتح قائمة التفاعلات",
|
||||
"participants": "مشاركون",
|
||||
"pip": "استعمل وضع صورة-في-صورة",
|
||||
"privateMessage": "أرسل رسالة خاصة",
|
||||
"profile": "عدِّل ملفك الشخصي",
|
||||
"raiseHand": "ارفع / اخفض يدك",
|
||||
"raiseYourHand": "ارفع يدك",
|
||||
"reactionBoo": "Send boo reaction",
|
||||
"reactionClap": "Send clap reaction",
|
||||
"reactionLaugh": "Send laugh reaction",
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"security": "خيارات الحماية",
|
||||
"Settings": "الإعدادات",
|
||||
"shareaudio": "مشاركة الصوت",
|
||||
"sharedvideo": "شارك فيديو يوتيوب",
|
||||
"shareRoom": "ادعُ أحدًا",
|
||||
"shortcuts": "اعرض الاختصارات",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "حالة المتحدث",
|
||||
"startScreenSharing": "ابدأ بمشاركة الشاشة",
|
||||
"startSubtitles": "أظهر الترجمة",
|
||||
"stopAudioSharing": "وقف مشاركة الصوت",
|
||||
"stopScreenSharing": "أوقف مشاركة الشاشة",
|
||||
"stopSubtitles": "أخفِ الترجمة",
|
||||
"stopSharedVideo": "أوقف فيديو يوتيوب المشارك",
|
||||
"surprised": "Surprised",
|
||||
"talkWhileMutedPopup": "أتحاول التحدث؟ المجهار مكتوم لديك.",
|
||||
"tileViewToggle": "بدِّل عنوان العرض",
|
||||
"toggleCamera": "بدِّل الكاميرا",
|
||||
"videomute": "استعمل / أوقف الكاميرا",
|
||||
"selectBackground": "اختر الخلفية"
|
||||
"startvideoblur": "غبِّش الخلفية",
|
||||
"stopvideoblur": "توقف عن تغبيش الخلفية"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "أظهر / أخف الترجمة",
|
||||
@@ -1033,7 +816,6 @@
|
||||
"react-nativeGrantPermissions": "اختر <b><i>السماح</i></b> عندما يطلب المتصفح الأذونات.",
|
||||
"safariGrantPermissions": "اختر <b><i>تمام</i></b> عندما يطلب المتصفح الأذونات."
|
||||
},
|
||||
"volumeSlider": "Volume slider",
|
||||
"videoSIPGW": {
|
||||
"busy": "نعمل على تحرير الموارد. حاول مرة أخرى لاحقًا بعد بضعة دقائق.",
|
||||
"busyTitle": "خدمة الغرفة مشغولة حاليًا",
|
||||
@@ -1056,29 +838,26 @@
|
||||
"ld": "دقة منخفضة",
|
||||
"ldTooltip": "عرض فيديو بدقة منخفضة",
|
||||
"lowDefinition": "دقة منخفضة",
|
||||
"onlyAudioAvailable": "الصوت متاح فقط",
|
||||
"onlyAudioSupported": "الصوت مدعوم فقط في هذا المتصفح",
|
||||
"sd": "دقة قياسية",
|
||||
"sdTooltip": "عرض فيديو بدقة قياسية",
|
||||
"standardDefinition": "دقة قياسية"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "معلومات الاتصال",
|
||||
"domute": "كتم",
|
||||
"domuteVideo": "تعطيل الكاميرا",
|
||||
"domuteOthers": "كتم الجميع",
|
||||
"domuteVideoOfOthers": "تعطيل الكاميرا للآخرين",
|
||||
"flip": "قلب",
|
||||
"grantModerator": "امنح صلاحيات رئيس الجلسة",
|
||||
"kick": "طرد خارجًا",
|
||||
"moderator": "رئيس الجلسة",
|
||||
"mute": "المشارك مكتوم الصوت",
|
||||
"muted": "مكتوم",
|
||||
"videoMuted": "الكاميرا معطلة",
|
||||
"remoteControl": "بدء / إيقاف التحكم البعيد",
|
||||
"show": "أظهر على المنصة",
|
||||
"videomute": "أوقف المشارك الكاميرا"
|
||||
},
|
||||
"welcomepage": {
|
||||
"addMeetingName": "Add Meeting name",
|
||||
"accessibilityLabel": {
|
||||
"join": "انقر للمشاركة",
|
||||
"roomname": "أدخل اسم الغرفة"
|
||||
@@ -1095,14 +874,8 @@
|
||||
"getHelp": "أريد مساعدة",
|
||||
"go": "ابدأ",
|
||||
"goSmall": "ابدأ",
|
||||
"headerTitle": "Jitsi Meet",
|
||||
"headerSubtitle": "Secure and high quality meetings",
|
||||
"info": "معلومات",
|
||||
"join": "أنشئ / انضم",
|
||||
"jitsiOnMobile": "Jitsi on mobile – download our apps and start a meeting from anywhere",
|
||||
"mobileDownLoadLinkIos": "Download mobile app for iOS",
|
||||
"mobileDownLoadLinkAndroid": "Download mobile app for Android",
|
||||
"mobileDownLoadLinkFDroid": "Download mobile app for F-Droid",
|
||||
"moderatedMessage": "أو a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">احجز رابط لاجتماع</a> إن كنت رئيس الجلسة الوحيد فقط.",
|
||||
"privacy": "الخصوصية",
|
||||
"recentList": "الجديد",
|
||||
@@ -1113,17 +886,8 @@
|
||||
"roomname": "أدخل اسم الغرفة",
|
||||
"roomnameHint": "أدخل اسم أو رابط الغرفة التي تريد الانضمام إليها. يمكنك إنشاء اسم جديد لترسله إلى من تريد أن تجتمع معهم.",
|
||||
"sendFeedback": "أبدِ رأيك",
|
||||
"startMeeting": "Start meeting",
|
||||
"terms": "الشروط",
|
||||
"title": "منصة عقد مؤتمرات واجتماعات آمنة وكاملة المزايا ومجانية بالمطلق",
|
||||
"logo":{
|
||||
"calendar":"Calendar logo",
|
||||
"microsoftLogo":"Microsoft logo",
|
||||
"logoDeepLinking":"Jitsi meet logo",
|
||||
"desktopPreviewThumbnail":"Desktop preview thumbnail",
|
||||
"googleLogo":"Google Logo",
|
||||
"policyLogo":"Policy logo"
|
||||
}
|
||||
"title": "منصة عقد مؤتمرات واجتماعات آمنة وكاملة المزايا ومجانية بالمطلق"
|
||||
},
|
||||
"lonelyMeetingExperience": {
|
||||
"button": "ادعُ آخرين",
|
||||
@@ -1133,8 +897,6 @@
|
||||
"header": "مركز المساعدة"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Admit",
|
||||
"admitAll": "Admit all",
|
||||
"knockingParticipantList": "تنبيه قائمة المشاركين",
|
||||
"allow": "اسمح",
|
||||
"backToKnockModeButton": "لا يوجد كلمة مرور، اطلب الإذن بالدخول بدلًا من ذلك.",
|
||||
@@ -1147,7 +909,6 @@
|
||||
"enableDialogText": "يحمي وضع الانتظار غرفة الاجتماع عبر منح رئيس الجلسة إمكانية الموافقة على انضمام المشاركين.",
|
||||
"enterPasswordButton": "أدخل كلمة المرور لهذا الاجتماع",
|
||||
"enterPasswordTitle": "أدخل كلمة المرور للدخول للاجتماع",
|
||||
"errorMissingPassword": "Please enter the meeting password",
|
||||
"invalidPassword": "كلمة مرور غير صحيحة",
|
||||
"joiningMessage": "ستتمكن من الانضمام للاجتماع بعد الموافقة على طلبك",
|
||||
"joinWithPasswordMessage": "الرجاء الانتظار أثناء محاولة الدخول دون كلمة مرور...",
|
||||
@@ -1166,7 +927,6 @@
|
||||
"passwordField": "أدخل كلمة الدخول إلى الاجتماع",
|
||||
"passwordJoinButton": "انضم",
|
||||
"reject": "رفض",
|
||||
"rejectAll": "Reject all",
|
||||
"toggleLabel": "فعِّل غرفة الانتظار"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"copyInvite": "Copiar la invitación a la reunión",
|
||||
"copyLink": "Copiar el link de la reunión",
|
||||
"copyStream": "Copiar el link de la transmisión en vivo",
|
||||
"contacts": "",
|
||||
"countryNotSupported": "Aún no contamos con soporte a este destino.",
|
||||
"countryReminder": "¿Llamando fuera de los Estados Unidos? ¡Por favor, asegúrese de empezar con el código de país!",
|
||||
"defaultEmail": "Dirección de correo por defecto",
|
||||
@@ -17,14 +16,18 @@
|
||||
"inviteMoreMailSubject": "Unirse a la reunión {{appName}}",
|
||||
"inviteMorePrompt": "Invitar a más personas",
|
||||
"linkCopied": "Link copiado al portapapeles",
|
||||
"loading": "Buscando por contacto y número telefónico",
|
||||
"loadingNumber": "Validando el número telefónico",
|
||||
"loadingPeople": "Buscando contactos a invitar",
|
||||
"noResults": "No se encontraron coincidencias",
|
||||
"noValidNumbers": "Por favor ingrese un número de teléfono",
|
||||
"outlookEmail": "Correo de Outlook",
|
||||
"phoneNumbers": "",
|
||||
"searching": "",
|
||||
"searchNumbers": "Agregar números telefónicos",
|
||||
"searchPeople": "Buscar personas",
|
||||
"searchPeopleAndNumbers": "Buscar personas o añadir sus números de teléfono",
|
||||
"shareInvite": "Compartir la invitación a la reunión",
|
||||
"shareLink": "Compartir el link de la reunion",
|
||||
"shareStream": "Compartir el link de la transmición en vivo",
|
||||
"sipAddresses": "",
|
||||
"telephone": "Teléfono: {{number}}",
|
||||
"title": "Invitar a otras personas a esta reunión",
|
||||
"yahooEmail": "Correo de Yahoo"
|
||||
@@ -58,7 +61,6 @@
|
||||
"today": "Hoy"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "",
|
||||
"error": "Error: su mensaje no se envío. Motivo: {{error}}",
|
||||
"fieldPlaceHolder": "Escriba su mensaje aquí",
|
||||
"messagebox": "Escriba un mensaje",
|
||||
@@ -70,14 +72,6 @@
|
||||
"titleWithPolls": "Introduce un apodo para usar el chat"
|
||||
},
|
||||
"privateNotice": "Mensaje privado para {{recipient}}",
|
||||
"message": "",
|
||||
"messageAccessibleTitle": "",
|
||||
"messageAccessibleTitleMe": "",
|
||||
"smileysPanel": "",
|
||||
"tabs": {
|
||||
"chat": "",
|
||||
"polls": ""
|
||||
},
|
||||
"title": "Chat",
|
||||
"titleWithPolls": "Chat",
|
||||
"you": "usted"
|
||||
@@ -85,8 +79,7 @@
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": "Instalar la extensión para Google Calendar y la integración con Office 365",
|
||||
"buttonText": "Instalar extensión de Chrome",
|
||||
"dontShowAgain": "No mostrar nuevamente",
|
||||
"close": ""
|
||||
"dontShowAgain": "No mostrar nuevamente"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "Conectándose a su reunión…"
|
||||
@@ -101,6 +94,7 @@
|
||||
"DISCONNECTED": "Desconectado",
|
||||
"DISCONNECTING": "Desconectando",
|
||||
"ERROR": "Error",
|
||||
"RECONNECTING": "Ocurrió un problema en la red. Reconectando...",
|
||||
"FETCH_SESSION_ID": "Obteniendo session-ID…",
|
||||
"GET_SESSION_ID_ERROR": "Obtener session-id error: {{code}}",
|
||||
"GOT_SESSION_ID": "Obteniendo session-ID… Listo",
|
||||
@@ -124,6 +118,7 @@
|
||||
"maxEnabledResolution": "enviar max",
|
||||
"more": "Mostrar más",
|
||||
"packetloss": "Pérdida de paquetes:",
|
||||
"participant_id": "ID participante:",
|
||||
"quality": {
|
||||
"good": "Buena",
|
||||
"inactive": "Inactivo",
|
||||
@@ -135,9 +130,8 @@
|
||||
"remoteaddress_plural": "Direcciones remotas:",
|
||||
"remoteport": "Puerto remoto:",
|
||||
"remoteport_plural": "Puertos remotos:",
|
||||
"resolution": "Resolución:",
|
||||
"savelogs": "Guardar logs",
|
||||
"participant_id": "ID participante:",
|
||||
"resolution": "Resolución:",
|
||||
"status": "Calidad:",
|
||||
"transport": "Transporte:",
|
||||
"transport_plural": "Transportes:",
|
||||
@@ -153,10 +147,11 @@
|
||||
"description": "¿No pasó nada? Hemos intentado iniciar la reunión en la aplicación de escritorio {{app}}. Intenta de nuevo o inicia en la aplicación web {{app}}.",
|
||||
"descriptionWithoutWeb": "¿No pasó nada? Intentamos iniciar su reunión en la aplicación de escritorio {{app}}.",
|
||||
"downloadApp": "Descargar la app",
|
||||
"launchWebButton": "Iniciar en el navegador",
|
||||
"ifDoNotHaveApp": "Si aún no tienes la app:",
|
||||
"ifHaveApp": "Si ya tienes la app:",
|
||||
"joinInApp": "Unirse a la reunion usando la app",
|
||||
"launchWebButton": "Iniciar en el navegador",
|
||||
"openApp": "Continuar a la aplicación",
|
||||
"title": "Iniciando la reunión en {{app}}…",
|
||||
"tryAgainButton": "Intentar de nuevo en el escritorio"
|
||||
},
|
||||
@@ -183,14 +178,12 @@
|
||||
"alreadySharedVideoMsg": "Otro participante ya está compartiendo un vídeo. Esta conferencia sólo permite compartir un vídeo a la vez.",
|
||||
"alreadySharedVideoTitle": "Solo se permite un vídeo compartido a la vez",
|
||||
"applicationWindow": "Ventana de aplicación",
|
||||
"authenticationRequired": "",
|
||||
"Back": "Anterior",
|
||||
"cameraConstraintFailedError": "Su cámara no satisface algunos de los requerimientos.",
|
||||
"cameraNotFoundError": "No se encontró la cámara.",
|
||||
"cameraNotSendingData": "No podemos acceder a la cámara. Asegúrate que no haya otra aplicación usándola, selecciona otro dispositivo del menú de configuración o intenta volver a cargar la aplicación.",
|
||||
"cameraNotSendingDataTitle": "No es posible acceder a la cámara",
|
||||
"cameraPermissionDeniedError": "No has otorgado permiso para usar la cámara. Puedes unirte a la conferencia de todos modos, pero los demás asistentes no te podrán ver. Usa el botón de la cámara en la barra de direcciones para solucionar esto.",
|
||||
"cameraTimeoutError": "",
|
||||
"cameraUnknownError": "No se puede usar la cámara por un motivo desconocido.",
|
||||
"cameraUnsupportedResolutionError": "La cámara no admite la resolución de vídeo requerida.",
|
||||
"Cancel": "Cancelar",
|
||||
@@ -205,27 +198,29 @@
|
||||
"connectError": "¡Oops! Algo salió mal y no fue posible conectarnos a la conferencia.",
|
||||
"connectErrorWithMsg": "¡Oops! Algo salió mal y no fue posible conectarnos a la conferencia: {{msg}}",
|
||||
"connecting": "Conectando",
|
||||
"contactSupport": "Contacta al soporte técnico",
|
||||
"copied": "Copiado",
|
||||
"contactSupport": "Contacta al soporte técnico",
|
||||
"copy": "Copiar",
|
||||
"dismiss": "Descartar",
|
||||
"displayNameRequired": "¡Hola! ¿Cuál es tu nombre?",
|
||||
"done": "Listo",
|
||||
"e2eeDescription": "El cifrado de extremo a extremo es actualmente EXPERIMENTAL. Tenga en cuenta que activarlo puede deshabilitar servicios como: grabación, transmisión en vivo y participación telefónica. Además, esta reunión solo funcionará con personas que se unan con un navegador.",
|
||||
"e2eeLabel": "Habilitar cifrado Extremo-a-Extremo",
|
||||
"e2eeDisabledDueToMaxModeDescription": "",
|
||||
"e2eeDescription": "El cifrado de extremo a extremo es actualmente EXPERIMENTAL. Tenga en cuenta que activarlo puede deshabilitar servicios como: grabación, transmisión en vivo y participación telefónica. Además, esta reunión solo funcionará con personas que se unan con un navegador.",
|
||||
"e2eeWarning": "ADVERTENCIA: No todos los participantes de esta reunión soportan el cifrado de extremo a extremo. Si usted habilita esta opción, ellos no podrán verlo ni oírlo.",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "",
|
||||
"enterDisplayName": "Por favor ingresa tu nombre aquí",
|
||||
"embedMeeting": "",
|
||||
"enterDisplayNameToJoin": "Por favor ingresa tu nombre para unirte",
|
||||
"error": "Error",
|
||||
"externalInstallationMsg": "Necesita instalar nuestra extensión para compartir pantalla.",
|
||||
"externalInstallationTitle": "Extensión requerida",
|
||||
"goToStore": "Ir a la tienda web",
|
||||
"gracefulShutdown": "Nuestro servicio se encuentra en mantenimiento. Por favor, intente más tarde.",
|
||||
"grantModeratorDialog": "¿Estas seguro de que quieres convertir a este participante en moderator?",
|
||||
"grantModeratorTitle": "Convertir en moderador",
|
||||
"hideShareAudioHelper": "",
|
||||
"IamHost": "Soy el anfitrión",
|
||||
"incorrectRoomLockPassword": "Contraseña incorrecta",
|
||||
"incorrectPassword": "Nombre de usuario o contraseña incorrecta",
|
||||
"inlineInstallationMsg": "Necesita instalar nuestra extensión para compartir escritorio.",
|
||||
"inlineInstallExtension": "Instalar ahora",
|
||||
"internalError": "¡Oops! Algo salió mal. El siguiente error ocurrió: {{error}}",
|
||||
"internalErrorTitle": "Error interno",
|
||||
"kickMessage": "Puede ponerse en contacto con {{participantDisplayName}} para obtener más detalles.",
|
||||
@@ -233,14 +228,14 @@
|
||||
"kickParticipantDialog": "¿Seguro que quiere expulsar a este participante?",
|
||||
"kickParticipantTitle": "¿Expulsar a este participante?",
|
||||
"kickTitle": "¡Ay! {{participantDisplayName}} te expulsó de la reunión",
|
||||
"liveStreaming": "Transmisión en vivo",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "No es posible mientras la grabación este activa",
|
||||
"liveStreaming": "Transmisión en vivo",
|
||||
"liveStreamingDisabledForGuestTooltip": "Los invitados no pueden iniciar la transmisión en vivo.",
|
||||
"liveStreamingDisabledTooltip": "Las trasmisiones están deshabilitadas.",
|
||||
"lockMessage": "No se pudo bloquear la conferencia.",
|
||||
"lockRoom": "Agregar $t(lockRoomPasswordUppercase) a la reunión",
|
||||
"lockTitle": "El bloqueo falló",
|
||||
"logoutQuestion": "¿Está seguro que desea salir y detener la conferencia?",
|
||||
"login": "",
|
||||
"logoutTitle": "Cerrar sesión",
|
||||
"maxUsersLimitReached": "El límite máximo de participantes ha sido alcanzado. Por favor contacta al organizador o intenta más tarde.",
|
||||
"maxUsersLimitReachedTitle": "La reunión está llena.",
|
||||
@@ -249,34 +244,18 @@
|
||||
"micNotSendingData": "Vaya a la configuración de su computadora para activar el micrófono y ajustar su nivel",
|
||||
"micNotSendingDataTitle": "Su micrófono está silenciado en la configuración de su sistema",
|
||||
"micPermissionDeniedError": "No ha otorgado permisos para usar su micrófono. Puede unirse a la conferencia, pero no lo podrán escuchar. Utilice el botón en la barra de dirección para solucionar esto.",
|
||||
"micTimeoutError": "",
|
||||
"micUnknownError": "No se puede usar el micrófono por motivos desconocidos.",
|
||||
"moderationAudioLabel": "",
|
||||
"moderationVideoLabel": "",
|
||||
"muteEveryoneElseDialog": "Una vez silenciados, no podrás quitarles el modo silencio, pero ellos podrán hacerlo en cualquier momento.",
|
||||
"muteEveryoneElseTitle": "¿Silenciar a todos excepto a {{whom}}?",
|
||||
"muteEveryoneDialog": "¿Estás seguro que quieres silenciar a todos? No podrás quitarles el silencio, pero ellos pueden quitárselo en cualquier momento.",
|
||||
"muteEveryoneDialogModerationOn": "",
|
||||
"muteEveryoneTitle": "¿Silenciar a todos?",
|
||||
"muteEveryoneElsesVideoDialog": "",
|
||||
"muteEveryoneElsesVideoTitle": "",
|
||||
"muteEveryonesVideoDialog": "",
|
||||
"muteEveryonesVideoDialogModerationOn": "",
|
||||
"muteEveryonesVideoDialogOk": "",
|
||||
"muteEveryonesVideoTitle": "",
|
||||
"muteEveryoneSelf": "ti mismo",
|
||||
"muteEveryoneStartMuted": "Todos inician silenciados desde ahora",
|
||||
"muteParticipantBody": "No podrás quitarles el modo en silencio, pero ellos pueden quitárselo en cualquier momento.",
|
||||
"muteParticipantButton": "Silenciar",
|
||||
"muteParticipantDialog": "¿Seguro que quieres silenciar a este participante? No podrás revertir esta acción, pero el participante podrá hacerlo en cualquier momento",
|
||||
"muteParticipantsVideoDialog": "",
|
||||
"muteParticipantTitle": "¿Silenciar a este participante?",
|
||||
"muteParticipantsVideoButton": "",
|
||||
"muteParticipantsVideoTitle": "",
|
||||
"muteParticipantsVideoBody": "",
|
||||
"noDropboxToken": "",
|
||||
"Ok": "Aceptar",
|
||||
"password": "",
|
||||
"passwordLabel": "$t(lockRoomPasswordUppercase)",
|
||||
"passwordNotSupported": "No se soporta $t(lockRoomPassword) en la reunión",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) no es compatible",
|
||||
@@ -289,6 +268,7 @@
|
||||
"readMore": "mas",
|
||||
"recording": "Grabando",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "No es posible mientras la transmisión en vivo este activa",
|
||||
"recordingDisabledForGuestTooltip": "Los invitados no pueden iniciar grabaciones.",
|
||||
"recordingDisabledTooltip": "Inicio de grabación desactivado.",
|
||||
"rejoinNow": "Reunirse ahora",
|
||||
"remoteControlAllowedMessage": "¡{{user}} ha aceptado tu solicitud de control remoto!",
|
||||
@@ -308,6 +288,10 @@
|
||||
"screenSharingAudio": "Compartir audio",
|
||||
"screenSharingFailed": "¡Oops! ¡Algo salio mal, no se pudo iniciar la compartición de su pantalla!",
|
||||
"screenSharingFailedTitle": "¡Fallo al compartir su pantalla!",
|
||||
"screenSharingFailedToInstall": "¡Uy! La extensión de uso compartido de pantalla no se pudo instalar.",
|
||||
"screenSharingFailedToInstallTitle": "La extensión de uso compartido de pantalla no se pudo instalar",
|
||||
"screenSharingFirefoxPermissionDeniedError": "Algo salió mal al compartir pantalla. Asegúrate de habernos dado permiso para hacerlo.",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "¡Uy! No pudimos iniciar el uso compartido de la pantalla.",
|
||||
"screenSharingPermissionDeniedError": "¡Uy! Algo salió mal con tus permisos de extensión para compartir pantalla. Vuelve a cargar la página e intenta de nuevo.",
|
||||
"sendPrivateMessage": "Acabas de recibir un mensaje privado. ¿Deseas responder en privado o a todos?",
|
||||
"sendPrivateMessageCancel": "Enviar al grupo",
|
||||
@@ -315,27 +299,15 @@
|
||||
"sendPrivateMessageTitle": "¿Enviar en privado?",
|
||||
"serviceUnavailable": "Servicio no disponible",
|
||||
"sessTerminated": "Llamada terminada",
|
||||
"sessionRestarted": "",
|
||||
"Share": "Compartir",
|
||||
"shareAudio": "",
|
||||
"shareAudioTitle": "",
|
||||
"shareAudioWarningTitle": "",
|
||||
"shareAudioWarningH1": "",
|
||||
"shareAudioWarningD1": "",
|
||||
"shareAudioWarningD2": "",
|
||||
"shareMediaWarningGenericH2": "",
|
||||
"shareVideoLinkError": "Proporciona un enlace de YouTube correcto.",
|
||||
"shareVideoTitle": "Compartir un vídeo",
|
||||
"shareYourScreen": "Compartir pantalla",
|
||||
"shareYourScreenDisabled": "Se desactivó la opción para compartir pantalla.",
|
||||
"shareYourScreenDisabledForGuest": "Los invitados no pueden compartir la pantalla.",
|
||||
"startLiveStreaming": "Iniciar transmisión en vivo",
|
||||
"startRecording": "Iniciar grabación",
|
||||
"startRemoteControlErrorMessage": "Se produjo un error al intentar iniciar la sesión de control remoto.",
|
||||
"shareScreenWarningTitle": "",
|
||||
"shareScreenWarningH1": "",
|
||||
"shareScreenWarningD1": "",
|
||||
"shareScreenWarningD2": "",
|
||||
"sharedVideoLinkPlaceholder": "",
|
||||
"stopLiveStreaming": "Detener transmisión en vivo",
|
||||
"stopRecording": "Detener grabación",
|
||||
"stopRecordingWarning": "¿Estás seguro de que deseas detener la grabación?",
|
||||
@@ -348,20 +320,12 @@
|
||||
"tokenAuthFailedTitle": "Error de autenticación",
|
||||
"transcribing": "Transcribiendo",
|
||||
"unlockRoom": "Quitar la $t(lockRoomPassword) de reunión",
|
||||
"user": "",
|
||||
"userIdentifier": "",
|
||||
"userPassword": "contraseña del usuario",
|
||||
"videoLink": "",
|
||||
"viewUpgradeOptions": "",
|
||||
"viewUpgradeOptionsContent": "",
|
||||
"viewUpgradeOptionsTitle": "",
|
||||
"WaitForHostMsg": "La conferencia <b>{{room}}</b> aún no ha comenzado. Si eres el anfitrión, inicia sesión. De lo contrario, espera a que llegue el anfitrión.",
|
||||
"WaitForHostMsgWOk": "La conferencia <b>{{room}}</b> aún no ha comenzado. Si eres el anfitrión, presiona Aceptar para autenticar. De lo contrario, espera a que llegue el anfitrión.",
|
||||
"WaitingForHostTitle": "",
|
||||
"WaitingForHost": "Esperando al anfitrión…",
|
||||
"Yes": "Sí",
|
||||
"yourEntireScreen": "Toda la pantalla",
|
||||
"remoteUserControls": "",
|
||||
"localUserControls": ""
|
||||
"yourEntireScreen": "Toda la pantalla"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "está {{status}}"
|
||||
@@ -373,30 +337,10 @@
|
||||
"labelToolTip": "La comunicación de audio y vídeo en esta reunión está cifrada de extremo a extremo"
|
||||
},
|
||||
"embedMeeting": {
|
||||
"title": "Insertar esta reunión"
|
||||
"title": "Insertar reunión en sitio web"
|
||||
},
|
||||
"virtualBackground": {
|
||||
"apply": "",
|
||||
"title": "",
|
||||
"blur": "",
|
||||
"slightBlur": "",
|
||||
"removeBackground": "",
|
||||
"addBackground": "",
|
||||
"pleaseWait": "",
|
||||
"none": "",
|
||||
"uploadedImage": "",
|
||||
"deleteImage": "",
|
||||
"image1": "",
|
||||
"image2": "",
|
||||
"image3": "",
|
||||
"image4": "",
|
||||
"image5": "",
|
||||
"image6": "",
|
||||
"image7": "",
|
||||
"desktopShareError": "",
|
||||
"desktopShare": "",
|
||||
"webAssemblyWarning": "",
|
||||
"backgroundEffectError": ""
|
||||
"embedMeeting": {
|
||||
"title": "Insertar esta reunión"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Promedio",
|
||||
@@ -405,8 +349,7 @@
|
||||
"good": "Buena",
|
||||
"rateExperience": "¿Cómo estuvo tu experiencia en la reunión?",
|
||||
"veryBad": "Muy mala",
|
||||
"veryGood": "Muy buena",
|
||||
"star": ""
|
||||
"veryGood": "Muy buena"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "Contestar",
|
||||
@@ -423,7 +366,6 @@
|
||||
"country": "País",
|
||||
"dialANumber": "Para unirte a la reunión, marca uno de estos números y, luego introduce el PIN.",
|
||||
"dialInConferenceID": "PIN:",
|
||||
"copyNumber": "",
|
||||
"dialInNotSupported": "Lo sentimos, actualmente no se admite el marcado telefónico a la reunión.",
|
||||
"dialInNumber": "Marcar:",
|
||||
"dialInSummaryError": "Se ha producido un error al capturar la información de marcación. Vuelva a intentarlo más tarde",
|
||||
@@ -432,11 +374,6 @@
|
||||
"inviteLiveStream": "Para ver la transmisión en vivo de esta reunión, haz clic en este enlace: {{url}}",
|
||||
"invitePhone": "También puedes entrar por llamada telefónica: Marca al número {{number}}, y al escuchar la contestadora introduce {{conferenceID}}#\n",
|
||||
"invitePhoneAlternatives": "Si necesitas un número telefónico de otro país, revisa los números disponibles en {{url}}\n\n\nSi además de entrar vía llamada vas a usar otro dispositivo, puedes usar este link para entrar sin audio: {{silentUrl}}",
|
||||
"inviteSipEndpoint": "",
|
||||
"inviteTextiOSPersonal": "",
|
||||
"inviteTextiOSJoinSilent": "",
|
||||
"inviteTextiOSInviteUrl": "",
|
||||
"inviteTextiOSPhone": "",
|
||||
"inviteURLFirstPartGeneral": "Estás invitado a unirte a una reunión.",
|
||||
"inviteURLFirstPartPersonal": "{{name}} te esta invitando a una reunión.\n",
|
||||
"inviteURLSecondPart": "\nLink para unirse a la reunión:\n{{url}}\n",
|
||||
@@ -447,7 +384,6 @@
|
||||
"noRoom": "No se especificó la sala a marcar.",
|
||||
"numbers": "Números para entrar por llamada telefónica:",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"sip": "",
|
||||
"title": "Compartir",
|
||||
"tooltip": "Compartir el enlace y acceso telefónico para esta reunión",
|
||||
"label": "Información de la reunión"
|
||||
@@ -466,7 +402,6 @@
|
||||
"support": "Soporte",
|
||||
"supportMsg": "Si esto sigue ocurriendo, contacta a"
|
||||
},
|
||||
"jitsiHome": "",
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "Ver tu cámara",
|
||||
"focusRemote": "Ver la cámara de otras personas",
|
||||
@@ -479,14 +414,12 @@
|
||||
"showSpeakerStats": "Mostrar estadísticas de los hablantes",
|
||||
"toggleChat": "Abrir o cerrar el chat",
|
||||
"toggleFilmstrip": "Mostrar u ocultar miniaturas de vídeo",
|
||||
"toggleParticipantsPane": "",
|
||||
"toggleScreensharing": "Cambiar entre cámara y pantalla compartida",
|
||||
"toggleShortcuts": "Mostrar u ocultar atajos del teclado",
|
||||
"videoMute": "Prender o apagar la cámara"
|
||||
"videoMute": "Prender o apagar la cámara",
|
||||
"videoQuality": "Ajustar la calidad de la llamada"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"limitNotificationDescriptionWeb": "Debido a la alta demanda su transmisión estará limitada a {{limit}} minutos. Puede obtener transmisiones ilimitadas en <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "Su transmisión estará limitada a {{limit}} minutos. Puede obtener transmisiones ilimitadas en {{app}}.",
|
||||
"busy": "Nuestros servidores andan un poco ocupados. Vuelve a intentarlo en unos minutos.",
|
||||
"busyTitle": "Todos los transmisores están ocupados",
|
||||
"changeSignIn": "Cambiar de cuenta.",
|
||||
@@ -498,6 +431,9 @@
|
||||
"errorLiveStreamNotEnabled": "La transmisión en vivo no está activada en {{email}}. Por favor, activa la transmisión en vivo o inicia sesión en una cuenta con la transmisión en vivo activada.",
|
||||
"expandedOff": "La transmisión en vivo se ha detenido",
|
||||
"expandedOn": "La reunión se está transmitiendo a YouTube.",
|
||||
"googlePrivacyPolicy": "Política de Privacidad de Google",
|
||||
"limitNotificationDescriptionWeb": "Debido a la alta demanda su transmisión estará limitada a {{limit}} minutos. Puede obtener transmisiones ilimitadas en <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "Su transmisión estará limitada a {{limit}} minutos. Puede obtener transmisiones ilimitadas en {{app}}.",
|
||||
"expandedPending": "La transmisión en vivo se está iniciando…",
|
||||
"failedToStart": "La transmisión en vivo no se pudo iniciar",
|
||||
"getStreamKeyManually": "No pudimos encontrar tu clave de transmisión. Por favor, obtenla de la página de YouTube y pégala.",
|
||||
@@ -515,8 +451,7 @@
|
||||
"start": "Iniciar una transmisión en vivo",
|
||||
"streamIdHelp": "¿Qué es esto?",
|
||||
"unavailableTitle": "Transmisión en vivo no disponible",
|
||||
"youtubeTerms": "Términos de servicios de YouTube",
|
||||
"googlePrivacyPolicy": "Política de Privacidad de Google"
|
||||
"youtubeTerms": "Términos de servicios de YouTube"
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
@@ -551,15 +486,13 @@
|
||||
"lockRoomPasswordUppercase": "Contraseña",
|
||||
"me": "yo",
|
||||
"notify": {
|
||||
"allowAction": "",
|
||||
"allowedUnmute": "",
|
||||
"connectedOneMember": "{{name}} se unió a la reunión",
|
||||
"connectedThreePlusMembers": "{{name}} y {{count}} más se unieron a la reunión",
|
||||
"connectedTwoMembers": "{{first}} y {{second}} se unieron a la reunión",
|
||||
"disconnected": "desconectado",
|
||||
"focus": "Enfocar conferencia",
|
||||
"focusFail": "{{component}} no disponible. Vuelve a intentar en {{ms}} segundos",
|
||||
"hostAskedUnmute": "",
|
||||
"grantedTo": "Se otorgaron derechos de moderador a {{to}}.",
|
||||
"invitedOneMember": "{{name}} ha sido invitado",
|
||||
"invitedThreePlusMembers": "{{name}} y {{count}} más han sido invitados",
|
||||
"invitedTwoMembers": "{{first}} y {{second}} han sido invitados",
|
||||
@@ -570,13 +503,9 @@
|
||||
"mutedTitle": "¡Estás silenciado!",
|
||||
"mutedRemotelyTitle": "¡{{Nombre del participante}} te ha silenciado!",
|
||||
"mutedRemotelyDescription": "Siempre puedes reactivar tu micrófono cuando estés listo para hablar. Vuélvelo a apagar cuando termines de hablar para no hacer ruido.",
|
||||
"videoMutedRemotelyTitle": "",
|
||||
"videoMutedRemotelyDescription": "",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) eliminada por otro participante",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) agregada por otro participante",
|
||||
"raisedHand": "{{name}} quisiera hablar.",
|
||||
"screenShareNoAudio": "",
|
||||
"screenShareNoAudioTitle": "",
|
||||
"somebody": "Alguien",
|
||||
"startSilentTitle": "¡Te uniste sin audio!",
|
||||
"startSilentDescription": "Vuelve a ingresar para activar el audio",
|
||||
@@ -589,87 +518,13 @@
|
||||
"OldElectronAPPTitle": "¡Aplicación obsoleta e insegura!",
|
||||
"oldElectronClientDescription1": "Estás usando una versión vieja de la aplicación de Jitsi Meet que tiene problemas de seguridad. ¡Por favor, actualiza a la ",
|
||||
"oldElectronClientDescription2": "versión más reciente",
|
||||
"oldElectronClientDescription3": " YA!",
|
||||
"moderationInEffectDescription": "",
|
||||
"moderationInEffectCSDescription": "",
|
||||
"moderationInEffectVideoDescription": "",
|
||||
"moderationInEffectTitle": "",
|
||||
"moderationInEffectCSTitle": "",
|
||||
"moderationInEffectVideoTitle": "",
|
||||
"moderationRequestFromModerator": "",
|
||||
"moderationRequestFromParticipant": "",
|
||||
"moderationStartedTitle": "",
|
||||
"moderationStoppedTitle": "",
|
||||
"moderationToggleDescription": "",
|
||||
"raiseHandAction": "",
|
||||
"reactionSounds": "",
|
||||
"groupTitle": ""
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "",
|
||||
"header": "",
|
||||
"headings": {
|
||||
"lobby": "",
|
||||
"participantsList": "",
|
||||
"waitingLobby": ""
|
||||
},
|
||||
"actions": {
|
||||
"allow": "",
|
||||
"allowVideo": "",
|
||||
"audioModeration": "",
|
||||
"blockEveryoneMicCamera": "",
|
||||
"invite": "",
|
||||
"askUnmute": "",
|
||||
"mute": "",
|
||||
"muteAll": "",
|
||||
"muteEveryoneElse": "",
|
||||
"stopEveryonesVideo": "",
|
||||
"stopVideo": "",
|
||||
"unblockEveryoneMicCamera": "",
|
||||
"videoModeration": ""
|
||||
}
|
||||
"oldElectronClientDescription3": " YA!"
|
||||
},
|
||||
"passwordSetRemotely": "definida por otro participante",
|
||||
"passwordDigitsOnly": "Hasta {{number]] cifras",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "",
|
||||
"answerPlaceholder": "",
|
||||
"create": "",
|
||||
"cancel": "",
|
||||
"pollOption": "",
|
||||
"pollQuestion": "",
|
||||
"questionPlaceholder": "",
|
||||
"removeOption": "",
|
||||
"send": ""
|
||||
},
|
||||
"answer": {
|
||||
"skip": "",
|
||||
"submit": ""
|
||||
},
|
||||
"results": {
|
||||
"vote": "",
|
||||
"changeVote": "",
|
||||
"empty": "",
|
||||
"hideDetailedResults": "",
|
||||
"showDetailedResults": ""
|
||||
},
|
||||
"notification": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"poweredby": "con tecnología de",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "Error en audio y vídeo:",
|
||||
"audioDeviceProblem": "Hay un problema con su dispositivo de audio",
|
||||
"audioOnlyError": "Error en audio:",
|
||||
"audioTrackError": "No se pudo crear la pista de audio.",
|
||||
"calling": "Llamando",
|
||||
"callMe": "Llámame",
|
||||
"callMeAtNumber": "Llamame a este número:",
|
||||
"configuringDevices": "Configurando dispositivos...",
|
||||
"connectedWithAudioQ": "¿Estás está conectado con audio?",
|
||||
"connection": {
|
||||
"good": "¡Su conexión a internet es buena!",
|
||||
"nonOptimal": "Su conexión a internet no es óptima",
|
||||
@@ -689,6 +544,16 @@
|
||||
"videoLowQuality": "Prevemos que su video tendrá baja calidad en términos de velocidad de fotogramas y resolución.",
|
||||
"videoTearing": "Prevemos que su video se pixelará o tendrá artefactos visuales."
|
||||
},
|
||||
"errorMissingName": "Ingrese su nombre para unirse a la reunión",
|
||||
"premeeting": "Pre-reunión",
|
||||
"showScreen": "Habilitar pantalla pre-reunión",
|
||||
"audioAndVideoError": "Error en audio y vídeo:",
|
||||
"audioOnlyError": "Error en audio:",
|
||||
"audioTrackError": "No se pudo crear la pista de audio.",
|
||||
"callMe": "Llámame",
|
||||
"callMeAtNumber": "Llamame a este número:",
|
||||
"configuringDevices": "Configurando dispositivos...",
|
||||
"connectedWithAudioQ": "¿Estás está conectado con audio?",
|
||||
"copyAndShare": "Copia y comparte el link de la reuinión",
|
||||
"dialInMeeting": "Entrar con llamada telefónica",
|
||||
"dialInPin": "Marca a la reunión e ingresa el código:",
|
||||
@@ -698,8 +563,6 @@
|
||||
"errorDialOutDisconnected": "No se pudo marcar. Desconectado.",
|
||||
"errorDialOutFailed": "No se pudo marcar. La llamada falló.",
|
||||
"errorDialOutStatus": "Hubo algún error, y no se pudo determinar cuál",
|
||||
"errorMissingName": "Ingrese su nombre para unirse a la reunión",
|
||||
"errorNoPermissions": "",
|
||||
"errorStatusCode": "Error al marcar, código de error {{status}}",
|
||||
"errorValidation": "No se pudo validar el número",
|
||||
"iWantToDialIn": "Quiero entrar por teléfono",
|
||||
@@ -710,9 +573,7 @@
|
||||
"linkCopied": "Se copió el link",
|
||||
"lookGood": "Tu micrófono funciona bien.",
|
||||
"or": "o",
|
||||
"premeeting": "Pre-reunión",
|
||||
"showScreen": "Habilitar pantalla pre-reunión",
|
||||
"keyboardShortcuts": "",
|
||||
"calling": "Llamando",
|
||||
"startWithPhone": "Iniciar con audio de llamada telefónica",
|
||||
"screenSharingError": "Error al compartir pantalla:",
|
||||
"videoOnlyError": "Error con el vídeo:",
|
||||
@@ -734,7 +595,6 @@
|
||||
"ringing": "Timbrando…"
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "",
|
||||
"setDisplayNameLabel": "Configura tu nombre para mostrar",
|
||||
"setEmailInput": "Introducir correo electrónico",
|
||||
"setEmailLabel": "Configurar tu correo electrónico de Gravatar",
|
||||
@@ -742,22 +602,19 @@
|
||||
},
|
||||
"raisedHand": "Desea hablar",
|
||||
"recording": {
|
||||
"limitNotificationDescriptionWeb": "Debido a la alta demanda su grabación estará limitada a {{limit}} minutos. Puede obtener grabaciones ilimitadas en <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "Su grabación estará limitada a {{limit}} minutos. Puede obtener grabaciones ilimitadas en <3>{{app}}</3>.",
|
||||
"authDropboxText": "Subir a Dropbox",
|
||||
"availableSpace": "Espacio disponible: {{spaceLeft}} MB (aproximadamente {{duration}} minutos de grabación)",
|
||||
"beta": "BETA",
|
||||
"busy": "Estamos trabajando para liberar recursos de grabación. Vuelve a intentarlo en unos minutos.",
|
||||
"busyTitle": "Todas las grabadoras están actualmente ocupadas",
|
||||
"copyLink": "",
|
||||
"error": "Error de grabación. Vuelve a intentarlo.",
|
||||
"errorFetchingLink": "",
|
||||
"expandedOff": "Grabación detenida",
|
||||
"expandedOn": "La reunión está siendo grabada.",
|
||||
"expandedPending": "La grabación se está iniciando…",
|
||||
"failedToStart": "No se pudo iniciar la grabación",
|
||||
"fileSharingdescription": "Compartir la grabación con los participantes de la reunión",
|
||||
"linkGenerated": "",
|
||||
"limitNotificationDescriptionWeb": "Debido a la alta demanda su grabación estará limitada a {{limit}} minutos. Puede obtener grabaciones ilimitadas en <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "Su grabación estará limitada a {{limit}} minutos. Puede obtener grabaciones ilimitadas en <3>{{app}}</3>.",
|
||||
"live": "EN VIVO",
|
||||
"loggedIn": "Sesión iniciada como {{userName}}",
|
||||
"off": "Grabación detenida",
|
||||
@@ -767,20 +624,18 @@
|
||||
"pending": "Preparando para grabar la reunión…",
|
||||
"rec": "GRA",
|
||||
"serviceDescription": "El servicio de grabación guardará la grabación",
|
||||
"serviceDescriptionCloud": "",
|
||||
"serviceName": "Servicio de grabación",
|
||||
"signIn": "Iniciar sesión",
|
||||
"signOut": "Cerrar sesión",
|
||||
"unavailable": "¡Uy! {{serviceName}} actualmente no está disponible. Estamos trabajando para resolver el problema. Vuelve a intentarlo más tarde.",
|
||||
"unavailableTitle": "Grabación no disponible",
|
||||
"uploadToCloud": ""
|
||||
"unavailableTitle": "Grabación no disponible"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Mueve el dedo para abajo para actualizar."
|
||||
},
|
||||
"security": {
|
||||
"about": "Puedes agregar una contraseña a la reunión. Los participantes necesitarán la contraseña para unirse a la reunión.",
|
||||
"aboutReadOnly": "Los participantes moderadores pueden agregar una $t(lockRoomPassword) a la reunión. Los participantes deberán proporcionar la $t(lockRoomPassword) antes de que se les permita unirse a la reunión.",
|
||||
"about": "Puedes agregar una contraseña a la reunión. Los participantes necesitarán la contraseña para unirse a la reunión.",
|
||||
"insecureRoomNameWarning": "El nombre de la sala es inseguro. Participantes no deseados pueden llegar a unirse a la reunión.",
|
||||
"securityOptions": "Opciones de seguridad"
|
||||
},
|
||||
@@ -792,13 +647,8 @@
|
||||
"signedIn": "Actualmente se accede a eventos del calendario para {{email}}. Haz clic en el botón Desconectar más abajo para detener el acceso a eventos del calendario.",
|
||||
"title": "Calendario"
|
||||
},
|
||||
"desktopShareFramerate": "",
|
||||
"desktopShareWarning": "",
|
||||
"desktopShareHighFpsWarning": "",
|
||||
"devices": "Dispositivos",
|
||||
"followMe": "Todos me siguen",
|
||||
"framesPerSecond": "",
|
||||
"incomingMessage": "",
|
||||
"language": "Idioma",
|
||||
"loggedIn": "Sesión iniciada como {{name}}",
|
||||
"microphones": "Micrófono",
|
||||
@@ -806,19 +656,13 @@
|
||||
"more": "Más",
|
||||
"name": "Nombre",
|
||||
"noDevice": "Ninguno",
|
||||
"participantJoined": "",
|
||||
"participantLeft": "",
|
||||
"playSounds": "",
|
||||
"reactions": "",
|
||||
"sameAsSystem": "",
|
||||
"selectAudioOutput": "Salida de audio",
|
||||
"selectCamera": "Cámara",
|
||||
"selectMic": "Micrófono",
|
||||
"sounds": "",
|
||||
"speakers": "Parlantes",
|
||||
"speakers": "Altavoces",
|
||||
"startAudioMuted": "Todos inician silenciados",
|
||||
"startVideoMuted": "Todos inician con cámara desactivada",
|
||||
"talkWhileMuted": "",
|
||||
"speakers": "Parlantes",
|
||||
"title": "Ajustes"
|
||||
},
|
||||
"settingsView": {
|
||||
@@ -831,9 +675,9 @@
|
||||
"conferenceSection": "Conferencia",
|
||||
"disableCallIntegration": "Desactivar la integración nativa de llamadas",
|
||||
"disableP2P": "Desactivar la comunicación directa (\"Peer-To-Peer\")",
|
||||
"displayName": "Nombre a mostrar",
|
||||
"disableCrashReporting": "Desactivar el reporte de crasheos",
|
||||
"disableCrashReportingWarning": "¿Estás seguro que no deseas reportarnos los crasheos? La opción se activará al reiniciar la app.",
|
||||
"displayName": "Nombre a mostrar",
|
||||
"email": "Correo electrónico",
|
||||
"header": "Configuración",
|
||||
"profileSection": "Perfil",
|
||||
@@ -849,7 +693,6 @@
|
||||
},
|
||||
"speaker": "Participante",
|
||||
"speakerStats": {
|
||||
"search": "",
|
||||
"hours": "{{count}} h",
|
||||
"minutes": "{{count}} min",
|
||||
"name": "Nombre",
|
||||
@@ -859,7 +702,6 @@
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"genericTitle": "",
|
||||
"title": "{{app}} necesita usar el micrófono y la cámara."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
@@ -871,23 +713,22 @@
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "Alternar cámaras de los demás",
|
||||
"audioRoute": "Seleccionar el dispositivo de sonido",
|
||||
"boo": "",
|
||||
"callQuality": "Administrar la calidad de vídeo",
|
||||
"cc": "Alternar subtítulos",
|
||||
"chat": "Alternar ventana de chat",
|
||||
"clap": "",
|
||||
"document": "Alternar documento compartido",
|
||||
"download": "Descargar nuestras aplicaciones",
|
||||
"embedMeeting": "Insertar reunión",
|
||||
"grantModerator": "Convertir en moderador",
|
||||
"download": "Descargar nuestras aplicaciones",
|
||||
"e2ee": "Cifrado de extremo a extremo",
|
||||
"feedback": "Dejar comentarios",
|
||||
"fullScreen": "Alternar pantalla completa",
|
||||
"grantModerator": "Convertir en moderador",
|
||||
"hangup": "Colgar",
|
||||
"help": "Ayuda",
|
||||
"invite": "Invitar personas",
|
||||
"lobbyButtonDisable": "Desactivar sala de espera",
|
||||
"lobbyButtonEnable": "Activar sala de espera",
|
||||
"kick": "Expulsar participante",
|
||||
"laugh": "",
|
||||
"like": "",
|
||||
"lobbyButton": "Activar / desactivar el modo lobby",
|
||||
"localRecording": "Alternar controles de grabación local",
|
||||
"lockRoom": "Alternar contraseña de la reunión",
|
||||
@@ -896,57 +737,39 @@
|
||||
"moreOptions": "Mostrar más opciones",
|
||||
"mute": "Silenciar micrófono",
|
||||
"muteEveryone": "Silenciar a todos",
|
||||
"muteEveryoneElse": "",
|
||||
"muteEveryonesVideo": "",
|
||||
"muteEveryoneElsesVideo": "",
|
||||
"participants": "",
|
||||
"pip": "Alternar modo ventana en miniatura",
|
||||
"privateMessage": "Enviar mensaje privado",
|
||||
"profile": "Editar perfil",
|
||||
"raiseHand": "Levantar o bajar la mano",
|
||||
"reactionsMenu": "",
|
||||
"recording": "Alternar grabación",
|
||||
"remoteMute": "Silenciar participante",
|
||||
"remoteVideoMute": "",
|
||||
"security": "Opciones de seguridad",
|
||||
"Settings": "Alternar configuración",
|
||||
"shareaudio": "",
|
||||
"sharedvideo": "Alternar vídeo compartido de YouTube",
|
||||
"shareRoom": "Invitar a alguien",
|
||||
"shareYourScreen": "Alternar pantalla compartida",
|
||||
"shortcuts": "Alternar accesos directos",
|
||||
"show": "Mostrar en primer",
|
||||
"silence": "",
|
||||
"speakerStats": "Alternar estadísticas del orador",
|
||||
"surprised": "",
|
||||
"tileView": "Alternar vista de mosaico",
|
||||
"toggleCamera": "Alternar cámara",
|
||||
"toggleFilmstrip": "Alternar mosaicos",
|
||||
"videomute": "Alternar vídeo",
|
||||
"videoblur": "Alternar desenfoque de vídeo",
|
||||
"selectBackground": "",
|
||||
"expand": "",
|
||||
"collapse": ""
|
||||
"videoblur": "Alternar desenfoque de vídeo"
|
||||
},
|
||||
"addPeople": "Agregar personas a la llamada",
|
||||
"audioSettings": "",
|
||||
"videoSettings": "",
|
||||
"audioOnlyOff": "Mostrar cámaras de los demás",
|
||||
"audioOnlyOn": "Ocultar cámaras de los demás (para ahorrar datos)",
|
||||
"audioRoute": "Selecciona el dispositivo de sonido",
|
||||
"authenticate": "Autenticar",
|
||||
"boo": "",
|
||||
"callQuality": "Ajustar la calidad de vídeo",
|
||||
"chat": "Abrir o cerrar chat",
|
||||
"clap": "",
|
||||
"closeChat": "Cerrar chat",
|
||||
"closeReactionsMenu": "",
|
||||
"disableReactionSounds": "",
|
||||
"documentClose": "Cerrar documento compartido",
|
||||
"documentOpen": "Abrir documento compartido",
|
||||
"download": "Descarga nuestras aplicaciones",
|
||||
"e2ee": "Cifrado de extremo a extremo",
|
||||
"embedMeeting": "Insertar reunión",
|
||||
"e2ee": "Cifrado de extremo a extremo",
|
||||
"enterFullScreen": "Pantalla completa",
|
||||
"enterTileView": "Ver en cuadrícula",
|
||||
"exitFullScreen": "Salir de pantalla completa",
|
||||
@@ -955,8 +778,6 @@
|
||||
"hangup": "Colgar",
|
||||
"help": "Ayuda",
|
||||
"invite": "Invitar personas",
|
||||
"laugh": "",
|
||||
"like": "",
|
||||
"lobbyButtonDisable": "Desactivar el modo lobby",
|
||||
"lobbyButtonEnable": "Activar el modo lobby",
|
||||
"login": "Inicio de sesión",
|
||||
@@ -966,7 +787,6 @@
|
||||
"moreOptions": "Más opciones",
|
||||
"mute": "Activar o silenciar el micrófono",
|
||||
"muteEveryone": "Silenciar a todos",
|
||||
"muteEveryonesVideo": "",
|
||||
"noAudioSignalTitle": "¡No se registra audio de tu micrófono!",
|
||||
"noAudioSignalDesc": "Checa si no está silenciado en tu configuración del sistema o dispositivo, o cambia de micrófono.",
|
||||
"noAudioSignalDescSuggestion": "Si no lo silenciaste a propósito desde la configuración del sistema o el dispositivo, intenta usar este otro micrófono:",
|
||||
@@ -975,39 +795,28 @@
|
||||
"noisyAudioInputTitle": "Tu micrófono parece estar ruidoso",
|
||||
"noisyAudioInputDesc": "Tu micrófono está haciendo ruido, siléncialo, ajusta su volumen en configuración del sistema, o cambia de micrófono.",
|
||||
"openChat": "Abrir chat",
|
||||
"openReactionsMenu": "",
|
||||
"participants": "",
|
||||
"pip": "Mostrar en ventana pequeña",
|
||||
"privateMessage": "Enviar mensaje privado",
|
||||
"profile": "Editar perfil",
|
||||
"raiseHand": "Levantar o bajar la mano",
|
||||
"raiseYourHand": "Levantar la mano",
|
||||
"reactionBoo": "",
|
||||
"reactionClap": "",
|
||||
"reactionLaugh": "",
|
||||
"reactionLike": "",
|
||||
"reactionSilence": "",
|
||||
"reactionSurprised": "",
|
||||
"security": "Opciones de seguridad",
|
||||
"Settings": "Configuración",
|
||||
"shareaudio": "",
|
||||
"sharedvideo": "Compartir un vídeo de YouTube",
|
||||
"shareRoom": "Invitar a alguien",
|
||||
"shortcuts": "Ver atajos del teclado",
|
||||
"silence": "",
|
||||
"speakerStats": "Estadísticas de los hablantes",
|
||||
"startScreenSharing": "Comenzar a compartir pantalla",
|
||||
"startSubtitles": "Iniciar subtítulos",
|
||||
"stopAudioSharing": "",
|
||||
"stopScreenSharing": "Dejar de compartir pantalla",
|
||||
"stopSubtitles": "Detener subtítulos",
|
||||
"stopSharedVideo": "Detener vídeo de YouTube",
|
||||
"surprised": "",
|
||||
"talkWhileMutedPopup": "¿Intentas hablar? Estás silenciado.",
|
||||
"tileViewToggle": "Activar o desactivar vista en cuadrícula",
|
||||
"toggleCamera": "Activar o desactivar cámara",
|
||||
"videomute": "Iniciar o detener cámara",
|
||||
"selectBackground": ""
|
||||
"startvideoblur": "Desenfocar mi fondo",
|
||||
"stopvideoblur": "Desactivar desenfoque del fondo"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Iniciar o detener subtítulos",
|
||||
@@ -1033,7 +842,6 @@
|
||||
"react-nativeGrantPermissions": "Selecciona <b><i>Permitir</i></b> cuando el navegador solicite permisos.",
|
||||
"safariGrantPermissions": "Selecciona <b><i>Aceptar</i></b> cuando el navegador solicite permisos."
|
||||
},
|
||||
"volumeSlider": "",
|
||||
"videoSIPGW": {
|
||||
"busy": "Estamos trabajando para liberar recursos. Vuelve a intentarlo en unos minutos.",
|
||||
"busyTitle": "El servicio de salas está actualmente ocupado",
|
||||
@@ -1056,29 +864,26 @@
|
||||
"ld": "LD",
|
||||
"ldTooltip": "Viendo vídeo en baja definición",
|
||||
"lowDefinition": "Baja definición",
|
||||
"onlyAudioAvailable": "Solo hay audio disponible",
|
||||
"onlyAudioSupported": "Solo admitimos audio en este navegador.",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Viendo vídeo en definición estándar",
|
||||
"standardDefinition": "Definición estándar"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "",
|
||||
"domute": "Silenciar",
|
||||
"domuteVideo": "",
|
||||
"domuteOthers": "Silenciar a todos",
|
||||
"domuteVideoOfOthers": "",
|
||||
"flip": "Voltear",
|
||||
"grantModerator": "Convertir en moderador",
|
||||
"kick": "Expulsar",
|
||||
"moderator": "Moderador",
|
||||
"mute": "Se silenció el participante",
|
||||
"muted": "Silenciado",
|
||||
"videoMuted": "",
|
||||
"remoteControl": "Control remoto",
|
||||
"show": "Mostrar en primer plano",
|
||||
"videomute": "El participante paró su cámara"
|
||||
},
|
||||
"welcomepage": {
|
||||
"addMeetingName": "",
|
||||
"accessibilityLabel": {
|
||||
"join": "Toca para unirte",
|
||||
"roomname": "Introduce el nombre de la sala"
|
||||
@@ -1093,37 +898,22 @@
|
||||
"connectCalendarText": "Conecta tu calendario para ver todas tus reuniones de {{app}}. Además, agrega reuniones a tu calendario e inícialas con un solo clic.",
|
||||
"enterRoomTitle": "Comenzar una reunión",
|
||||
"getHelp": "Obtener ayuda",
|
||||
"roomNameAllowedChars": "El nombre de la reunión no debe contener ninguno de estos caracteres: ?, &, :, ', \", %, #.",
|
||||
"go": "IR",
|
||||
"goSmall": "IR",
|
||||
"headerTitle": "",
|
||||
"headerSubtitle": "",
|
||||
"info": "Información",
|
||||
"join": "CREAR / UNIRSE",
|
||||
"jitsiOnMobile": "",
|
||||
"mobileDownLoadLinkIos": "",
|
||||
"mobileDownLoadLinkAndroid": "",
|
||||
"mobileDownLoadLinkFDroid": "",
|
||||
"info": "Información",
|
||||
"moderatedMessage": "O <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reserve con antelación una URL de reunión</a> en la que usted sea el único moderador.",
|
||||
"privacy": "Privacidad",
|
||||
"recentList": "Reciente",
|
||||
"recentListDelete": "Eliminar",
|
||||
"recentListEmpty": "Tu historial de reuniones está vacío. Reúnete y aparecerán aquí.",
|
||||
"reducedUIText": "¡Bienvenid@ a {{app}}!",
|
||||
"roomNameAllowedChars": "El nombre de la reunión no debe contener ninguno de estos caracteres: ?, &, :, ', \", %, #.",
|
||||
"roomname": "Introduce el nombre de la sala",
|
||||
"roomnameHint": "Introduce el nombre o URL de la sala a la que deseas unirte. Puedes inventar un nombre, simplemente infórmaselo a las personas con las que te reunirás para que introduzcan el mismo nombre.",
|
||||
"sendFeedback": "Enviar sugerencias",
|
||||
"startMeeting": "",
|
||||
"terms": "Términos",
|
||||
"title": "Videoconferencias seguras, con gran variedad de funcionalidades y completamente gratuitas",
|
||||
"logo": {
|
||||
"calendar": "",
|
||||
"microsoftLogo": "",
|
||||
"logoDeepLinking": "",
|
||||
"desktopPreviewThumbnail": "",
|
||||
"googleLogo": "",
|
||||
"policyLogo": ""
|
||||
}
|
||||
"title": "Videoconferencias seguras, con gran variedad de funcionalidades y completamente gratuitas"
|
||||
},
|
||||
"lonelyMeetingExperience": {
|
||||
"button": "Invita a otros",
|
||||
@@ -1133,8 +923,6 @@
|
||||
"header": "Centro de ayuda"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "",
|
||||
"admitAll": "",
|
||||
"knockingParticipantList": "Participantes que quieren entrar",
|
||||
"allow": "permitir",
|
||||
"backToKnockModeButton": "No hay contraseña, pide permiso para entrar.",
|
||||
@@ -1147,7 +935,6 @@
|
||||
"enableDialogText": "La sala de espera te deja proteger tu reunión, de modo que todas los que entren enviarán una solicitud que deberá ser aprobada por un moderador.",
|
||||
"enterPasswordButton": "Introduce la contraseña de la reunión",
|
||||
"enterPasswordTitle": "Introduce la contraseña para poder entrar",
|
||||
"errorMissingPassword": "",
|
||||
"invalidPassword": "Contraseña inválida",
|
||||
"joiningMessage": "Podrás entrar tan pronto te acepten tu solicitud.",
|
||||
"joinWithPasswordMessage": "Tratando de entrar con contraseña, por favor espera...",
|
||||
@@ -1166,7 +953,6 @@
|
||||
"passwordField": "Introduce la contraseña de la reunión",
|
||||
"passwordJoinButton": "Entrar",
|
||||
"reject": "Rechazar",
|
||||
"rejectAll": "",
|
||||
"toggleLabel": "Activar sala de espera"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,8 +248,6 @@
|
||||
"micPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre microphone. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous entendre. Utilisez le bouton du microphone dans la barre d'adresse pour résoudre ce problème.",
|
||||
"micTimeoutError": "Impossible de démarrer la source audio. Délai dépassé!",
|
||||
"micUnknownError": "Vous ne pouvez pas utiliser le microphone pour une raison inconnue.",
|
||||
"moderationAudioLabel": "Autoriser les participants à réactiver leur micro",
|
||||
"moderationVideoLabel": "Autoriser les participants à démarrer leur vidéo",
|
||||
"muteEveryoneElseDialog": "Une fois leur micro coupé, vous ne pourrez plus le réactiver, mais ils pourront l'activer par eux-mêmes à tout moment.",
|
||||
"muteEveryoneElseTitle": "Couper le micro de tout le monde sauf de {{whom}} ?",
|
||||
"muteEveryoneDialog": "Êtes-vous sûr de vouloir couper les micros de tout le monde ? Vous ne pourrez plus réactiver leur micro, mais ils pourront l'activer par eux-mêmes à tout moment.",
|
||||
@@ -565,21 +563,12 @@
|
||||
"close": "Fermer",
|
||||
"headings": {
|
||||
"lobby": "Salle d'attente ({{count}})",
|
||||
"participantsList": "Participants de la réunion ({{count}})",
|
||||
"waitingLobby": "Dans la salle d'attente ({{count}})"
|
||||
"participantsList": "Participants de la réunion ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "Autoriser les participant à:",
|
||||
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
|
||||
"invite": "Inviter quelqu'un",
|
||||
"askUnmute": "Demander de réactiver le micro",
|
||||
"mute": "Couper le micro",
|
||||
"muteAll": "Couper le micro de tout le monde",
|
||||
"muteEveryoneElse": "Couper le micro de tous les autres",
|
||||
"startModeration": "Réactiver son micro ou démarrer sa vidéo",
|
||||
"stopEveryonesVideo": "Couper toutes les caméras",
|
||||
"stopVideo": "Couper la vidéo",
|
||||
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras"
|
||||
"muteAll": "couper le micro de tout le monde",
|
||||
"stopVideo": "couper la vidéo"
|
||||
}
|
||||
},
|
||||
"passwordSetRemotely": "défini par un autre participant",
|
||||
|
||||
@@ -17,17 +17,21 @@
|
||||
"inviteMoreMailSubject": "Rejónher la conferéncia {{appName}}",
|
||||
"inviteMorePrompt": "Convidar mai de monde",
|
||||
"linkCopied": "Ligam copiat al quichapapièrs",
|
||||
"loading": "Recèrca de monde e de numèro de telefòn",
|
||||
"loadingNumber": "Validacion del numèro de telefòn",
|
||||
"loadingPeople": "Recèrca de monde de convidar",
|
||||
"noResults": "Pas cap de resultat trobat",
|
||||
"noValidNumbers": "Picatz lo numèro de telefòn",
|
||||
"outlookEmail": "Outlook Email",
|
||||
"searchNumbers": "Apondre de numèros de telefòn",
|
||||
"searchPeople": "Cercar de monde",
|
||||
"searchPeopleAndNumbers": "Cercar de monde o apondre lor numèros de telefòn",
|
||||
"shareInvite": "Partejar invitacion conferéncia",
|
||||
"shareLink": "Partejar lo ligam de la conferéncia per convidar de monde",
|
||||
"shareStream": "Partejar la ligam de la difusion en dirècte",
|
||||
"telephone": "Telefòn : {{number}}",
|
||||
"title": "Convidatz de monde a vòstra conferéncia",
|
||||
"yahooEmail": "Yahoo Email",
|
||||
"phoneNumbers": "numèros de telefòn",
|
||||
"searching": "Recèrca...",
|
||||
"sipAddresses": "adreças sip"
|
||||
"yahooEmail": "Yahoo Email"
|
||||
},
|
||||
"audioDevices": {
|
||||
"bluetooth": "Bluetooth",
|
||||
@@ -75,18 +79,12 @@
|
||||
"smileysPanel": "Panèl d’Emoji",
|
||||
"title": "Messatjariá",
|
||||
"titleWithPolls": "Messatjariá",
|
||||
"you": "vos",
|
||||
"enter": "Dintrar dins la sala",
|
||||
"tabs": {
|
||||
"chat": "Messatjariá",
|
||||
"polls": "Sondatges"
|
||||
}
|
||||
"you": "vos"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"buttonText": "Installar l’extension Chrome",
|
||||
"dontShowAgain": "Me mostrar pas mai aquò",
|
||||
"installExtensionText": "Installar l’extension per l’integracion de Google Calendar e Office 365",
|
||||
"close": "Tampar"
|
||||
"installExtensionText": "Installar l’extension per l’integracion de Google Calendar e Office 365"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "Connexion a vòstra reünion…"
|
||||
@@ -104,7 +102,8 @@
|
||||
"FETCH_SESSION_ID": "Obtencion de session-id...",
|
||||
"GET_SESSION_ID_ERROR": "Obténer l’error session-id : {{code}}",
|
||||
"GOT_SESSION_ID": "Obtencion de session-id... Fach",
|
||||
"LOW_BANDWIDTH": "La vidèo per {{displayName}} es estada copada per estalviar la banda passanta"
|
||||
"LOW_BANDWIDTH": "La vidèo per {{displayName}} es estada copada per estalviar la banda passanta",
|
||||
"RECONNECTING": "Un problèma ret s'es produita. Reconnexion en cors..."
|
||||
},
|
||||
"connectionindicator": {
|
||||
"address": "Adreça :",
|
||||
@@ -157,6 +156,7 @@
|
||||
"ifHaveApp": "S’avètz ja l’aplicacion :",
|
||||
"joinInApp": "Rejónher la conferéncia en utilizant l’aplicacion",
|
||||
"launchWebButton": "Lançar del navigador",
|
||||
"openApp": "Telecargar l’aplicacion",
|
||||
"title": "Aviada de vòstra conferéncia dins {{app}}…",
|
||||
"tryAgainButton": "Tornar ensajar del burèu"
|
||||
},
|
||||
@@ -214,12 +214,17 @@
|
||||
"e2eeWarning": "AVERTIMENT : pas totes los participants d'aquesta conferéncia semblan poder suportar lo chiframent del cap a la fin. Se l'activatz poiràn pas vos veire nimai vos entendre.",
|
||||
"enterDisplayName": "Volgatz picar vòstre nom aquí",
|
||||
"error": "Error",
|
||||
"externalInstallationMsg": "Avètz d'installar nòstra extension de partiment d'ecran.",
|
||||
"externalInstallationTitle": "Extension requesida",
|
||||
"goToStore": "Anar al webstore",
|
||||
"gracefulShutdown": "Lo servici es actualament en mantenença. Ensajatz tornamai pus tard.",
|
||||
"grantModeratorDialog": "Volètz vertadièrament far venir aqueste participant moderator ?",
|
||||
"grantModeratorTitle": "Passar moderator",
|
||||
"IamHost": "Soi l’òste",
|
||||
"incorrectPassword": "Nom de compte o senhal incorrècte",
|
||||
"incorrectRoomLockPassword": "Senhal incorrècte",
|
||||
"inlineInstallationMsg": "Avètz d'installar nòstra extension de partiment d'ecran.",
|
||||
"inlineInstallExtension": "Installar ara",
|
||||
"internalError": "Òu ! Quicòm a pas foncionat. L'error seguenta s'es producha : {{error}}",
|
||||
"internalErrorTitle": "Error intèrna",
|
||||
"kickMessage": "Podètz contactat {{participantDisplayName}} per mai de detalhs.",
|
||||
@@ -229,6 +234,7 @@
|
||||
"kickTitle": "Ai ! {{participantDisplayName}} vos a forabandit de la conferéncia",
|
||||
"liveStreaming": "La difusion en dirècte es estada arrestada",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Impossible pendent un enregistrament actiu",
|
||||
"liveStreamingDisabledForGuestTooltip": "Los convidats pòdon pas aviar una difusion en dirècte.",
|
||||
"liveStreamingDisabledTooltip": "Difusion en dirècte desactivada.",
|
||||
"lockMessage": "Impossible de verrolhar la conferéncia.",
|
||||
"lockRoom": "Ajustar un $t(lockRoomPasswordUppercase) a la conferéncia",
|
||||
@@ -258,11 +264,12 @@
|
||||
"passwordNotSupported": "Ajustar un $t(lockRoomPassword) a una conferéncia es pas suportat.",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) pas suportat",
|
||||
"passwordRequired": "$t(lockRoomPasswordUppercase) requesit",
|
||||
"popupError": "Vòstre navigador bloca las fenèstras que sorgisson a partir d'aqueste site. Mercés d'activar aquelas fenèstras dins los paramètres de vòstre navigador e de tornar ensajar.",
|
||||
"popupError": "Vòstre navigator bloca las fenèstras que sorgisson a partir d'aqueste site. Mercés d'activar aquelas fenèstras dins los paramètres de vòstre navigator e de tornar ensajar.",
|
||||
"popupErrorTitle": "Fenèstra que sorgís blocada",
|
||||
"readMore": "mai",
|
||||
"recording": "Enregistrament",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Impossible pendent una difusion activa",
|
||||
"recordingDisabledForGuestTooltip": "Los convits pòdon pas lançar d’enregistraments.",
|
||||
"recordingDisabledTooltip": "L’enregistrament es desactivat.",
|
||||
"rejoinNow": "Participar ara",
|
||||
"remoteControlAllowedMessage": "{{user}} a acceptat vòstra demanda de contraròtle alonhat !",
|
||||
@@ -282,6 +289,10 @@
|
||||
"screenSharingAudio": "Partejar l’àudio",
|
||||
"screenSharingFailed": "Ops ! Quicòm a trucat, avèm pas pogut començar lo partiment d'ecran!",
|
||||
"screenSharingFailedTitle": "Fracàs del partiment d'ecran !",
|
||||
"screenSharingFailedToInstall": "Òu ! Fracàs de l'installacion de partatge d'ecran.",
|
||||
"screenSharingFailedToInstallTitle": "Fracàs de l'installacion de partatge d'ecran",
|
||||
"screenSharingFirefoxPermissionDeniedError": "Quicòm a fach mèuca quand èrem a ensajar de partejar vòstre ecran. Mercés de verificar qu’avètz donat l’autorizacion de lo partejar.",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "Ops ! Avèm pas pogut aviar lo partatge d’ecran.",
|
||||
"screenSharingPermissionDeniedError": "Òps ! Quicòm s'es pas ben passat amb l'autorizacion de vòstra autorizacion de partatge d'ecran. Mercés de recargar e tornar ensajar.",
|
||||
"sendPrivateMessage": "Avètz recentament recebut un messatge privat. Avètz ensajat d’i respondre en privat, o volètz enviar lo messatge al grop ?",
|
||||
"sendPrivateMessageCancel": "Enviar al grop",
|
||||
@@ -294,6 +305,7 @@
|
||||
"shareVideoTitle": "Partejar una vidèo",
|
||||
"shareYourScreen": "Partejar vòstre ecran",
|
||||
"shareYourScreenDisabled": "Lo partiment d’ecran es desactivat.",
|
||||
"shareYourScreenDisabledForGuest": "Los convits pòdon pas partejar l’ecran.",
|
||||
"startLiveStreaming": "Aviar una difusion en dirècte",
|
||||
"startRecording": "Arrestar l'enregistrament",
|
||||
"startRemoteControlErrorMessage": "Una error s'es produsida en ensajar de començar la session de contraròtle a distància !",
|
||||
@@ -313,55 +325,9 @@
|
||||
"userPassword": "senhal utilizaire",
|
||||
"WaitForHostMsg": "La conferéncia <b>{{room}}</b> a pas encara començat. Se sètz l’òst volgatz ben vos identificar. Autrament esperatz qu’arribe l’òste.",
|
||||
"WaitForHostMsgWOk": "La conferéncia <b>{{room}}</b> a pas encara començat. Se sètz l’òst volgatz ben clicar Ok per vos identificar. Autrament esperatz qu’arribe l’òste.",
|
||||
"WaitingForHost": "En espèra de l’òste...",
|
||||
"Yes": "Òc",
|
||||
"yourEntireScreen": "Vòstre ecran complet",
|
||||
"authenticationRequired": "Autentificacion requerida",
|
||||
"login": "Connexion",
|
||||
"muteEveryoneElsesVideoDialog": "Un còp la camèra desactivada, poiretz pas la reactivar, mas pòdon la reactivar quand vòlgan.",
|
||||
"WaitingForHostTitle": "En espèra de l’òste...",
|
||||
"embedMeeting": "Integrar la conferéncia",
|
||||
"hideShareAudioHelper": "Afichar pas mai aquesta fenèstra",
|
||||
"micTimeoutError": "Aviada impossibla de la font àudio. Relambi despassat !",
|
||||
"moderationAudioLabel": "Permetre als convidats de se copar lo son",
|
||||
"moderationVideoLabel": "Permetre als convidats d’aviar lor vidèo",
|
||||
"muteEveryoneDialogModerationOn": "Los participants pòdon enviar una requèsta per parlar quand vòlgan.",
|
||||
"muteEveryoneElsesVideoTitle": "Arrestar la vidèo de tot lo monde levat {{whom}} ?",
|
||||
"muteEveryonesVideoTitle": "Arrestar la vidèo de tot lo monde ?",
|
||||
"muteParticipantsVideoButton": "Arrestar la camèra",
|
||||
"muteParticipantsVideoTitle": "Desactivar la camèra d’aqueste participant ?",
|
||||
"noDropboxToken": "Cap de geton Dropbox pas valid",
|
||||
"password": "Senhal",
|
||||
"sessionRestarted": "Sonada reaviada pel pont",
|
||||
"shareAudio": "Contunhar",
|
||||
"shareAudioTitle": "Cossí partejar l’àudio",
|
||||
"shareAudioWarningTitle": "Devètz arrestar lo partiment d’ecran abans lo partiment d’àudio",
|
||||
"shareAudioWarningH1": "Se volètz partejar pas que l’àudio :",
|
||||
"shareAudioWarningD1": "devètz arrestar lo partiment d’ecran abans lo partiment d’àudio.",
|
||||
"shareMediaWarningGenericH2": "Se volètz partejar vòstre ecran e l’àudio",
|
||||
"shareScreenWarningTitle": "Devètz arrestar lo partiment àudio abans lo partiment de l’ecran",
|
||||
"shareScreenWarningH1": "Se volètz partejar pas que l’ecran :",
|
||||
"shareScreenWarningD1": "devètz arrestar lo partiment àudio abans lo partiment de l’ecran.",
|
||||
"sharedVideoLinkPlaceholder": "Ligam YouTube o ligam vidèo dirèct",
|
||||
"userIdentifier": "Identificador utilizaire",
|
||||
"cameraTimeoutError": "Aviada impossibla de la font vidèo. Relambi despassat !",
|
||||
"muteEveryonesVideoDialog": "Los participants pòdon activar lor vidèo quand vòlgan.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Los participants pòdon enviar una requèsta per activar lor vidèo quand vòlgan.",
|
||||
"muteEveryonesVideoDialogOk": "Desactivar",
|
||||
"permissionErrorTitle": "Autorizacion requerida",
|
||||
"videoLink": "ligam vidèo",
|
||||
"viewUpgradeOptions": "Veire las opcions de mesa a nivèl",
|
||||
"viewUpgradeOptionsTitle": "Avètz descobèrt una foncionalitat premium !",
|
||||
"e2eeDisabledDueToMaxModeDescription": "Impossible d'activar lo chiframent del cap a la fin a causa d'un nombre tròp bèl de participants dins la conferéncia.",
|
||||
"remoteUserControls": "Contraròtle a distància de l'utilizaire {{username}}",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "AVERTIMENT : lo chiframent del cap a la fin serà automaticament desactivat se mai de participants rejonhent la conferéncia.",
|
||||
"permissionCameraRequiredError": "L’autorizacion de la camèra es requerida per participar a la conferéncia amb la vidèo. Mercés de la donar als Paramètres",
|
||||
"permissionMicRequiredError": "L’autorizacion del microfòn es requerida per participar a la conferéncia amb l’àudio. Mercés de la donar als Paramètres",
|
||||
"viewUpgradeOptionsContent": "Per obténer un accès sens limit a las foncionalitats premium coma l’enregistrament, la transcripcion, la difusion RTMP e encara mai, devètz passar al nivèl superior.",
|
||||
"muteParticipantsVideoDialog": "Volètz vertadièrament copar la camèra d’aqueste participant ? Poiretz pas li activar de nòu, mas poiriá se la restablir quand vòlga.",
|
||||
"muteParticipantsVideoBody": "Poiretz pas lor activar la camèra de nòu, mas poirián se la restablir quand vòlga.",
|
||||
"shareAudioWarningD2": "devètz reaviar lo partiment d’ecran e clicar l’opcion « partejar l’àudio ».",
|
||||
"shareScreenWarningD2": "devètz arrestar lo partiment àudio, aviar lo partiment d’ecran e clicar l’opcion « partejar l’àudio ».",
|
||||
"localUserControls": "Contraròtles de l’utilizaire local"
|
||||
"yourEntireScreen": "Vòstre ecran complet"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "ara es {{status}}"
|
||||
@@ -382,8 +348,7 @@
|
||||
"good": "Bona",
|
||||
"rateExperience": "Mercés de donar una nòta a vòstra experiéncia",
|
||||
"veryBad": "Fòrça marrida",
|
||||
"veryGood": "Fòrça bona",
|
||||
"star": "Estela"
|
||||
"veryGood": "Fòrça bona"
|
||||
},
|
||||
"helpView": {
|
||||
"header": "Centre d’ajuda"
|
||||
@@ -423,14 +388,7 @@
|
||||
"numbers": "Sonar de numèros",
|
||||
"password": "$t(lockRoomPasswordUppercase) :",
|
||||
"title": "Partejar",
|
||||
"tooltip": "Partejar lo ligam e las informacions d’aquesta conferéncia",
|
||||
"copyNumber": "Copiar lo numèro",
|
||||
"inviteTextiOSPersonal": "{{name}} vos convida a la conferéncia.",
|
||||
"sip": "adreça SIP",
|
||||
"inviteSipEndpoint": "Per rejónher en utilizant l’adreça SIP, picatz aquò : {{sipUri}}",
|
||||
"inviteTextiOSPhone": "Per participar per telefòn, utilizatz aqueste numèro : {{number}},,{{conferenceID}}#. Se cercatz un numèro diferent, vaquí la lista complèta : {{didUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Clicatz lo ligam seguent per rejónher : {{inviteUrl}}.",
|
||||
"inviteTextiOSJoinSilent": "Se sonatz via un aparelh de sala de reünion, utilizatz aqueste ligam per rejónher sens connectar l’àudio : {{silentUrl}}."
|
||||
"tooltip": "Partejar lo ligam e las informacions d’aquesta conferéncia"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Avèm un pauc patit a manténer la connexion.",
|
||||
@@ -461,7 +419,7 @@
|
||||
"toggleScreensharing": "Caplevar entre camèra e partatge d'ecran",
|
||||
"toggleShortcuts": "Mostrar o amagar los acorchis clavièr",
|
||||
"videoMute": "Aviar o arrestar vòstra camèra",
|
||||
"toggleParticipantsPane": "Afichar o amagar lo panèl dels participants"
|
||||
"videoQuality": "Gerir la qualitat de las sonadas"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "Sèm a ensajar de liurar de ressorças flux. Mercés de tornar ensajar dins una estona.",
|
||||
@@ -526,13 +484,7 @@
|
||||
"passwordField": "Picatz lo senhal de la conferéncia",
|
||||
"passwordJoinButton": "Rejónher",
|
||||
"reject": "Regetar",
|
||||
"toggleLabel": "Activar la sala d'espèra",
|
||||
"admit": "Acceptar",
|
||||
"admitAll": "Tot acceptar",
|
||||
"rejectAll": "Tot regetar",
|
||||
"errorMissingPassword": "Mercés de picar lo senhal de la conferéncia",
|
||||
"disableDialogContent": "La sala d’espèra es actualament activada. Aquesta foncionalitat assegura que los participants indesirables poiràn pas participar. La volètz desactivar ?",
|
||||
"enableDialogText": "La sala d’espèra vos permet de protegir vòstras conferéncias en autorizant las personas de dintrar qu’aprèp l’aprobacion formala d’un moderator."
|
||||
"toggleLabel": "Activar la sala d'espèra"
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
@@ -577,15 +529,16 @@
|
||||
"disconnected": "desconnectat",
|
||||
"focus": "Focus de conferéncia",
|
||||
"focusFail": "{{component}} es pas disponible - ensajatz tornamai dins {{ms}} sec",
|
||||
"grantedTo": "Dreches moderator acordats a {{to}} !",
|
||||
"invitedOneMember": "{{name}} es estat convidat",
|
||||
"invitedThreePlusMembers": "{{name}} e {{count}} autres son estats convidats",
|
||||
"invitedTwoMembers": "{{first}} e {{second}} son estats convidats",
|
||||
"kickParticipant": "{{kicked}} es estat expulsat per {{kicker}}",
|
||||
"me": "Ieu",
|
||||
"moderator": "Sètz ara moderator",
|
||||
"moderator": "Dreches moderator acordats !",
|
||||
"muted": "Avètz començat la conversacion en mut.",
|
||||
"mutedRemotelyDescription": "Podètz totjorn activar vòstre microfòn per prendre la paraula. Desactivatz lo microfò quand terminetz per evitar los bruches parasits.",
|
||||
"mutedRemotelyTitle": "{{participantDisplayName}} vos a mes en silenci",
|
||||
"mutedRemotelyTitle": "{{participantDisplayName}} vos a mes en silenci !",
|
||||
"mutedTitle": "Sètz en mut !",
|
||||
"newDeviceAction": "Utilizar",
|
||||
"newDeviceAudioTitle": "Nòu periferic àudio detectat",
|
||||
@@ -600,30 +553,9 @@
|
||||
"somebody": "Qualqu'un",
|
||||
"startSilentDescription": "Rejónher la conferéncia per activar l’àudio",
|
||||
"startSilentTitle": "Avètz jonch sens cap de sortida àudio !",
|
||||
"suboptimalBrowserWarning": "Planhèm que vòstra experiéncia de la conferéncia siá pas de las bonas. Sèm a cercar de solucions per melhorar aquò, d’aquel temps, ensajatz un dels <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigadors compatibles</a>.",
|
||||
"suboptimalExperienceTitle": "Avertiment del navigador",
|
||||
"unmute": "Restablir lo son",
|
||||
"allowAction": "Autorizar",
|
||||
"videoMutedRemotelyTitle": "{{moderator}} a copat vòstra camèra",
|
||||
"moderationInEffectTitle": "Lo moderator a amudit vòstre microfòn",
|
||||
"moderationInEffectVideoTitle": "Lo moderator a blocat vòstra camèra",
|
||||
"videoMutedRemotelyDescription": "La podètz totjorn tornar activar.",
|
||||
"moderationInEffectCSTitle": "Lo moderator a blocat lo partiment d’ecran",
|
||||
"moderationRequestFromModerator": "L’òste volriá que restabliguèssetz vòstre son",
|
||||
"moderationRequestFromParticipant": "Vòl parlar",
|
||||
"moderationStartedTitle": "Moderacion començada",
|
||||
"moderationStoppedTitle": "Moderacion arrestada",
|
||||
"moderationToggleDescription": "per {{participantDisplayName}}",
|
||||
"raiseHandAction": "Levar la man",
|
||||
"reactionSounds": "Desactivar los sons",
|
||||
"groupTitle": "Notificacions",
|
||||
"hostAskedUnmute": "Lo moderator vos que parletz",
|
||||
"moderationInEffectDescription": "Volgatz levar la man se volètz parlar.",
|
||||
"screenShareNoAudioTitle": "Partiment impossible de l’àudio del sistèma !",
|
||||
"allowedUnmute": "Vos podètz restablir lo son del microfòn, aviar la camèra e partejar l’ecran.",
|
||||
"screenShareNoAudio": " La casa Partejar l’àudio es pas estada marcada a l’ecran de seleccion de la fenèstra.",
|
||||
"moderationInEffectCSDescription": "Volgatz levar la man se volètz partejar vòstre ecran.",
|
||||
"moderationInEffectVideoDescription": "Volgatz levar la man se volètz aviar vòstra camèra."
|
||||
"suboptimalBrowserWarning": "Planhèm que vòstra experiéncia de la conferéncia siá pas de las bonas. Sèm a cercar de solucions per melhorar aquò, d’aquel temps, ensajatz un dels <a href='{{recommendedBrowserPageLink}}' target='_blank'>navegators compatibles</a>.",
|
||||
"suboptimalExperienceTitle": "Avertiment del navegador",
|
||||
"unmute": "Restablir lo son"
|
||||
},
|
||||
"passwordDigitsOnly": "Fins a {{number}} chifras",
|
||||
"passwordSetRemotely": "causit per qualqu'un mai",
|
||||
@@ -652,10 +584,7 @@
|
||||
"noVideo": "Nos esperam a trobat vòstra qualitat vidèo òrra.",
|
||||
"veryPoorConnection": "Nos esperam a trobar vòstra qualitat vidèo plan òrra.",
|
||||
"videoFreezing": "Nos esperam a veire vòstra vidèo se gelar, venir negra e se pixelizar.",
|
||||
"videoHighQuality": "Nos esperam a trobar vòstra qualitat vidèo de bona qualitat.",
|
||||
"undetectable": "Se capitatz totjorn pas de sonar del navigador estant, vos recomandam de vos assegurar que vos naut-parlaires, lo microfòn e la camèra son corrèctament configurats, qu’avètz donadas las autorizacions al navegador pel microfòn e la camèra e que lo navigador es a jorn. Se tenètz d’aver de dificultats per sonar, devètz contactar lo desvolopaire de l’aplicacion Web.",
|
||||
"videoLowQuality": "Presumissèm que vòstra vidèo aurà una baissa qualitat tocant la frequéncia d’imatge e la resolucion.",
|
||||
"videoTearing": "Presumissèm que vòstra vidèo serà pixelizada e aurà d’artefacts visuals."
|
||||
"videoHighQuality": "Nos esperam a trobar vòstra qualitat vidèo de bona qualitat."
|
||||
},
|
||||
"copyAndShare": "Copiar e partejar lo ligam de conferéncia",
|
||||
"dialing": "A sonar",
|
||||
@@ -681,11 +610,7 @@
|
||||
"startWithPhone": "Començar amb l’àudio del telefòn",
|
||||
"videoOnlyError": "Error vidèo :",
|
||||
"videoTrackError": "Creacion d’una pista àudio impossibla.",
|
||||
"viewAllNumbers": "veire totes los numèros",
|
||||
"errorValidation": "Validacion del numèro fracassada",
|
||||
"keyboardShortcuts": "Activar los acorchis de clavièr",
|
||||
"errorStatusCode": "Error de numerotacion, còdi d’estat : {{status}}",
|
||||
"errorNoPermissions": "Devètz activar l’accès al microfòn e a la camèra"
|
||||
"viewAllNumbers": "veire totes los numèros"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"busy": "Ocupat",
|
||||
@@ -705,8 +630,7 @@
|
||||
"setDisplayNameLabel": "Causissètz vòstre escais",
|
||||
"setEmailInput": "Picatz lo corrièl",
|
||||
"setEmailLabel": "Definissètz vòstre corrièl per Gravatar",
|
||||
"title": "Perfil",
|
||||
"avatar": "avatar"
|
||||
"title": "Perfil"
|
||||
},
|
||||
"raisedHand": "Volriá charrar",
|
||||
"recording": {
|
||||
@@ -735,13 +659,7 @@
|
||||
"signIn": "Connexion",
|
||||
"signOut": "Se desconnectar",
|
||||
"unavailable": "Ops ! Lo {{serviceName}} es pas disponible pel moment. Sèm a reglar aqueste problèma. Mercés de tornar ensajar mai tard.",
|
||||
"unavailableTitle": "Enregistrament indisponible",
|
||||
"copyLink": "Copiar lo ligam",
|
||||
"linkGenerated": "Avèm generat un ligam pel vòstre enregistrament.",
|
||||
"uploadToCloud": "Enviar al cloud",
|
||||
"limitNotificationDescriptionWeb": "A causa d’una demanda fòrta vòstre enregistrament serà limitat a {{limit}} min. Per d’enregistraments sens limit ensajatz <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "A causa d’una demanda fòrta vòstre enregistrament serà limitat a {{limit}} min. Per d’enregistraments sens limit ensajatz <3>{{app}}</3>.",
|
||||
"errorFetchingLink": "Error en recuperant lo ligam de l’enregistrament."
|
||||
"unavailableTitle": "Enregistrament indisponible"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Tirar per actualizar"
|
||||
@@ -775,19 +693,7 @@
|
||||
"speakers": "Naut-parlairs",
|
||||
"startAudioMuted": "Començan totes sens son",
|
||||
"startVideoMuted": "Començan totes sens vidèo",
|
||||
"title": "Paramètres",
|
||||
"desktopShareFramerate": "Frequéncia d’imatge del partiment de burèu",
|
||||
"framesPerSecond": "frames-per-second",
|
||||
"participantJoined": "Lo participant a rejonch",
|
||||
"participantLeft": "Lo participant es partit",
|
||||
"playSounds": "Lectura dels sons activa",
|
||||
"reactions": "Reaccions de la conferéncia",
|
||||
"sameAsSystem": "Coma pel sistèma ({{label}})",
|
||||
"sounds": "Sons",
|
||||
"talkWhileMuted": "Parlar en mut",
|
||||
"desktopShareHighFpsWarning": "Una frequéncia d’imatge mai nauta pel partiment burèu pòt afectar la benda passanta. Devètz reaviar lo partiment d’ecran per aplicar los paramètres novèls.",
|
||||
"desktopShareWarning": "Devètz reaviar lo partiment d’ecran per prendre en compte las modificacions.",
|
||||
"incomingMessage": "Messatge dintrant"
|
||||
"title": "Paramètres"
|
||||
},
|
||||
"settingsView": {
|
||||
"advanced": "Avançat",
|
||||
@@ -822,8 +728,7 @@
|
||||
"name": "Escais",
|
||||
"seconds": "{{count}} segondas",
|
||||
"speakerStats": "Estatisticas orator",
|
||||
"speakerTime": "Temps de paraula",
|
||||
"search": "Recercar"
|
||||
"speakerTime": "Temps de paraula"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"genericTitle": "Aquesta reünion requerís l'utilizacion del microfòn e de la camèra.",
|
||||
@@ -844,6 +749,7 @@
|
||||
"chat": "Passar a la fenèstra chat",
|
||||
"document": "Tampar los documents partejats",
|
||||
"download": "Telecargar nòstra aplicacion",
|
||||
"e2ee": "Chiframent del cap a la fin",
|
||||
"embedMeeting": "Conferéncia integrada",
|
||||
"feedback": "Daissar un comentari",
|
||||
"fullScreen": "Passar al ecran complèt",
|
||||
@@ -878,23 +784,7 @@
|
||||
"toggleCamera": "Passar a la camèra",
|
||||
"toggleFilmstrip": "Bascular mòde benda film",
|
||||
"videoblur": "Enfoscar o non la vidèo",
|
||||
"videomute": "Silenciar la vidèo",
|
||||
"boo": "Aücar",
|
||||
"clap": "Picar de las mans",
|
||||
"laugh": "Rire",
|
||||
"like": "Levar lo det gròs",
|
||||
"muteEveryonesVideo": "Copar la camèra dels autres",
|
||||
"muteEveryoneElsesVideo": "Copar la camèra de los demai",
|
||||
"participants": "Participants",
|
||||
"remoteVideoMute": "Copar la camèra del participant",
|
||||
"shareaudio": "Partejar l’àudio",
|
||||
"silence": "Amudir",
|
||||
"surprised": "Suspresa",
|
||||
"selectBackground": "Seleccionar un rèireplan",
|
||||
"expand": "Espandir",
|
||||
"collapse": "Plegar",
|
||||
"muteEveryoneElse": "Copar lo microfòn dels autres",
|
||||
"reactionsMenu": "Dobrir / Tampar lo menú de reaccions"
|
||||
"videomute": "Silenciar la vidèo"
|
||||
},
|
||||
"addPeople": "Ajustar de monde a vòstra sonada",
|
||||
"audioOnlyOff": "Desactivar lo mòde connexion febla",
|
||||
@@ -928,8 +818,8 @@
|
||||
"muteEveryone": "Rendre mut tot lo monde",
|
||||
"noAudioSignalDesc": "S’avètz pas volontàriament copat lo son a partir dels paramètres sistèma o material, pensatz de cambiar d’aparelh.",
|
||||
"noAudioSignalDescSuggestion": "S’avètz pas volontàriament copat lo son a partir dels paramètres sistèma o material, pensatz d’utilizar un autre aparelh suggerit.",
|
||||
"noAudioSignalDialInDesc": "Podètz tanben sonar en utilizant :",
|
||||
"noAudioSignalDialInLinkDesc": "Numèros de sonada",
|
||||
"noAudioSignalDialInDesc": "",
|
||||
"noAudioSignalDialInLinkDesc": "",
|
||||
"noAudioSignalTitle": "I a pas cap de son en entrada del microfòn !",
|
||||
"noisyAudioInputDesc": "Sembla que vòstre microfòn mene bruch, pensatz de lo copar o de lo cambiar.",
|
||||
"noisyAudioInputTitle": "Vòstre microfòn sembla brusent !",
|
||||
@@ -947,35 +837,15 @@
|
||||
"speakerStats": "Estatisticas parladors",
|
||||
"startScreenSharing": "Aviar lo partatge d’ecran",
|
||||
"startSubtitles": "Aviar los sostítols",
|
||||
"startvideoblur": "Trebolar mon rèire-plan",
|
||||
"stopScreenSharing": "Arrestar lo partatge d’ecran",
|
||||
"stopSharedVideo": "Arrestar la vidèo Youtube",
|
||||
"stopSubtitles": "Arrestar los sostítols",
|
||||
"stopvideoblur": "Desactivar lo borrolatge del rèire-plan",
|
||||
"talkWhileMutedPopup": "Ensajatz de parlar ? Vòstre microfòn es copat.",
|
||||
"tileViewToggle": "Activar/Desactivar la vista en mosaïc",
|
||||
"toggleCamera": "Passar a la camèra",
|
||||
"videomute": "Aviar / Arrestar la camèra",
|
||||
"openReactionsMenu": "Dobrir lo menú de reaccions",
|
||||
"participants": "Participants",
|
||||
"shareaudio": "Partejar l’àudio",
|
||||
"silence": "Amudir",
|
||||
"surprised": "Suspresa",
|
||||
"selectBackground": "Seleccionar un rèireplan",
|
||||
"audioSettings": "Paramètres àudio",
|
||||
"videoSettings": "Paramètres vidèo",
|
||||
"laugh": "Rire",
|
||||
"muteEveryonesVideo": "Desactivar la camèra de tot lo monde",
|
||||
"stopAudioSharing": "Arrestar lo partiment àudio",
|
||||
"closeReactionsMenu": "Tampar lo menú de reaccions",
|
||||
"boo": "Aücar",
|
||||
"clap": "Picar de las mans",
|
||||
"disableReactionSounds": "Podètz desactivar los sons de las reaccions per aquesta conferéncia",
|
||||
"like": "Levar lo det gròs",
|
||||
"reactionBoo": "Enviar una reaccion de bramada",
|
||||
"reactionLaugh": "Enviar una reaccion de rire",
|
||||
"reactionLike": "Enviar una reaccion d’aprobacion",
|
||||
"reactionSilence": "Enviar una reaccion de silenci",
|
||||
"reactionSurprised": "Enviar una reaccion de suspresa",
|
||||
"reactionClap": "Enviar una reaccion d’aplaudiment"
|
||||
"videomute": "Aviar / Arrestar la camèra"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Aviar / Arrestat los sostítols",
|
||||
@@ -1023,6 +893,8 @@
|
||||
"ld": "Bassa definicion",
|
||||
"ldTooltip": "Difusion vidèo en bassa definicion",
|
||||
"lowDefinition": "Bassa definicion",
|
||||
"onlyAudioAvailable": "Pas que l’àudio es disponible",
|
||||
"onlyAudioSupported": "Sèm compatibles solament amb l’àudio dins aqueste navigator.",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Difusion vidèo en definicion estandard",
|
||||
"standardDefinition": "Definicion estandard"
|
||||
@@ -1039,10 +911,7 @@
|
||||
"muted": "Mut",
|
||||
"remoteControl": "Contraròtle alonhat",
|
||||
"show": "Mostrar davant",
|
||||
"videomute": "Lo participant a arrestat la camèra",
|
||||
"domuteVideo": "Desactivar la camèra",
|
||||
"domuteVideoOfOthers": "Desactivar la camèra dels demai",
|
||||
"videoMuted": "Camèra desactivada"
|
||||
"videomute": "Lo participant a arrestat la camèra"
|
||||
},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
@@ -1063,6 +932,7 @@
|
||||
"goSmall": "Crear",
|
||||
"headerTitle": "Jitsi Meet",
|
||||
"info": "Infor",
|
||||
"jitsiMeet": "Jitsi Meet",
|
||||
"jitsiOnMobile": "Jitsi sus mobil –telecargatz nòstra aplicacion e començatz de conferéncias de pertot",
|
||||
"join": "CREAR / REJÓNHER",
|
||||
"moderatedMessage": "O <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservatz una URL de conferéncia</a> a l'avança ont sètz l'unic moderator.",
|
||||
@@ -1074,99 +944,10 @@
|
||||
"roomname": "Sasissètz un nom de sala",
|
||||
"roomNameAllowedChars": "Lo nom de la conferéncia deu pas conténer aqueles caractèrs : ?, &, :, ', \", %, #.",
|
||||
"roomnameHint": "Picatz lo nom o l’URL de la sala que volètz jónher. Podètz inventar un nom, cal pas que lo monde que volètz convidar lo sàpian.",
|
||||
"secureMeetings": "Conferéncias seguras e de nauta qualitat",
|
||||
"sendFeedback": "Mandar vòstra opinion",
|
||||
"startMeeting": "Començar la reünion",
|
||||
"terms": "Tèrmes",
|
||||
"title": "Conferéncias vidèo securizadas amb plen de foncionalitats e complètament gratuitas",
|
||||
"addMeetingName": "Nomenar la conferéncia",
|
||||
"mobileDownLoadLinkIos": "Telecargar l'aplicacion per iOS",
|
||||
"mobileDownLoadLinkAndroid": "Telecargar l'aplicacion per Android",
|
||||
"mobileDownLoadLinkFDroid": "Telecargar l'aplicacion per F-Droid",
|
||||
"headerSubtitle": "Conferéncias securizadas e de Nauta qualitat",
|
||||
"logo": {
|
||||
"policyLogo": "Logotipe de la politica",
|
||||
"calendar": "Logotipe Calendar",
|
||||
"microsoftLogo": "Logotipe Microsoft",
|
||||
"logoDeepLinking": "Logotipe Jitsi meet",
|
||||
"desktopPreviewThumbnail": "Vinheta d'apercebut del burèu",
|
||||
"googleLogo": "Logotipe Google"
|
||||
}
|
||||
},
|
||||
"virtualBackground": {
|
||||
"addBackground": "Apondre un rèireplan",
|
||||
"image7": "Solelh levant",
|
||||
"backgroundEffectError": "Aplicacion fracassada de l’efeich al rèireplan.",
|
||||
"apply": "Aplicar",
|
||||
"title": "Rèireplans virtuals",
|
||||
"blur": "Fosc",
|
||||
"slightBlur": "Fosc leugièr",
|
||||
"removeBackground": "Suprimir lo rèireplan",
|
||||
"pleaseWait": "Esperatz...",
|
||||
"none": "Cap",
|
||||
"uploadedImage": "Imatge enviat {{index}}",
|
||||
"deleteImage": "Suprimir l’imatge",
|
||||
"image1": "Plaja",
|
||||
"image2": "Paret blanca neutra",
|
||||
"image3": "Sala voida blanca",
|
||||
"image4": "Lampadari negre",
|
||||
"image5": "Montanha",
|
||||
"image6": "Forèst ",
|
||||
"desktopShareError": "Creacion impossibla d’un partiment de burèu",
|
||||
"desktopShare": "Partiment de burèu",
|
||||
"webAssemblyWarning": "WebAssembly pas pres en carga"
|
||||
},
|
||||
"participantsPane": {
|
||||
"headings": {
|
||||
"lobby": "Sala d’espèra ({{count}})",
|
||||
"participantsList": "Participants de la conferéncia ({{count}})",
|
||||
"waitingLobby": "Dins la sala d'espèra ({{count}})"
|
||||
},
|
||||
"close": "Tancar",
|
||||
"header": "Participants",
|
||||
"actions": {
|
||||
"allow": "Permetre als convidats de :",
|
||||
"audioModeration": "Se tornar lo son",
|
||||
"blockEveryoneMicCamera": "Blocar lo microfòn e la camèra del monde",
|
||||
"invite": "Convidar qualqu'un",
|
||||
"askUnmute": "Demandar a restablir lo son",
|
||||
"mute": "Amudir",
|
||||
"muteAll": "Amudir tot lo monde",
|
||||
"muteEveryoneElse": "Amudir tot los demai",
|
||||
"stopEveryonesVideo": "Arrestar la vidèo de tot lo monde",
|
||||
"stopVideo": "Arrestar la vidèo",
|
||||
"unblockEveryoneMicCamera": "Desblocar lo microfòn e la camèra de tot lo monde",
|
||||
"videoModeration": "Aviar lor vidèo",
|
||||
"allowVideo": "Autorizar la vidèo"
|
||||
}
|
||||
},
|
||||
"jitsiHome": "{{logo}} Logotipe, mena a la pagina d'acuèlh",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "Apondre una opcion",
|
||||
"answerPlaceholder": "Opcion {{index}}",
|
||||
"create": "Crear un sondatge",
|
||||
"cancel": "Anullar",
|
||||
"pollOption": "Opcion sondarge {{index}}",
|
||||
"pollQuestion": "Question del sondatge",
|
||||
"questionPlaceholder": "Pausar una question",
|
||||
"removeOption": "Suprimir l'opcion",
|
||||
"send": "Enviar"
|
||||
},
|
||||
"answer": {
|
||||
"skip": "Sautar",
|
||||
"submit": "Enviar"
|
||||
},
|
||||
"results": {
|
||||
"vote": "Votar",
|
||||
"changeVote": "Cambiar lo vote",
|
||||
"hideDetailedResults": "Rescondre los detalhs",
|
||||
"showDetailedResults": "Mostrar los detalhs",
|
||||
"empty": "I a pas cap de sondatge dins la conferéncia pel moment. Començatz-ne un aicí !"
|
||||
},
|
||||
"notification": {
|
||||
"title": "Un sondatge novèl es estat apondut a la conferéncia",
|
||||
"description": "Dobrissètz l’onglet dels sondatge per votar"
|
||||
}
|
||||
},
|
||||
"volumeSlider": "Nivèl de volum"
|
||||
"title": "Conferéncias vidèo securizadas amb plen de foncionalitats e complètament gratuitas"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,14 +801,13 @@
|
||||
"moreOptions": "Больше настроек",
|
||||
"mute": "Микрофон (вкл./выкл.)",
|
||||
"muteEveryone": "Выкл. микрофон у всех",
|
||||
"muteEveryonesVideo": "Выкл. Камеру у всех",
|
||||
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
|
||||
"noAudioSignalDesc": "Если вы специально не отключали микрофон в системных настройках, подумайте о том, чтобы поменять его.",
|
||||
"noAudioSignalDescSuggestion": "Если вы специально не отключали микрофон в системных настройках, вы можете попробовать использовать следующее устройство:",
|
||||
"noAudioSignalDialInDesc": "Вы можете также дозвониться используя:",
|
||||
"noAudioSignalDialInLinkDesc": "Номера для дозвона",
|
||||
"noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!",
|
||||
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
|
||||
"noisyAudioInputDesc": "Возможно, ваш микрофон создает шум. Вы можете выключить его или смените устройство.",
|
||||
"noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!",
|
||||
"openChat": "Открыть чат",
|
||||
"participants": "Участники",
|
||||
"pip": "Вкл режим Картинка-в-картинке",
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
"muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantsVideoDialog": "Are you sure you want to turn off this participant's camera? You won't be able to turn the camera back on, but they can turn it back on at any time.",
|
||||
"muteParticipantTitle": "Mute this participant?",
|
||||
"muteParticipantsVideoButton": "Stop video",
|
||||
"muteParticipantsVideoButton": "Stop camera",
|
||||
"muteParticipantsVideoTitle": "Disable camera of this participant?",
|
||||
"muteParticipantsVideoBody": "You won't be able to turn the camera back on, but they can turn it back on at any time.",
|
||||
"noDropboxToken": "No valid Dropbox token",
|
||||
@@ -897,8 +897,8 @@
|
||||
"mute": "Mute / Unmute",
|
||||
"muteEveryone": "Mute everyone",
|
||||
"muteEveryoneElse": "Mute everyone else",
|
||||
"muteEveryonesVideo": "Disable everyone's video",
|
||||
"muteEveryoneElsesVideo": "Disable everyone else's video",
|
||||
"muteEveryonesVideo": "Disable everyone's camera",
|
||||
"muteEveryoneElsesVideo": "Disable everyone else's camera",
|
||||
"participants": "Participants",
|
||||
"pip": "Toggle Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
# Jitsi Meet Translation
|
||||
|
||||
Jitsi Meet Translation
|
||||
==========================
|
||||
Jitsi Meet uses [i18next](http://i18next.com) library for translation.
|
||||
i18next uses separate json files for each language.
|
||||
|
||||
|
||||
## Translating Jitsi Meet
|
||||
|
||||
Translating Jitsi Meet
|
||||
======================
|
||||
The translation of Jitsi Meet is handled editing manually the language files.
|
||||
|
||||
You can use the `update-translation.js` script as follows to help you with that:
|
||||
|
||||
```js
|
||||
cd lang
|
||||
node update-translation.js main-es.json
|
||||
```
|
||||
|
||||
That will cause the `main-es.json` file to be updated with all the missing keys set as empty
|
||||
strings. All that's missing is for you to fill in the blanks!
|
||||
|
||||
## Development
|
||||
|
||||
Development
|
||||
===========
|
||||
If you want to add new functionality for Jitsi Meet and you have texts that need to be translated you must add key and value in main.json file in English for each translatable text.
|
||||
Than you can use the key to get the translated text for the current language.
|
||||
|
||||
@@ -53,3 +43,7 @@ You can add translatable text in the HTML:
|
||||
For the available values of ``options`` parameter for the above methods of translation module see [i18next documentation](http://i18next.com/pages/doc_features).
|
||||
|
||||
**Note:** It is useful to add attributes in the HTML for persistent HTML elements because when the language is changed the text will be automatically translated.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/* eslint-disable */
|
||||
|
||||
const fs = require('fs');
|
||||
const process = require('process');
|
||||
const traverse = require('traverse');
|
||||
const mainLang = require('./main.json');
|
||||
|
||||
const [ targetLangFile ] = process.argv.slice(-1);
|
||||
|
||||
if (!targetLangFile) {
|
||||
console.log('No target language file specified');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const targetLang = require(`./${targetLangFile}`);
|
||||
|
||||
const paths = traverse(mainLang).reduce(function(acc, item) {
|
||||
if (this.isLeaf) {
|
||||
acc.push(this.path);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const result = {};
|
||||
|
||||
for (const path of paths) {
|
||||
if (traverse(targetLang).has(path)) {
|
||||
traverse(result).set(path, traverse(targetLang).get(path));
|
||||
} else {
|
||||
//console.log(`${path.join('.')} is missing`);
|
||||
traverse(result).set(path, '');
|
||||
}
|
||||
}
|
||||
|
||||
const data = JSON.stringify(result, undefined, 4);
|
||||
|
||||
fs.writeFileSync(`./${targetLangFile}`, data);
|
||||
@@ -1033,15 +1033,6 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application that the data channel has been opened.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyDataChannelOpened() {
|
||||
this._sendEvent({ name: 'data-channel-opened' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that we are ready to be
|
||||
* closed.
|
||||
|
||||
1
modules/API/external/external_api.js
vendored
1
modules/API/external/external_api.js
vendored
@@ -80,7 +80,6 @@ const events = {
|
||||
'camera-error': 'cameraError',
|
||||
'chat-updated': 'chatUpdated',
|
||||
'content-sharing-participants-changed': 'contentSharingParticipantsChanged',
|
||||
'data-channel-opened': 'dataChannelOpened',
|
||||
'device-list-changed': 'deviceListChanged',
|
||||
'display-name-change': 'displayNameChange',
|
||||
'email-change': 'emailChange',
|
||||
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -11117,8 +11117,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#afc1c34e7504782e07bca952e0ae4b39ed6fc144",
|
||||
"from": "github:jitsi/lib-jitsi-meet#afc1c34e7504782e07bca952e0ae4b39ed6fc144",
|
||||
"version": "github:jitsi/lib-jitsi-meet#1bef6319fbdfdfb03b51d3f7efa348a111ac5805",
|
||||
"from": "github:jitsi/lib-jitsi-meet#1bef6319fbdfdfb03b51d3f7efa348a111ac5805",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "1.0.2",
|
||||
"@jitsi/sdp-interop": "github:jitsi/sdp-interop#4669790bb9020cc8f10c1d1f3823c26b08497547",
|
||||
@@ -15201,9 +15201,8 @@
|
||||
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
|
||||
},
|
||||
"react-native-webrtc": {
|
||||
"version": "1.92.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.92.2.tgz",
|
||||
"integrity": "sha512-eq8otVRvLBD/kSM53SPymyrI7sq06/qejwhiLQKVlUsxaXTKPf3C5lktXsnw5OGZUmVk7y5rH+8ZJ3k+422t6g==",
|
||||
"version": "github:react-native-webrtc/react-native-webrtc#aeb735154c9393bbfde40bf02b797e6eeb91f63a",
|
||||
"from": "github:react-native-webrtc/react-native-webrtc#aeb735154c9393bbfde40bf02b797e6eeb91f63a",
|
||||
"requires": {
|
||||
"base64-js": "^1.1.2",
|
||||
"cross-os": "^1.3.0",
|
||||
@@ -15840,8 +15839,8 @@
|
||||
"integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA=="
|
||||
},
|
||||
"rtcstats": {
|
||||
"version": "github:jitsi/rtcstats#7b3a16b51c84fc860ce0af26f0d035ed5c94d4b2",
|
||||
"from": "github:jitsi/rtcstats#v8.1.0",
|
||||
"version": "github:jitsi/rtcstats#7593044b35b46881fb88af9b20db0f9b660f5752",
|
||||
"from": "github:jitsi/rtcstats#v8.0.1",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "1.0.0",
|
||||
"uuid": "3.1.0"
|
||||
@@ -17777,12 +17776,6 @@
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
||||
"dev": true
|
||||
},
|
||||
"traverse": {
|
||||
"version": "0.6.6",
|
||||
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
|
||||
"integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=",
|
||||
"dev": true
|
||||
},
|
||||
"trim-right": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"jquery-i18next": "1.2.1",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#afc1c34e7504782e07bca952e0ae4b39ed6fc144",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#1bef6319fbdfdfb03b51d3f7efa348a111ac5805",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.1",
|
||||
@@ -92,7 +92,7 @@
|
||||
"react-native-url-polyfill": "1.2.0",
|
||||
"react-native-video": "5.1.1",
|
||||
"react-native-watch-connectivity": "0.4.3",
|
||||
"react-native-webrtc": "1.92.2",
|
||||
"react-native-webrtc": "github:react-native-webrtc/react-native-webrtc#aeb735154c9393bbfde40bf02b797e6eeb91f63a",
|
||||
"react-native-webview": "11.0.2",
|
||||
"react-native-youtube-iframe": "2.1.1",
|
||||
"react-redux": "7.1.0",
|
||||
@@ -103,7 +103,7 @@
|
||||
"redux": "4.0.4",
|
||||
"redux-thunk": "2.2.0",
|
||||
"rnnoise-wasm": "github:jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
|
||||
"rtcstats": "github:jitsi/rtcstats#v8.1.0",
|
||||
"rtcstats": "github:jitsi/rtcstats#v8.0.1",
|
||||
"styled-components": "3.4.9",
|
||||
"util": "0.12.1",
|
||||
"uuid": "3.1.0",
|
||||
@@ -146,7 +146,6 @@
|
||||
"sass": "1.26.8",
|
||||
"string-replace-loader": "2.1.1",
|
||||
"style-loader": "0.19.0",
|
||||
"traverse": "0.6.6",
|
||||
"unorm": "1.6.0",
|
||||
"webpack": "4.43.0",
|
||||
"webpack-bundle-analyzer": "3.4.1",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import '../authentication/middleware';
|
||||
import '../base/devices/middleware';
|
||||
import '../dynamic-branding/middleware';
|
||||
import '../e2ee/middleware';
|
||||
import '../external-api/middleware';
|
||||
import '../keyboard-shortcuts/middleware';
|
||||
|
||||
@@ -74,16 +74,6 @@ export const REQUEST_ENABLE_VIDEO_MODERATION = 'REQUEST_ENABLE_VIDEO_MODERATION'
|
||||
*/
|
||||
export const LOCAL_PARTICIPANT_APPROVED = 'LOCAL_PARTICIPANT_APPROVED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that the local participant had been blocked.
|
||||
*
|
||||
* {
|
||||
* type: LOCAL_PARTICIPANT_REJECTED,
|
||||
* mediaType: MediaType
|
||||
* }
|
||||
*/
|
||||
export const LOCAL_PARTICIPANT_REJECTED = 'LOCAL_PARTICIPANT_REJECTED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals to show notification to the local participant.
|
||||
*
|
||||
@@ -104,17 +94,6 @@ export const LOCAL_PARTICIPANT_MODERATION_NOTIFICATION = 'LOCAL_PARTICIPANT_MODE
|
||||
*/
|
||||
export const PARTICIPANT_APPROVED = 'PARTICIPANT_APPROVED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a participant was blocked for a media type.
|
||||
*
|
||||
* {
|
||||
* type: PARTICIPANT_REJECTED,
|
||||
* mediaType: MediaType
|
||||
* participantId: String
|
||||
* }
|
||||
*/
|
||||
export const PARTICIPANT_REJECTED = 'PARTICIPANT_REJECTED';
|
||||
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a participant asked to have its audio umuted.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { getConferenceState } from '../base/conference';
|
||||
import { MEDIA_TYPE, type MediaType } from '../base/media/constants';
|
||||
import { getParticipantById, isParticipantModerator } from '../base/participants';
|
||||
import { getParticipantById } from '../base/participants';
|
||||
import { isForceMuted } from '../participants-pane/functions';
|
||||
|
||||
import {
|
||||
@@ -16,9 +16,7 @@ import {
|
||||
REQUEST_DISABLE_AUDIO_MODERATION,
|
||||
REQUEST_ENABLE_AUDIO_MODERATION,
|
||||
REQUEST_DISABLE_VIDEO_MODERATION,
|
||||
REQUEST_ENABLE_VIDEO_MODERATION,
|
||||
LOCAL_PARTICIPANT_REJECTED,
|
||||
PARTICIPANT_REJECTED
|
||||
REQUEST_ENABLE_VIDEO_MODERATION
|
||||
} from './actionTypes';
|
||||
import { isEnabledFromState } from './functions';
|
||||
|
||||
@@ -35,57 +33,15 @@ export const approveParticipant = (id: string) => (dispatch: Function, getState:
|
||||
|
||||
const isAudioForceMuted = isForceMuted(participant, MEDIA_TYPE.AUDIO, state);
|
||||
const isVideoForceMuted = isForceMuted(participant, MEDIA_TYPE.VIDEO, state);
|
||||
const isAudioModerationOn = isEnabledFromState(MEDIA_TYPE.AUDIO, state);
|
||||
const isVideoModerationOn = isEnabledFromState(MEDIA_TYPE.VIDEO, state);
|
||||
|
||||
if (!(isAudioModerationOn || isVideoModerationOn) || (isAudioModerationOn && isAudioForceMuted)) {
|
||||
if (isEnabledFromState(MEDIA_TYPE.AUDIO, state) && isAudioForceMuted) {
|
||||
conference.avModerationApprove(MEDIA_TYPE.AUDIO, id);
|
||||
}
|
||||
if (isVideoModerationOn && isVideoForceMuted) {
|
||||
if (isEnabledFromState(MEDIA_TYPE.VIDEO, state) && isVideoForceMuted) {
|
||||
conference.avModerationApprove(MEDIA_TYPE.VIDEO, id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Action used by moderator to reject audio for a participant.
|
||||
*
|
||||
* @param {staring} id - The id of the participant to be rejected.
|
||||
* @returns {void}
|
||||
*/
|
||||
export const rejectParticipantAudio = (id: string) => (dispatch: Function, getState: Function) => {
|
||||
const state = getState();
|
||||
const { conference } = getConferenceState(state);
|
||||
const audioModeration = isEnabledFromState(MEDIA_TYPE.AUDIO, state);
|
||||
|
||||
const participant = getParticipantById(state, id);
|
||||
const isAudioForceMuted = isForceMuted(participant, MEDIA_TYPE.AUDIO, state);
|
||||
const isModerator = isParticipantModerator(participant);
|
||||
|
||||
if (audioModeration && !isAudioForceMuted && !isModerator) {
|
||||
conference.avModerationReject(MEDIA_TYPE.AUDIO, id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Action used by moderator to reject video for a participant.
|
||||
*
|
||||
* @param {staring} id - The id of the participant to be rejected.
|
||||
* @returns {void}
|
||||
*/
|
||||
export const rejectParticipantVideo = (id: string) => (dispatch: Function, getState: Function) => {
|
||||
const state = getState();
|
||||
const { conference } = getConferenceState(state);
|
||||
const videoModeration = isEnabledFromState(MEDIA_TYPE.VIDEO, state);
|
||||
|
||||
const participant = getParticipantById(state, id);
|
||||
const isVideoForceMuted = isForceMuted(participant, MEDIA_TYPE.VIDEO, state);
|
||||
const isModerator = isParticipantModerator(participant);
|
||||
|
||||
if (videoModeration && !isVideoForceMuted && !isModerator) {
|
||||
conference.avModerationReject(MEDIA_TYPE.VIDEO, id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio or video moderation is disabled.
|
||||
*
|
||||
@@ -213,21 +169,6 @@ export const localParticipantApproved = (mediaType: MediaType) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Local participant was blocked to be able to unmute audio and video.
|
||||
*
|
||||
* @param {MediaType} mediaType - The media type to disable.
|
||||
* @returns {{
|
||||
* type: LOCAL_PARTICIPANT_REJECTED
|
||||
* }}
|
||||
*/
|
||||
export const localParticipantRejected = (mediaType: MediaType) => {
|
||||
return {
|
||||
type: LOCAL_PARTICIPANT_REJECTED,
|
||||
mediaType
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows notification when A/V moderation is enabled and local participant is still not approved.
|
||||
*
|
||||
@@ -270,21 +211,3 @@ export function participantApproved(id: string, mediaType: MediaType) {
|
||||
mediaType
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A participant was blocked to unmute for a mediaType.
|
||||
*
|
||||
* @param {string} id - The id of the approved participant.
|
||||
* @param {MediaType} mediaType - The media type which was approved.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_REJECTED,
|
||||
* }}
|
||||
*/
|
||||
export function participantRejected(id: string, mediaType: MediaType) {
|
||||
return {
|
||||
type: PARTICIPANT_REJECTED,
|
||||
id,
|
||||
mediaType
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,7 @@ import {
|
||||
enableModeration,
|
||||
localParticipantApproved,
|
||||
participantApproved,
|
||||
participantPendingAudio,
|
||||
localParticipantRejected,
|
||||
participantRejected
|
||||
participantPendingAudio
|
||||
} from './actions';
|
||||
import {
|
||||
ASKED_TO_UNMUTE_SOUND_ID, AUDIO_MODERATION_NOTIFICATION_ID,
|
||||
@@ -178,10 +176,6 @@ StateListenerRegistry.register(
|
||||
}
|
||||
});
|
||||
|
||||
conference.on(JitsiConferenceEvents.AV_MODERATION_REJECTED, ({ mediaType }) => {
|
||||
dispatch(localParticipantRejected(mediaType));
|
||||
});
|
||||
|
||||
conference.on(JitsiConferenceEvents.AV_MODERATION_CHANGED, ({ enabled, mediaType, actor }) => {
|
||||
enabled ? dispatch(enableModeration(mediaType, actor)) : dispatch(disableModeration(mediaType, actor));
|
||||
});
|
||||
@@ -200,14 +194,5 @@ StateListenerRegistry.register(
|
||||
dispatch(dismissPendingParticipant(id, mediaType));
|
||||
});
|
||||
});
|
||||
|
||||
// this is received by moderators
|
||||
conference.on(
|
||||
JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED,
|
||||
({ participant, mediaType }) => {
|
||||
const { _id: id } = participant;
|
||||
|
||||
dispatch(participantRejected(id, mediaType));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,10 +13,8 @@ import {
|
||||
DISMISS_PENDING_PARTICIPANT,
|
||||
ENABLE_MODERATION,
|
||||
LOCAL_PARTICIPANT_APPROVED,
|
||||
LOCAL_PARTICIPANT_REJECTED,
|
||||
PARTICIPANT_APPROVED,
|
||||
PARTICIPANT_PENDING_AUDIO,
|
||||
PARTICIPANT_REJECTED
|
||||
PARTICIPANT_PENDING_AUDIO
|
||||
} from './actionTypes';
|
||||
import { MEDIA_TYPE_TO_PENDING_STORE_KEY } from './constants';
|
||||
|
||||
@@ -107,16 +105,6 @@ ReducerRegistry.register('features/av-moderation', (state = initialState, action
|
||||
};
|
||||
}
|
||||
|
||||
case LOCAL_PARTICIPANT_REJECTED: {
|
||||
const newState = action.mediaType === MEDIA_TYPE.AUDIO
|
||||
? { audioUnmuteApproved: false } : { videoUnmuteApproved: false };
|
||||
|
||||
return {
|
||||
...state,
|
||||
...newState
|
||||
};
|
||||
}
|
||||
|
||||
case PARTICIPANT_PENDING_AUDIO: {
|
||||
const { participant } = action;
|
||||
|
||||
@@ -240,32 +228,6 @@ ReducerRegistry.register('features/av-moderation', (state = initialState, action
|
||||
return state;
|
||||
}
|
||||
|
||||
case PARTICIPANT_REJECTED: {
|
||||
const { mediaType, id } = action;
|
||||
|
||||
if (mediaType === MEDIA_TYPE.AUDIO) {
|
||||
return {
|
||||
...state,
|
||||
audioWhitelist: {
|
||||
...state.audioWhitelist,
|
||||
[id]: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (mediaType === MEDIA_TYPE.VIDEO) {
|
||||
return {
|
||||
...state,
|
||||
videoWhitelist: {
|
||||
...state.videoWhitelist,
|
||||
[id]: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -84,7 +84,6 @@ export default [
|
||||
'disableAEC',
|
||||
'disableAGC',
|
||||
'disableAP',
|
||||
'disableAddingBackgroundImages',
|
||||
'disableAudioLevels',
|
||||
'disableChatSmileys',
|
||||
'disableDeepLinking',
|
||||
@@ -189,7 +188,6 @@ export default [
|
||||
'subject',
|
||||
'testing',
|
||||
'toolbarButtons',
|
||||
'toolbarConfig',
|
||||
'useHostPageLocalStorage',
|
||||
'useTurnUdp',
|
||||
'videoQuality.persist',
|
||||
|
||||
@@ -227,28 +227,6 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
newValue.toolbarButtons = interfaceConfig.TOOLBAR_BUTTONS;
|
||||
}
|
||||
|
||||
if (!oldValue.toolbarConfig) {
|
||||
oldValue.toolbarConfig = {};
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.alwaysVisible !== 'boolean'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE === 'boolean') {
|
||||
newValue.toolbarConfig.alwaysVisible = interfaceConfig.TOOLBAR_ALWAYS_VISIBLE;
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.initialTimeout !== 'number'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.INITIAL_TOOLBAR_TIMEOUT === 'number') {
|
||||
newValue.toolbarConfig.initialTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.timeout !== 'number'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_TIMEOUT === 'number') {
|
||||
newValue.toolbarConfig.timeout = interfaceConfig.TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key]);
|
||||
|
||||
if (filteredConferenceInfo.length) {
|
||||
|
||||
@@ -82,6 +82,7 @@ const _updateLastN = debounce(({ dispatch, getState }) => {
|
||||
lastNSelected = 1;
|
||||
}
|
||||
|
||||
logger.info(`Setting last N to: ${lastNSelected}`);
|
||||
dispatch(setLastN(lastNSelected));
|
||||
}, 1000); /* Don't send this more often than once a second. */
|
||||
|
||||
|
||||
@@ -261,20 +261,17 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
|
||||
*
|
||||
* @param {boolean} enabled - The state to toggle screen sharing to.
|
||||
* @param {boolean} audioOnly - Only share system audio.
|
||||
* @param {boolean} ignoreDidHaveVideo - Wether or not to ignore if video was on when sharing started.
|
||||
* @returns {{
|
||||
* type: TOGGLE_SCREENSHARING,
|
||||
* on: boolean,
|
||||
* audioOnly: boolean,
|
||||
* ignoreDidHaveVideo: boolean
|
||||
* audioOnly: boolean
|
||||
* }}
|
||||
*/
|
||||
export function toggleScreensharing(enabled, audioOnly = false, ignoreDidHaveVideo = false) {
|
||||
export function toggleScreensharing(enabled, audioOnly = false) {
|
||||
return {
|
||||
type: TOGGLE_SCREENSHARING,
|
||||
enabled,
|
||||
audioOnly,
|
||||
ignoreDidHaveVideo
|
||||
audioOnly
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -149,11 +149,10 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
return;
|
||||
}
|
||||
|
||||
const { enabled, audioOnly, ignoreDidHaveVideo } = action;
|
||||
const { enabled, audioOnly } = action;
|
||||
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING, { enabled,
|
||||
audioOnly,
|
||||
ignoreDidHaveVideo });
|
||||
audioOnly });
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -179,7 +178,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
} else if (jitsiTrack.isLocal() && !(jitsiTrack.videoType === VIDEO_TYPE.DESKTOP)) {
|
||||
APP.conference.setVideoMuteStatus();
|
||||
} else if (jitsiTrack.isLocal() && muted && jitsiTrack.videoType === VIDEO_TYPE.DESKTOP) {
|
||||
store.dispatch(toggleScreensharing(false, false, true));
|
||||
store.dispatch(toggleScreensharing(false));
|
||||
} else {
|
||||
APP.UI.setVideoMuted(participantID);
|
||||
}
|
||||
|
||||
@@ -161,8 +161,10 @@ class ConferenceInfo extends Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<div className = 'details-container' >
|
||||
{ this._renderAlwaysVisible() }
|
||||
{ this._renderAutoHide() }
|
||||
{ [
|
||||
this._renderAlwaysVisible(),
|
||||
this._renderAutoHide()
|
||||
] }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* @param {string} path - The URL path.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function extractFqnFromPath() {
|
||||
const parts = window.location.pathname.split('/');
|
||||
export function extractFqnFromPath(path: string) {
|
||||
const parts = path.split('/');
|
||||
const len = parts.length;
|
||||
|
||||
return parts.length > 2 ? `${parts[len - 2]}/${parts[len - 1]}` : '';
|
||||
@@ -28,7 +28,7 @@ export function getDynamicBrandingUrl(state: Object) {
|
||||
}
|
||||
|
||||
const baseUrl = state['features/base/config'].brandingDataUrl;
|
||||
const fqn = extractFqnFromPath();
|
||||
const fqn = extractFqnFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
|
||||
if (baseUrl && fqn) {
|
||||
return `${baseUrl}?conferenceFqn=${encodeURIComponent(fqn)}`;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { APP_WILL_MOUNT } from '../base/app';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { fetchCustomBrandingData } from './actions';
|
||||
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT: {
|
||||
|
||||
store.dispatch(fetchCustomBrandingData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
import { type Image } from '../virtual-background/constants';
|
||||
|
||||
import {
|
||||
SET_DYNAMIC_BRANDING_DATA,
|
||||
@@ -114,15 +113,7 @@ const DEFAULT_STATE = {
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
useDynamicBrandingData: false,
|
||||
|
||||
/**
|
||||
* An array of images to be used as virtual backgrounds instead of the default ones.
|
||||
*
|
||||
* @public
|
||||
* @type {Array<Object>}
|
||||
*/
|
||||
virtualBackgrounds: []
|
||||
useDynamicBrandingData: false
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -140,8 +131,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
inviteDomain,
|
||||
logoClickUrl,
|
||||
logoImageUrl,
|
||||
premeetingBackground,
|
||||
virtualBackgrounds
|
||||
premeetingBackground
|
||||
} = action.value;
|
||||
|
||||
return {
|
||||
@@ -156,8 +146,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
premeetingBackground,
|
||||
customizationFailed: false,
|
||||
customizationReady: true,
|
||||
useDynamicBrandingData: true,
|
||||
virtualBackgrounds: formatImages(virtualBackgrounds || [])
|
||||
useDynamicBrandingData: true
|
||||
};
|
||||
}
|
||||
case SET_DYNAMIC_BRANDING_FAILED: {
|
||||
@@ -177,30 +166,3 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
|
||||
return state;
|
||||
});
|
||||
|
||||
/**
|
||||
* Transforms the branding images into an array of Images objects ready
|
||||
* to be used as virtual backgrounds.
|
||||
*
|
||||
* @param {Array<string>} images -
|
||||
* @private
|
||||
* @returns {{Props}}
|
||||
*/
|
||||
function formatImages(images: Array<string> | Array<Object>): Array<Image> {
|
||||
return images.map((img, i) => {
|
||||
let src;
|
||||
let tooltip;
|
||||
|
||||
if (typeof img === 'object') {
|
||||
({ src, tooltip } = img);
|
||||
} else {
|
||||
src = img;
|
||||
}
|
||||
|
||||
return {
|
||||
id: `branding-${i}`,
|
||||
src,
|
||||
tooltip
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_JOINED,
|
||||
DATA_CHANNEL_OPENED,
|
||||
KICKED_OUT
|
||||
} from '../base/conference';
|
||||
import { NOTIFY_CAMERA_ERROR, NOTIFY_MIC_ERROR } from '../base/devices';
|
||||
@@ -104,10 +103,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
break;
|
||||
}
|
||||
|
||||
case DATA_CHANNEL_OPENED:
|
||||
APP.API.notifyDataChannelOpened();
|
||||
break;
|
||||
|
||||
case DOMINANT_SPEAKER_CHANGED:
|
||||
APP.API.notifyDominantSpeakerChanged(action.participant.id);
|
||||
break;
|
||||
|
||||
@@ -131,7 +131,7 @@ export function sendJaasFeedbackMetadata(conference: Object, feedback: Object) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const meetingFqn = extractFqnFromPath();
|
||||
const meetingFqn = extractFqnFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
const feedbackData = {
|
||||
...feedback,
|
||||
sessionId: conference.sessionId,
|
||||
|
||||
@@ -31,12 +31,14 @@ import { LocalVideoMenuTriggerButton, RemoteVideoMenuTriggerButton } from '../..
|
||||
import { setVolume } from '../../actions.web';
|
||||
import {
|
||||
DISPLAY_MODE_TO_CLASS_NAME,
|
||||
DISPLAY_MODE_TO_STRING,
|
||||
DISPLAY_VIDEO,
|
||||
DISPLAY_VIDEO_WITH_NAME,
|
||||
VIDEO_TEST_EVENTS,
|
||||
SHOW_TOOLBAR_CONTEXT_MENU_AFTER
|
||||
} from '../../constants';
|
||||
import { isVideoPlayable, computeDisplayMode } from '../../functions';
|
||||
import logger from '../../logger';
|
||||
|
||||
const JitsiTrackEvents = JitsiMeetJS.events.track;
|
||||
|
||||
@@ -331,8 +333,11 @@ class Thumbnail extends Component<Props, State> {
|
||||
*/
|
||||
_onDisplayModeChanged() {
|
||||
const input = Thumbnail.getDisplayModeInput(this.props, this.state);
|
||||
const displayModeString = DISPLAY_MODE_TO_STRING[this.state.displayMode];
|
||||
const id = this.props._participant?.id;
|
||||
|
||||
this._maybeSendScreenSharingIssueEvents(input);
|
||||
logger.debug(`Displaying ${displayModeString} for ${id}, data: [${JSON.stringify(input)}]`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -315,9 +315,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||
*/
|
||||
_parseQueryResults(response = []) {
|
||||
const { t, _dialOutEnabled } = this.props;
|
||||
|
||||
const userTypes = [ INVITE_TYPES.USER, INVITE_TYPES.VIDEO_ROOM, INVITE_TYPES.ROOM ];
|
||||
const users = response.filter(item => userTypes.includes(item.type));
|
||||
const users = response.filter(item => item.type === INVITE_TYPES.USER);
|
||||
const userDisplayItems = [];
|
||||
|
||||
for (const user of users) {
|
||||
|
||||
@@ -66,20 +66,9 @@ class NumbersList extends Component<Props> {
|
||||
(resultNumbers, number) => {
|
||||
// The i18n-iso-countries package insists on upper case.
|
||||
const countryCode = number.countryCode.toUpperCase();
|
||||
|
||||
let countryName;
|
||||
|
||||
if (countryCode === 'SIP') {
|
||||
countryName = t('info.sip');
|
||||
} else {
|
||||
countryName = t(`countries:countries.${countryCode}`);
|
||||
|
||||
// Some countries have multiple names as US ['United States of America', 'USA']
|
||||
// choose the first one if that is the case
|
||||
if (!countryName) {
|
||||
countryName = t(`countries:countries.${countryCode}.0`);
|
||||
}
|
||||
}
|
||||
const countryName = countryCode === 'SIP'
|
||||
? t('info.sip')
|
||||
: t(`countries:countries.${countryCode}`);
|
||||
|
||||
if (resultNumbers[countryName]) {
|
||||
resultNumbers[countryName].push(number);
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { Component } from 'react';
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { setColorAlpha } from '../../base/util';
|
||||
import { fetchCustomBrandingData } from '../../dynamic-branding';
|
||||
import { SharedVideo } from '../../shared-video/components/web';
|
||||
import { Captions } from '../../subtitles/';
|
||||
|
||||
@@ -27,6 +28,11 @@ type Props = {
|
||||
*/
|
||||
_customBackgroundImageUrl: string,
|
||||
|
||||
/**
|
||||
* Fetches the branding data.
|
||||
*/
|
||||
_fetchCustomBrandingData: Function,
|
||||
|
||||
/**
|
||||
* Prop that indicates whether the chat is open.
|
||||
*/
|
||||
@@ -46,6 +52,14 @@ type Props = {
|
||||
* @extends Component
|
||||
*/
|
||||
class LargeVideo extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props._fetchCustomBrandingData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
@@ -153,4 +167,8 @@ function _mapStateToProps(state) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(LargeVideo);
|
||||
const _mapDispatchToProps = {
|
||||
_fetchCustomBrandingData: fetchCustomBrandingData
|
||||
};
|
||||
|
||||
export default connect(_mapStateToProps, _mapDispatchToProps)(LargeVideo);
|
||||
|
||||
@@ -3,202 +3,10 @@
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
conferenceWillJoin,
|
||||
getCurrentConference,
|
||||
sendLocalParticipant,
|
||||
setPassword
|
||||
getCurrentConference
|
||||
} from '../base/conference';
|
||||
import { getLocalParticipant } from '../base/participants';
|
||||
|
||||
import {
|
||||
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
|
||||
KNOCKING_PARTICIPANT_LEFT,
|
||||
SET_KNOCKING_STATE,
|
||||
SET_LOBBY_MODE_ENABLED,
|
||||
SET_PASSWORD_JOIN_FAILED,
|
||||
SET_LOBBY_VISIBILITY
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Tries to join with a preset password.
|
||||
*
|
||||
* @param {string} password - The password to join with.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function joinWithPassword(password: string) {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
dispatch(setPassword(conference, conference.join, password));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to be dispatched when a knocking poarticipant leaves before any response.
|
||||
*
|
||||
* @param {string} id - The ID of the participant.
|
||||
* @returns {{
|
||||
* id: string,
|
||||
* type: KNOCKING_PARTICIPANT_LEFT
|
||||
* }}
|
||||
*/
|
||||
export function knockingParticipantLeft(id: string) {
|
||||
return {
|
||||
id,
|
||||
type: KNOCKING_PARTICIPANT_LEFT
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to be executed when a participant starts knocking or an already knocking participant gets updated.
|
||||
*
|
||||
* @param {Object} participant - The knocking participant.
|
||||
* @returns {{
|
||||
* participant: Object,
|
||||
* type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
|
||||
* }}
|
||||
*/
|
||||
export function participantIsKnockingOrUpdated(participant: Object) {
|
||||
return {
|
||||
participant,
|
||||
type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves (lets in) or rejects a knocking participant.
|
||||
*
|
||||
* @param {string} id - The id of the knocking participant.
|
||||
* @param {boolean} approved - True if the participant is approved, false otherwise.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setKnockingParticipantApproval(id: string, approved: boolean) {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
if (conference) {
|
||||
if (approved) {
|
||||
conference.lobbyApproveAccess(id);
|
||||
} else {
|
||||
conference.lobbyDenyAccess(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to admit multiple participants in the conference.
|
||||
*
|
||||
* @param {Array<Object>} participants - A list of knocking participants.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function admitMultiple(participants: Array<Object>) {
|
||||
return (dispatch: Function, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
participants.forEach(p => {
|
||||
conference.lobbyApproveAccess(p.id);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves the request of a knocking participant to join the meeting.
|
||||
*
|
||||
* @param {string} id - The id of the knocking participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function approveKnockingParticipant(id: string) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
conference && conference.lobbyApproveAccess(id);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Denies the request of a knocking participant to join the meeting.
|
||||
*
|
||||
* @param {string} id - The id of the knocking participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function rejectKnockingParticipant(id: string) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
conference && conference.lobbyDenyAccess(id);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to set the knocking state of the participant.
|
||||
*
|
||||
* @param {boolean} knocking - The new state.
|
||||
* @returns {{
|
||||
* state: boolean,
|
||||
* type: SET_KNOCKING_STATE
|
||||
* }}
|
||||
*/
|
||||
export function setKnockingState(knocking: boolean) {
|
||||
return {
|
||||
knocking,
|
||||
type: SET_KNOCKING_STATE
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to set the new state of the lobby mode.
|
||||
*
|
||||
* @param {boolean} enabled - The new state to set.
|
||||
* @returns {{
|
||||
* enabled: boolean,
|
||||
* type: SET_LOBBY_MODE_ENABLED
|
||||
* }}
|
||||
*/
|
||||
export function setLobbyModeEnabled(enabled: boolean) {
|
||||
return {
|
||||
enabled,
|
||||
type: SET_LOBBY_MODE_ENABLED
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to be dispatched when we failed to join with a password.
|
||||
*
|
||||
* @param {boolean} failed - True of recent password join failed.
|
||||
* @returns {{
|
||||
* failed: boolean,
|
||||
* type: SET_PASSWORD_JOIN_FAILED
|
||||
* }}
|
||||
*/
|
||||
export function setPasswordJoinFailed(failed: boolean) {
|
||||
return {
|
||||
failed,
|
||||
type: SET_PASSWORD_JOIN_FAILED
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts knocking and waiting for approval.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function startKnocking() {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { membersOnly } = state['features/base/conference'];
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
dispatch(conferenceWillJoin(membersOnly));
|
||||
|
||||
// We need to update the conference object with the current display name, if approved
|
||||
// we want to send that display name, it was not updated in case when pre-join is disabled
|
||||
sendLocalParticipant(state, membersOnly);
|
||||
|
||||
membersOnly.joinLobby(localParticipant.name, localParticipant.email);
|
||||
dispatch(setKnockingState(true));
|
||||
};
|
||||
}
|
||||
import { SET_LOBBY_VISIBILITY } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Action to toggle lobby mode on or off.
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import { appNavigate } from '../app/actions';
|
||||
import { openDialog } from '../base/dialog';
|
||||
|
||||
import { DisableLobbyModeDialog, EnableLobbyModeDialog } from './components/native';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
* Cancels the ongoing knocking and abandons the join flow.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function cancelKnocking() {
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
dispatch(appNavigate(undefined));
|
||||
};
|
||||
}
|
||||
export * from './actions.web';
|
||||
|
||||
/**
|
||||
* Action to show the dialog to disable lobby mode.
|
||||
|
||||
@@ -2,22 +2,223 @@
|
||||
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import { maybeRedirectToWelcomePage } from '../app/actions';
|
||||
|
||||
import { appNavigate, maybeRedirectToWelcomePage } from '../app/actions';
|
||||
import {
|
||||
conferenceWillJoin,
|
||||
getCurrentConference,
|
||||
sendLocalParticipant,
|
||||
setPassword
|
||||
} from '../base/conference';
|
||||
import { getLocalParticipant } from '../base/participants';
|
||||
export * from './actions.any';
|
||||
|
||||
import {
|
||||
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
|
||||
KNOCKING_PARTICIPANT_LEFT,
|
||||
SET_KNOCKING_STATE,
|
||||
SET_LOBBY_MODE_ENABLED,
|
||||
SET_PASSWORD_JOIN_FAILED
|
||||
} from './actionTypes';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Cancels the ongoing knocking and abandons the join flow.
|
||||
* Cancels the ongoing knocking and abandones the join flow.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function cancelKnocking() {
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
// when we are redirecting the library should handle any
|
||||
// unload and clean of the connection.
|
||||
APP.API.notifyReadyToClose();
|
||||
dispatch(maybeRedirectToWelcomePage());
|
||||
if (typeof APP !== 'undefined') {
|
||||
// when we are redirecting the library should handle any
|
||||
// unload and clean of the connection.
|
||||
APP.API.notifyReadyToClose();
|
||||
dispatch(maybeRedirectToWelcomePage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(appNavigate(undefined));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to join with a preset password.
|
||||
*
|
||||
* @param {string} password - The password to join with.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function joinWithPassword(password: string) {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
dispatch(setPassword(conference, conference.join, password));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to be dispatched when a knocking poarticipant leaves before any response.
|
||||
*
|
||||
* @param {string} id - The ID of the participant.
|
||||
* @returns {{
|
||||
* id: string,
|
||||
* type: KNOCKING_PARTICIPANT_LEFT
|
||||
* }}
|
||||
*/
|
||||
export function knockingParticipantLeft(id: string) {
|
||||
return {
|
||||
id,
|
||||
type: KNOCKING_PARTICIPANT_LEFT
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to be executed when a participant starts knocking or an already knocking participant gets updated.
|
||||
*
|
||||
* @param {Object} participant - The knocking participant.
|
||||
* @returns {{
|
||||
* participant: Object,
|
||||
* type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
|
||||
* }}
|
||||
*/
|
||||
export function participantIsKnockingOrUpdated(participant: Object) {
|
||||
return {
|
||||
participant,
|
||||
type: KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves (lets in) or rejects a knocking participant.
|
||||
*
|
||||
* @param {string} id - The id of the knocking participant.
|
||||
* @param {boolean} approved - True if the participant is approved, false otherwise.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setKnockingParticipantApproval(id: string, approved: boolean) {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
if (conference) {
|
||||
if (approved) {
|
||||
conference.lobbyApproveAccess(id);
|
||||
} else {
|
||||
conference.lobbyDenyAccess(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to admit multiple participants in the conference.
|
||||
*
|
||||
* @param {Array<Object>} participants - A list of knocking participants.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function admitMultiple(participants: Array<Object>) {
|
||||
return (dispatch: Function, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
participants.forEach(p => {
|
||||
conference.lobbyApproveAccess(p.id);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves the request of a knocking participant to join the meeting.
|
||||
*
|
||||
* @param {string} id - The id of the knocking participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function approveKnockingParticipant(id: string) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
conference && conference.lobbyApproveAccess(id);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Denies the request of a knocking participant to join the meeting.
|
||||
*
|
||||
* @param {string} id - The id of the knocking participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function rejectKnockingParticipant(id: string) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
conference && conference.lobbyDenyAccess(id);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to set the knocking state of the participant.
|
||||
*
|
||||
* @param {boolean} knocking - The new state.
|
||||
* @returns {{
|
||||
* state: boolean,
|
||||
* type: SET_KNOCKING_STATE
|
||||
* }}
|
||||
*/
|
||||
export function setKnockingState(knocking: boolean) {
|
||||
return {
|
||||
knocking,
|
||||
type: SET_KNOCKING_STATE
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to set the new state of the lobby mode.
|
||||
*
|
||||
* @param {boolean} enabled - The new state to set.
|
||||
* @returns {{
|
||||
* enabled: boolean,
|
||||
* type: SET_LOBBY_MODE_ENABLED
|
||||
* }}
|
||||
*/
|
||||
export function setLobbyModeEnabled(enabled: boolean) {
|
||||
return {
|
||||
enabled,
|
||||
type: SET_LOBBY_MODE_ENABLED
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to be dispatched when we failed to join with a password.
|
||||
*
|
||||
* @param {boolean} failed - True of recent password join failed.
|
||||
* @returns {{
|
||||
* failed: boolean,
|
||||
* type: SET_PASSWORD_JOIN_FAILED
|
||||
* }}
|
||||
*/
|
||||
export function setPasswordJoinFailed(failed: boolean) {
|
||||
return {
|
||||
failed,
|
||||
type: SET_PASSWORD_JOIN_FAILED
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts knocking and waiting for approval.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function startKnocking() {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { membersOnly } = state['features/base/conference'];
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
dispatch(conferenceWillJoin(membersOnly));
|
||||
|
||||
// We need to update the conference object with the current display name, if approved
|
||||
// we want to send that display name, it was not updated in case when pre-join is disabled
|
||||
sendLocalParticipant(state, membersOnly);
|
||||
|
||||
membersOnly.joinLobby(localParticipant.name, localParticipant.email);
|
||||
dispatch(setKnockingState(true));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { conferenceWillJoin, getConferenceName } from '../../base/conference';
|
||||
import { getConferenceName } from '../../base/conference';
|
||||
import { getFeatureFlag, INVITE_ENABLED } from '../../base/flags';
|
||||
import { getLocalParticipant } from '../../base/participants';
|
||||
import { getFieldValue } from '../../base/react';
|
||||
@@ -33,11 +33,6 @@ export type Props = {
|
||||
*/
|
||||
_meetingName: string,
|
||||
|
||||
/**
|
||||
* The members only conference if any,
|
||||
*/
|
||||
_membersOnlyConference: Object,
|
||||
|
||||
/**
|
||||
* The email of the participant about to knock/join.
|
||||
*/
|
||||
@@ -293,9 +288,6 @@ export default class AbstractLobbyScreen<P: Props = Props> extends PureComponent
|
||||
screenState: this.state.displayName ? SCREEN_STATES.VIEW : SCREEN_STATES.EDIT
|
||||
});
|
||||
this.props.dispatch(setPasswordJoinFailed(false));
|
||||
|
||||
// let's return to the correct state after password failed
|
||||
this.props.dispatch(conferenceWillJoin(this.props._membersOnlyConference));
|
||||
}
|
||||
|
||||
_onSwitchToPasswordMode: () => void;
|
||||
@@ -395,13 +387,11 @@ export function _mapStateToProps(state: Object): $Shape<Props> {
|
||||
const { iAmSipGateway } = state['features/base/config'];
|
||||
const showCopyUrlButton = inviteEnabledFlag || !disableInviteFunctions;
|
||||
const deviceStatusVisible = isDeviceStatusVisible(state);
|
||||
const { membersOnly } = state['features/base/conference'];
|
||||
|
||||
return {
|
||||
_deviceStatusVisible: deviceStatusVisible,
|
||||
_knocking: knocking,
|
||||
_meetingName: getConferenceName(state),
|
||||
_membersOnlyConference: membersOnly,
|
||||
_participantEmail: localParticipant?.email,
|
||||
_participantId: participantId,
|
||||
_participantName: localParticipant?.name,
|
||||
|
||||
@@ -28,11 +28,6 @@ type Props = {
|
||||
*/
|
||||
_audioMediaState: MediaState,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
*/
|
||||
_disableModeratorIndicator: boolean,
|
||||
|
||||
/**
|
||||
* The display name of the participant.
|
||||
*/
|
||||
@@ -136,7 +131,6 @@ class MeetingParticipantItem extends PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
_audioMediaState,
|
||||
_disableModeratorIndicator,
|
||||
_displayName,
|
||||
_isModerator,
|
||||
_local,
|
||||
@@ -148,7 +142,6 @@ class MeetingParticipantItem extends PureComponent<Props> {
|
||||
return (
|
||||
<ParticipantItem
|
||||
audioMediaState = { _audioMediaState }
|
||||
disableModeratorIndicator = { _disableModeratorIndicator }
|
||||
displayName = { _displayName }
|
||||
isKnockingParticipant = { false }
|
||||
isModerator = { _isModerator }
|
||||
@@ -178,11 +171,9 @@ function mapStateToProps(state, ownProps): Object {
|
||||
const _isVideoMuted = isParticipantVideoMuted(participant, state);
|
||||
const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
|
||||
const videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
|
||||
const { disableModeratorIndicator } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_audioMediaState: audioMediaState,
|
||||
_disableModeratorIndicator: disableModeratorIndicator,
|
||||
_displayName: getParticipantDisplayName(state, participant?.id),
|
||||
_isAudioMuted,
|
||||
_isFakeParticipant: Boolean(participant?.isFakeParticipant),
|
||||
|
||||
@@ -24,11 +24,6 @@ type Props = {
|
||||
*/
|
||||
children?: Node,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
*/
|
||||
disableModeratorIndicator?: boolean,
|
||||
|
||||
/**
|
||||
* The name of the participant. Used for showing lobby names.
|
||||
*/
|
||||
@@ -78,7 +73,6 @@ type Props = {
|
||||
function ParticipantItem({
|
||||
children,
|
||||
displayName,
|
||||
disableModeratorIndicator,
|
||||
isKnockingParticipant,
|
||||
isModerator,
|
||||
local,
|
||||
@@ -107,9 +101,7 @@ function ParticipantItem({
|
||||
</Text>
|
||||
{ local ? <Text style = { styles.isLocal }>({t('chat.you')})</Text> : null }
|
||||
</View>
|
||||
{isModerator && !disableModeratorIndicator
|
||||
&& <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>
|
||||
}
|
||||
{isModerator && <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>}
|
||||
</View>
|
||||
{
|
||||
!isKnockingParticipant
|
||||
|
||||
@@ -33,7 +33,7 @@ export const LobbyParticipantItem = ({
|
||||
openDrawerForParticipant
|
||||
}: Props) => {
|
||||
const { id } = p;
|
||||
const [ admit, reject ] = useLobbyActions({ participantID: id });
|
||||
const [ admit ] = useLobbyActions({ participantID: id });
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -48,11 +48,6 @@ export const LobbyParticipantItem = ({
|
||||
raisedHand = { p.raisedHand }
|
||||
videoMediaState = { MEDIA_STATE.NONE }
|
||||
youText = { t('chat.you') }>
|
||||
<ParticipantActionButton
|
||||
onClick = { reject }
|
||||
primary = { false }>
|
||||
{t('lobby.reject')}
|
||||
</ParticipantActionButton>
|
||||
<ParticipantActionButton
|
||||
onClick = { admit }
|
||||
primary = { true }>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { approveParticipant } from '../../../av-moderation/actions';
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
|
||||
import { openDialog } from '../../../base/dialog';
|
||||
@@ -13,12 +12,10 @@ import {
|
||||
IconCrown,
|
||||
IconMessage,
|
||||
IconMicDisabled,
|
||||
IconMicrophone,
|
||||
IconMuteEveryoneElse,
|
||||
IconShareVideo,
|
||||
IconVideoOff
|
||||
} from '../../../base/icons';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantByIdOrUndefined,
|
||||
@@ -34,7 +31,7 @@ import { Drawer, DrawerPortal } from '../../../toolbox/components/web';
|
||||
import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu';
|
||||
import { VolumeSlider } from '../../../video-menu/components/web';
|
||||
import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog';
|
||||
import { getComputedOuterHeight, isForceMuted } from '../../functions';
|
||||
import { getComputedOuterHeight } from '../../functions';
|
||||
|
||||
import {
|
||||
ContextMenu,
|
||||
@@ -46,11 +43,6 @@ import {
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Whether or not the participant is audio force muted.
|
||||
*/
|
||||
_isAudioForceMuted: boolean,
|
||||
|
||||
/**
|
||||
* True if the local participant is moderator and false otherwise.
|
||||
*/
|
||||
@@ -76,11 +68,6 @@ type Props = {
|
||||
*/
|
||||
_isParticipantAudioMuted: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the participant is video force muted.
|
||||
*/
|
||||
_isVideoForceMuted: boolean,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
@@ -219,7 +206,6 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
this._onSendPrivateMessage = this._onSendPrivateMessage.bind(this);
|
||||
this._position = this._position.bind(this);
|
||||
this._onVolumeChange = this._onVolumeChange.bind(this);
|
||||
this._onAskToUnmute = this._onAskToUnmute.bind(this);
|
||||
}
|
||||
|
||||
_getCurrentParticipantId: () => string;
|
||||
@@ -358,20 +344,6 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
dispatch(setVolume(id, value));
|
||||
}
|
||||
|
||||
_onAskToUnmute: () => void;
|
||||
|
||||
/**
|
||||
* Handles click on ask to unmute.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onAskToUnmute() {
|
||||
const { _participant, dispatch } = this.props;
|
||||
const { id } = _participant;
|
||||
|
||||
dispatch(approveParticipant(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React Component's componentDidMount.
|
||||
*
|
||||
@@ -401,13 +373,11 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_isAudioForceMuted,
|
||||
_isLocalModerator,
|
||||
_isChatButtonEnabled,
|
||||
_isParticipantModerator,
|
||||
_isParticipantVideoMuted,
|
||||
_isParticipantAudioMuted,
|
||||
_isVideoForceMuted,
|
||||
_localVideoOwner,
|
||||
_participant,
|
||||
_volume = 1,
|
||||
@@ -446,16 +416,6 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
{_isLocalModerator && (
|
||||
<ContextMenuItemGroup>
|
||||
<>
|
||||
{overflowDrawer && (_isAudioForceMuted || _isVideoForceMuted)
|
||||
&& <ContextMenuItem onClick = { this._onAskToUnmute }>
|
||||
<ContextMenuIcon src = { IconMicrophone } />
|
||||
<span>
|
||||
{t(_isAudioForceMuted
|
||||
? 'participantsPane.actions.askUnmute'
|
||||
: 'participantsPane.actions.allowVideo')}
|
||||
</span>
|
||||
</ContextMenuItem>
|
||||
}
|
||||
{
|
||||
!_isParticipantAudioMuted && overflowDrawer
|
||||
&& <ContextMenuItem onClick = { muteAudio(_participant) }>
|
||||
@@ -582,13 +542,11 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
const isLocal = participant?.local ?? true;
|
||||
|
||||
return {
|
||||
_isAudioForceMuted: isForceMuted(participant, MEDIA_TYPE.AUDIO, state),
|
||||
_isLocalModerator,
|
||||
_isChatButtonEnabled,
|
||||
_isParticipantModerator,
|
||||
_isParticipantVideoMuted,
|
||||
_isParticipantAudioMuted,
|
||||
_isVideoForceMuted: isForceMuted(participant, MEDIA_TYPE.VIDEO, state),
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_volume: isLocal ? undefined : id ? participantsVolume[id] : undefined
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { isSupported } from '../../../av-moderation/functions';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
@@ -10,7 +9,6 @@ import {
|
||||
getLocalParticipant,
|
||||
getParticipantByIdOrUndefined,
|
||||
getParticipantDisplayName,
|
||||
isLocalParticipantModerator,
|
||||
isParticipantModerator
|
||||
} from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
@@ -20,7 +18,7 @@ import {
|
||||
isParticipantAudioMuted,
|
||||
isParticipantVideoMuted
|
||||
} from '../../../base/tracks';
|
||||
import { ACTION_TRIGGER, type MediaState, MEDIA_STATE, QUICK_ACTION_BUTTON } from '../../constants';
|
||||
import { ACTION_TRIGGER, type MediaState, MEDIA_STATE } from '../../constants';
|
||||
import {
|
||||
getParticipantAudioMediaState,
|
||||
getParticipantVideoMediaState,
|
||||
@@ -44,30 +42,21 @@ type Props = {
|
||||
_audioTrack: ?Object,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
* Media state for video.
|
||||
*/
|
||||
_disableModeratorIndicator: boolean,
|
||||
_videoMediaState: MediaState,
|
||||
|
||||
|
||||
/**
|
||||
* The display name of the participant.
|
||||
*/
|
||||
_displayName: string,
|
||||
|
||||
/**
|
||||
* Whether or not moderation is supported.
|
||||
*/
|
||||
_isModerationSupported: boolean,
|
||||
|
||||
/**
|
||||
* True if the participant is the local participant.
|
||||
*/
|
||||
_local: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant is moderator.
|
||||
*/
|
||||
_localModerator: boolean,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
@@ -96,11 +85,6 @@ type Props = {
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* Media state for video.
|
||||
*/
|
||||
_videoMediaState: MediaState,
|
||||
|
||||
/**
|
||||
* The translated ask unmute text for the qiuck action buttons.
|
||||
*/
|
||||
@@ -172,17 +156,14 @@ type Props = {
|
||||
function MeetingParticipantItem({
|
||||
_audioMediaState,
|
||||
_audioTrack,
|
||||
_disableModeratorIndicator,
|
||||
_videoMediaState,
|
||||
_displayName,
|
||||
_isModerationSupported,
|
||||
_local,
|
||||
_localModerator,
|
||||
_localVideoOwner,
|
||||
_participant,
|
||||
_participantID,
|
||||
_quickActionButtonType,
|
||||
_raisedHand,
|
||||
_videoMediaState,
|
||||
askUnmuteText,
|
||||
isHighlighted,
|
||||
muteAudio,
|
||||
@@ -234,15 +215,10 @@ function MeetingParticipantItem({
|
||||
askToUnmuteText = t('participantsPane.actions.allowVideo');
|
||||
}
|
||||
|
||||
const buttonType = _isModerationSupported
|
||||
? _localModerator ? QUICK_ACTION_BUTTON.ASK_TO_UNMUTE : _quickActionButtonType
|
||||
: '';
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
actionsTrigger = { ACTION_TRIGGER.HOVER }
|
||||
audioMediaState = { audioMediaState }
|
||||
disableModeratorIndicator = { _disableModeratorIndicator }
|
||||
displayName = { _displayName }
|
||||
isHighlighted = { isHighlighted }
|
||||
isModerator = { isParticipantModerator(_participant) }
|
||||
@@ -259,7 +235,7 @@ function MeetingParticipantItem({
|
||||
&& <>
|
||||
<ParticipantQuickAction
|
||||
askUnmuteText = { askToUnmuteText }
|
||||
buttonType = { buttonType }
|
||||
buttonType = { _quickActionButtonType }
|
||||
muteAudio = { muteAudio }
|
||||
muteParticipantButtonText = { muteParticipantButtonText }
|
||||
participantID = { _participantID } />
|
||||
@@ -303,24 +279,17 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
const _audioTrack = participantID === localParticipantId
|
||||
? getLocalAudioTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantID);
|
||||
|
||||
const { disableModeratorIndicator } = state['features/base/config'];
|
||||
|
||||
const _localModerator = isLocalParticipantModerator(state);
|
||||
|
||||
return {
|
||||
_audioMediaState,
|
||||
_audioTrack,
|
||||
_disableModeratorIndicator: disableModeratorIndicator,
|
||||
_videoMediaState,
|
||||
_displayName: getParticipantDisplayName(state, participant?.id),
|
||||
_isModerationSupported: isSupported()(state),
|
||||
_local: Boolean(participant?.local),
|
||||
_localModerator,
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_participantID: participant?.id,
|
||||
_quickActionButtonType,
|
||||
_raisedHand: Boolean(participant?.raisedHand),
|
||||
_videoMediaState
|
||||
_raisedHand: Boolean(participant?.raisedHand)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import React, { useCallback, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { rejectParticipantAudio } from '../../../av-moderation/actions';
|
||||
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import {
|
||||
@@ -105,7 +104,6 @@ function MeetingParticipants({ participantsCount, showInviteButton, overflowDraw
|
||||
|
||||
const muteAudio = useCallback(id => () => {
|
||||
dispatch(muteRemote(id, MEDIA_TYPE.AUDIO));
|
||||
dispatch(rejectParticipantAudio(id));
|
||||
}, [ dispatch ]);
|
||||
const [ drawerParticipant, closeDrawer, openDrawerForParticipant ] = useParticipantDrawer();
|
||||
|
||||
|
||||
@@ -51,11 +51,6 @@ type Props = {
|
||||
*/
|
||||
children: Node,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
*/
|
||||
disableModeratorIndicator: boolean,
|
||||
|
||||
/**
|
||||
* The name of the participant. Used for showing lobby names.
|
||||
*/
|
||||
@@ -124,21 +119,20 @@ type Props = {
|
||||
* @returns {ReactNode}
|
||||
*/
|
||||
function ParticipantItem({
|
||||
actionsTrigger = ACTION_TRIGGER.HOVER,
|
||||
audioMediaState = MEDIA_STATE.NONE,
|
||||
children,
|
||||
disableModeratorIndicator,
|
||||
displayName,
|
||||
isHighlighted,
|
||||
isModerator,
|
||||
local,
|
||||
onLeave,
|
||||
actionsTrigger = ACTION_TRIGGER.HOVER,
|
||||
audioMediaState = MEDIA_STATE.NONE,
|
||||
videoMediaState = MEDIA_STATE.NONE,
|
||||
displayName,
|
||||
participantID,
|
||||
local,
|
||||
openDrawerForParticipant,
|
||||
overflowDrawer,
|
||||
participantID,
|
||||
raisedHand,
|
||||
t,
|
||||
videoMediaState = MEDIA_STATE.NONE,
|
||||
youText
|
||||
}: Props) {
|
||||
const ParticipantActions = Actions[actionsTrigger];
|
||||
@@ -168,7 +162,7 @@ function ParticipantItem({
|
||||
</ParticipantName>
|
||||
{ local ? <span> ({ youText })</span> : null }
|
||||
</ParticipantNameContainer>
|
||||
{isModerator && !disableModeratorIndicator && <ModeratorLabel>
|
||||
{isModerator && <ModeratorLabel>
|
||||
{t('videothumbnail.moderator')}
|
||||
</ModeratorLabel>}
|
||||
</ParticipantDetailsContainer>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
/**
|
||||
* Action type to signal that joining is in progress.
|
||||
* Action type to signal the start of the conference.
|
||||
*/
|
||||
export const PREJOIN_JOINING_IN_PROGRESS = 'PREJOIN_JOINING_IN_PROGRESS';
|
||||
export const PREJOIN_START_CONFERENCE = 'PREJOIN_START_CONFERENCE';
|
||||
|
||||
/**
|
||||
* Action type to signal that prejoin page was initialized.
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
// @flow
|
||||
|
||||
declare var JitsiMeetJS: Object;
|
||||
declare var APP: Object;
|
||||
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { getDialOutStatusUrl, getDialOutUrl, updateConfig } from '../base/config';
|
||||
import { isIosMobileBrowser } from '../base/environment/utils';
|
||||
import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions';
|
||||
import { createLocalTrack } from '../base/lib-jitsi-meet';
|
||||
import { isVideoMutedByUser, MEDIA_TYPE } from '../base/media';
|
||||
import { updateSettings } from '../base/settings';
|
||||
import { isVideoMutedByUser } from '../base/media';
|
||||
import {
|
||||
createLocalTracksF,
|
||||
getLocalAudioTrack,
|
||||
getLocalTracks,
|
||||
getLocalVideoTrack,
|
||||
trackAdded,
|
||||
replaceLocalTrack
|
||||
} from '../base/tracks';
|
||||
import { createLocalTracksF } from '../base/tracks/functions';
|
||||
import { openURLInBrowser } from '../base/util';
|
||||
import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
|
||||
import { showErrorNotification } from '../notifications';
|
||||
|
||||
import {
|
||||
PREJOIN_JOINING_IN_PROGRESS,
|
||||
PREJOIN_INITIALIZED,
|
||||
PREJOIN_START_CONFERENCE,
|
||||
SET_DEVICE_STATUS,
|
||||
SET_DIALOUT_COUNTRY,
|
||||
SET_DIALOUT_NUMBER,
|
||||
SET_DIALOUT_STATUS,
|
||||
@@ -34,10 +31,9 @@ import {
|
||||
SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
|
||||
SET_PRECALL_TEST_RESULTS,
|
||||
SET_PREJOIN_DEVICE_ERRORS,
|
||||
SET_PREJOIN_PAGE_VISIBILITY,
|
||||
SET_DEVICE_STATUS
|
||||
SET_PREJOIN_PAGE_VISIBILITY
|
||||
} from './actionTypes';
|
||||
import { type PREJOIN_SCREEN_STATE, PREJOIN_SCREEN_STATES } from './constants';
|
||||
import { type PREJOIN_SCREEN_STATE } from './constants';
|
||||
import {
|
||||
getFullDialOutNumber,
|
||||
getDialOutConferenceUrl,
|
||||
@@ -213,74 +209,15 @@ export function initPrejoin(tracks: Object[], errors: Object) {
|
||||
* Action used to start the conference.
|
||||
*
|
||||
* @param {Object} options - The config options that override the default ones (if any).
|
||||
* @param {boolean} ignoreJoiningInProgress - If true we won't check the joiningInProgress flag.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function joinConference(options?: Object, ignoreJoiningInProgress: boolean = false) {
|
||||
return async function(dispatch: Function, getState: Function) {
|
||||
if (!ignoreJoiningInProgress) {
|
||||
const state = getState();
|
||||
const { joiningInProgress } = state['features/prejoin'];
|
||||
|
||||
if (joiningInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(setJoiningInProgress(true));
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const { userSelectedSkipPrejoin } = state['features/prejoin'];
|
||||
let localTracks = getLocalTracks(state['features/base/tracks']);
|
||||
|
||||
options && dispatch(updateConfig(options));
|
||||
|
||||
userSelectedSkipPrejoin && dispatch(updateSettings({
|
||||
userSelectedSkipPrejoin
|
||||
}));
|
||||
|
||||
// Do not signal audio/video tracks if the user joins muted.
|
||||
for (const track of localTracks) {
|
||||
// Always add the audio track on mobile Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted.
|
||||
if (track.muted
|
||||
&& !(isIosMobileBrowser() && track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) {
|
||||
try {
|
||||
await dispatch(replaceLocalTrack(track.jitsiTrack, null));
|
||||
} catch (error) {
|
||||
logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-fetch the local tracks after muted tracks have been removed above.
|
||||
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should not be used
|
||||
// anymore.
|
||||
localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||
|
||||
const jitsiTracks = localTracks.map(t => t.jitsiTrack);
|
||||
|
||||
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING));
|
||||
|
||||
APP.conference.prejoinStart(jitsiTracks);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Action used to set the flag for joining operation in progress.
|
||||
*
|
||||
* @param {boolean} value - The config options that override the default ones (if any).
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setJoiningInProgress(value: boolean) {
|
||||
export function joinConference(options?: Object) {
|
||||
return {
|
||||
type: PREJOIN_JOINING_IN_PROGRESS,
|
||||
value
|
||||
type: PREJOIN_START_CONFERENCE,
|
||||
options
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Joins the conference without audio.
|
||||
*
|
||||
@@ -288,28 +225,16 @@ export function setJoiningInProgress(value: boolean) {
|
||||
*/
|
||||
export function joinConferenceWithoutAudio() {
|
||||
return async function(dispatch: Function, getState: Function) {
|
||||
const state = getState();
|
||||
const { joiningInProgress } = state['features/prejoin'];
|
||||
|
||||
if (joiningInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(setJoiningInProgress(true));
|
||||
const tracks = state['features/base/tracks'];
|
||||
const tracks = getState()['features/base/tracks'];
|
||||
const audioTrack = getLocalAudioTrack(tracks)?.jitsiTrack;
|
||||
|
||||
if (audioTrack) {
|
||||
try {
|
||||
await dispatch(replaceLocalTrack(audioTrack, null));
|
||||
} catch (error) {
|
||||
logger.error(`Failed to replace local audio with null: ${error}`);
|
||||
}
|
||||
await dispatch(replaceLocalTrack(audioTrack, null));
|
||||
}
|
||||
|
||||
dispatch(joinConference({
|
||||
startSilent: true
|
||||
}, true));
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
// @flow
|
||||
|
||||
import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference';
|
||||
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
|
||||
import { CONFERENCE_JOINED } from '../base/conference';
|
||||
import { updateConfig } from '../base/config';
|
||||
import { isIosMobileBrowser } from '../base/environment/utils';
|
||||
import { MEDIA_TYPE, SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { updateSettings } from '../base/settings';
|
||||
import {
|
||||
getLocalTracks,
|
||||
replaceLocalTrack,
|
||||
TRACK_ADDED,
|
||||
TRACK_NO_DATA_FROM_SOURCE
|
||||
} from '../base/tracks';
|
||||
|
||||
import { PREJOIN_START_CONFERENCE } from './actionTypes';
|
||||
import {
|
||||
setDeviceStatusOk,
|
||||
setDeviceStatusWarning,
|
||||
setJoiningInProgress,
|
||||
setPrejoinPageVisibility
|
||||
} from './actions';
|
||||
import { PREJOIN_SCREEN_STATES } from './constants';
|
||||
@@ -28,6 +32,43 @@ declare var APP: Object;
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => async action => {
|
||||
switch (action.type) {
|
||||
case PREJOIN_START_CONFERENCE: {
|
||||
const { getState, dispatch } = store;
|
||||
const state = getState();
|
||||
const { userSelectedSkipPrejoin } = state['features/prejoin'];
|
||||
let localTracks = getLocalTracks(state['features/base/tracks']);
|
||||
const { options } = action;
|
||||
|
||||
options && store.dispatch(updateConfig(options));
|
||||
|
||||
userSelectedSkipPrejoin && dispatch(updateSettings({
|
||||
userSelectedSkipPrejoin
|
||||
}));
|
||||
|
||||
// Do not signal audio/video tracks if the user joins muted.
|
||||
for (const track of localTracks) {
|
||||
// Always add the audio track on mobile Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted.
|
||||
if (track.muted
|
||||
&& !(isIosMobileBrowser() && track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) {
|
||||
await dispatch(replaceLocalTrack(track.jitsiTrack, null));
|
||||
}
|
||||
}
|
||||
|
||||
// Re-fetch the local tracks after muted tracks have been removed above.
|
||||
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should not be used
|
||||
// anymore.
|
||||
localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||
|
||||
const jitsiTracks = localTracks.map(t => t.jitsiTrack);
|
||||
|
||||
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING));
|
||||
|
||||
APP.conference.prejoinStart(jitsiTracks);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_AUDIO_MUTED: {
|
||||
if (isPrejoinPageVisible(store.getState())) {
|
||||
store.dispatch(updateSettings({
|
||||
@@ -69,9 +110,6 @@ MiddlewareRegistry.register(store => next => async action => {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONFERENCE_FAILED:
|
||||
store.dispatch(setJoiningInProgress(false));
|
||||
break;
|
||||
case CONFERENCE_JOINED:
|
||||
return _conferenceJoined(store, next, action);
|
||||
}
|
||||
@@ -89,7 +127,6 @@ MiddlewareRegistry.register(store => next => async action => {
|
||||
*/
|
||||
function _conferenceJoined({ dispatch }, next, action) {
|
||||
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
|
||||
dispatch(setJoiningInProgress(false));
|
||||
|
||||
return next(action);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
PREJOIN_JOINING_IN_PROGRESS,
|
||||
SET_DEVICE_STATUS,
|
||||
SET_DIALOUT_COUNTRY,
|
||||
SET_DIALOUT_NUMBER,
|
||||
@@ -54,11 +53,7 @@ PersistenceRegistry.register(STORE_NAME, {
|
||||
ReducerRegistry.register(
|
||||
'features/prejoin', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case PREJOIN_JOINING_IN_PROGRESS:
|
||||
return {
|
||||
...state,
|
||||
joiningInProgress: action.value
|
||||
};
|
||||
|
||||
case SET_SKIP_PREJOIN: {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -55,6 +55,7 @@ export async function sendReactionsWebhook(state: Object, reactions: Array<?stri
|
||||
const { webhookProxyUrl: url } = state['features/base/config'];
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
const headers = {
|
||||
@@ -64,7 +65,7 @@ export async function sendReactionsWebhook(state: Object, reactions: Array<?stri
|
||||
|
||||
|
||||
const reqBody = {
|
||||
meetingFqn: extractFqnFromPath(),
|
||||
meetingFqn: extractFqnFromPath(locationURL.pathname),
|
||||
sessionId: conference.sessionId,
|
||||
submitted: Date.now(),
|
||||
reactions,
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
/**
|
||||
* Starts a search by criteria.
|
||||
*
|
||||
* @param {string | null} criteria - The search criteria.
|
||||
* @param {string} criteria - The search criteria.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function initSearch(criteria: string | null) {
|
||||
export function initSearch(criteria: string) {
|
||||
return {
|
||||
type: INIT_SEARCH,
|
||||
criteria
|
||||
|
||||
@@ -36,7 +36,7 @@ type Props = {
|
||||
/**
|
||||
* The search criteria.
|
||||
*/
|
||||
_criteria: string | null,
|
||||
_criteria: string,
|
||||
|
||||
/**
|
||||
* The JitsiConference from which stats will be pulled.
|
||||
@@ -140,9 +140,24 @@ class SpeakerStats extends Component<Props> {
|
||||
const dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime();
|
||||
const hasLeft = statsModel.hasLeft();
|
||||
|
||||
let displayName;
|
||||
|
||||
if (statsModel.isLocalStats()) {
|
||||
const { t } = this.props;
|
||||
const meString = t('me');
|
||||
|
||||
displayName = this.props._localDisplayName;
|
||||
displayName
|
||||
= displayName ? `${displayName} (${meString})` : meString;
|
||||
} else {
|
||||
displayName
|
||||
= this.props._stats[userId].getDisplayName()
|
||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
return (
|
||||
<SpeakerStatsItem
|
||||
displayName = { statsModel.getDisplayName() }
|
||||
displayName = { displayName }
|
||||
dominantSpeakerTime = { dominantSpeakerTime }
|
||||
hasLeft = { hasLeft }
|
||||
isDominantSpeaker = { isDominantSpeaker }
|
||||
@@ -172,40 +187,7 @@ class SpeakerStats extends Component<Props> {
|
||||
* @private
|
||||
*/
|
||||
_updateStats() {
|
||||
this.props.dispatch(initUpdateStats(() => this._getSpeakerStats()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the internal state with the latest speaker stats.
|
||||
*
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
_getSpeakerStats() {
|
||||
const stats = { ...this.props.conference.getSpeakerStats() };
|
||||
|
||||
for (const userId in stats) {
|
||||
if (stats[userId]) {
|
||||
if (stats[userId].isLocalStats()) {
|
||||
const { t } = this.props;
|
||||
const meString = t('me');
|
||||
|
||||
stats[userId].setDisplayName(
|
||||
this.props._localDisplayName
|
||||
? `${this.props._localDisplayName} (${meString})`
|
||||
: meString
|
||||
);
|
||||
}
|
||||
|
||||
if (!stats[userId].getDisplayName()) {
|
||||
stats[userId].setDisplayName(
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
this.props.dispatch(initUpdateStats(() => this.props.conference.getSpeakerStats()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getParticipantById, PARTICIPANT_ROLE } from '../base/participants';
|
||||
import { getLocalParticipant, getParticipantById, PARTICIPANT_ROLE } from '../base/participants';
|
||||
import { objectSort } from '../base/util';
|
||||
|
||||
/**
|
||||
@@ -43,10 +43,10 @@ export function getSpeakerStats(state: Object) {
|
||||
* Gets speaker stats search criteria.
|
||||
*
|
||||
* @param {*} state - The redux state.
|
||||
* @returns {string | null} - The search criteria.
|
||||
* @returns {string} - The search criteria.
|
||||
*/
|
||||
export function getSearchCriteria(state: Object) {
|
||||
return state['features/speaker-stats']?.criteria;
|
||||
return state['features/speaker-stats']?.criteria ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +129,14 @@ function getEnhancedStatsForOrdering(state, stats, orderConfig) {
|
||||
|
||||
for (const id in stats) {
|
||||
if (stats[id].hasOwnProperty('_hasLeft') && !stats[id].hasLeft()) {
|
||||
if (orderConfig.includes('name')) {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
if (stats[id].isLocalStats()) {
|
||||
stats[id].setDisplayName(localParticipant.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (orderConfig.includes('role')) {
|
||||
const participant = getParticipantById(state, stats[id].getUserId());
|
||||
|
||||
@@ -153,14 +161,16 @@ export function filterBySearchCriteria(state: Object, stats: ?Object) {
|
||||
const filteredStats = _.cloneDeep(stats ?? getSpeakerStats(state));
|
||||
const criteria = getSearchCriteria(state);
|
||||
|
||||
if (criteria !== null) {
|
||||
if (criteria) {
|
||||
const searchRegex = new RegExp(criteria, 'gi');
|
||||
|
||||
for (const id in filteredStats) {
|
||||
if (filteredStats[id].hasOwnProperty('_isLocalStats')) {
|
||||
const name = filteredStats[id].getDisplayName();
|
||||
|
||||
filteredStats[id].hidden = !name || !name.match(searchRegex);
|
||||
if (!name || !name.match(searchRegex)) {
|
||||
filteredStats[id].hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
const INITIAL_STATE = {
|
||||
stats: {},
|
||||
pendingReorder: true,
|
||||
criteria: null
|
||||
criteria: ''
|
||||
};
|
||||
|
||||
ReducerRegistry.register('features/speaker-stats', (state = _getInitialState(), action) => {
|
||||
@@ -96,7 +96,7 @@ function _updateStats(state, { stats }) {
|
||||
{},
|
||||
state,
|
||||
{
|
||||
stats: { ...finalStats },
|
||||
stats: finalStats,
|
||||
pendingReorder: false
|
||||
},
|
||||
);
|
||||
|
||||
@@ -55,6 +55,16 @@ export const SET_OVERFLOW_MENU_VISIBLE = 'SET_OVERFLOW_MENU_VISIBLE';
|
||||
*/
|
||||
export const SET_TOOLBAR_HOVERED = 'SET_TOOLBAR_HOVERED';
|
||||
|
||||
/**
|
||||
* The type of the action which sets the permanent visibility of the Toolbox.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
* alwaysVisible: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_ALWAYS_VISIBLE = 'SET_TOOLBOX_ALWAYS_VISIBLE';
|
||||
|
||||
/**
|
||||
* The type of the (redux) action which enables/disables the Toolbox.
|
||||
*
|
||||
@@ -77,6 +87,17 @@ export const SET_TOOLBOX_ENABLED = 'SET_TOOLBOX_ENABLED';
|
||||
*/
|
||||
export const SET_TOOLBOX_TIMEOUT = 'SET_TOOLBOX_TIMEOUT';
|
||||
|
||||
/**
|
||||
* The type of the action which sets the delay in milliseconds after which
|
||||
* the Toolbox visibility is to be changed.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_TIMEOUT_MS,
|
||||
* timeoutMS: number
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_TIMEOUT_MS = 'SET_TOOLBOX_TIMEOUT_MS';
|
||||
|
||||
/**
|
||||
* The type of the (redux) action which shows/hides the Toolbox.
|
||||
*
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Enables/disables the toolbox.
|
||||
*
|
||||
* @param {boolean} enabled - True to enable the toolbox or false to disable it.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_ENABLED,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxEnabled(enabled: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_ENABLED,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows/hides the toolbox.
|
||||
*
|
||||
* @param {boolean} visible - True to show the toolbox or false to hide it.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setToolboxVisible(visible: boolean): Object {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const { toolbarConfig: { alwaysVisible } } = getState()['features/base/config'];
|
||||
|
||||
if (!visible && alwaysVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: SET_TOOLBOX_VISIBLE,
|
||||
visible
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,28 +1,160 @@
|
||||
// @flow
|
||||
/* @flow */
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
import {
|
||||
CLEAR_TOOLBOX_TIMEOUT,
|
||||
SET_OVERFLOW_MENU_VISIBLE,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_TOOLBOX_TIMEOUT_MS,
|
||||
SET_TOOLBOX_VISIBLE,
|
||||
TOGGLE_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
import { TOGGLE_TOOLBOX_VISIBLE } from './actionTypes';
|
||||
|
||||
export * from './actions.any';
|
||||
/**
|
||||
* Signals that toolbox timeout should be cleared.
|
||||
*
|
||||
* @returns {{
|
||||
* type: CLEAR_TOOLBOX_TIMEOUT
|
||||
* }}
|
||||
*/
|
||||
export function clearToolboxTimeout(): Object {
|
||||
return {
|
||||
type: CLEAR_TOOLBOX_TIMEOUT
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows/hides the overflow menu.
|
||||
*
|
||||
* @param {boolean} visible - True to show it or false to hide it.
|
||||
* @returns {{
|
||||
* type: SET_OVERFLOW_MENU_VISIBLE,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setOverflowMenuVisible(visible: boolean): Object {
|
||||
return {
|
||||
type: SET_OVERFLOW_MENU_VISIBLE,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that toolbar is hovered value should be changed.
|
||||
*
|
||||
* @param {boolean} hovered - Flag showing whether toolbar is hovered.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBAR_HOVERED,
|
||||
* hovered: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolbarHovered(hovered: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBAR_HOVERED,
|
||||
hovered
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that always visible toolbars value should be changed.
|
||||
*
|
||||
* @param {boolean} alwaysVisible - Value to be set in redux store.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
* alwaysVisible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxAlwaysVisible(alwaysVisible: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
alwaysVisible
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-disable flowtype/space-before-type-colon */
|
||||
|
||||
/**
|
||||
* Enables/disables the toolbox.
|
||||
*
|
||||
* @param {boolean} enabled - True to enable the toolbox or false to disable it.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_ENABLED,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxEnabled(enabled: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_ENABLED,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action which sets new timeout and clears the previous one.
|
||||
*
|
||||
* @param {Function} handler - Function to be invoked after the timeout.
|
||||
* @param {number} timeoutMS - Delay.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_TIMEOUT,
|
||||
* handler: Function,
|
||||
* timeoutMS: number
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxTimeout(handler: Function, timeoutMS: number)
|
||||
: Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_TIMEOUT,
|
||||
handler,
|
||||
timeoutMS
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-enable flowtype/space-before-type-colon */
|
||||
|
||||
/**
|
||||
* Dispatches an action which sets new toolbox timeout value.
|
||||
*
|
||||
* @param {number} timeoutMS - Delay.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_TIMEOUT_MS,
|
||||
* timeoutMS: number
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxTimeoutMS(timeoutMS: number): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_TIMEOUT_MS,
|
||||
timeoutMS
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows/hides the toolbox.
|
||||
*
|
||||
* @param {boolean} visible - True to show the toolbox or false to hide it.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_VISIBLE,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxVisible(visible: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_VISIBLE,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to toggle the toolbox visibility.
|
||||
*
|
||||
* @returns {Function}
|
||||
* @returns {{
|
||||
* type: TOGGLE_TOOLBOX_VISIBLE
|
||||
* }}
|
||||
*/
|
||||
export function toggleToolboxVisible() {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
|
||||
const { visible } = state['features/toolbox'];
|
||||
|
||||
if (visible && alwaysVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: TOGGLE_TOOLBOX_VISIBLE
|
||||
});
|
||||
return {
|
||||
type: TOGGLE_TOOLBOX_VISIBLE
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { overwriteConfig } from '../base/config';
|
||||
import { isLayoutTileView } from '../video-layout';
|
||||
|
||||
import {
|
||||
CLEAR_TOOLBOX_TIMEOUT,
|
||||
FULL_SCREEN_CHANGED,
|
||||
SET_FULL_SCREEN,
|
||||
SET_OVERFLOW_DRAWER,
|
||||
SET_OVERFLOW_MENU_VISIBLE,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_TIMEOUT
|
||||
SET_OVERFLOW_DRAWER
|
||||
} from './actionTypes';
|
||||
import { setToolboxVisible } from './actions';
|
||||
import { getToolbarTimeout } from './functions';
|
||||
import {
|
||||
clearToolboxTimeout,
|
||||
setToolboxTimeout,
|
||||
setToolboxTimeoutMS,
|
||||
setToolboxVisible
|
||||
} from './actions.native';
|
||||
|
||||
export * from './actions.any';
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
export * from './actions.native';
|
||||
|
||||
/**
|
||||
* Docks/undocks the Toolbox.
|
||||
@@ -27,9 +28,7 @@ export * from './actions.any';
|
||||
*/
|
||||
export function dockToolbox(dock: boolean): Function {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { visible } = state['features/toolbox'];
|
||||
const toolbarTimeout = getToolbarTimeout(state);
|
||||
const { timeoutMS, visible } = getState()['features/toolbox'];
|
||||
|
||||
if (dock) {
|
||||
// First make sure the toolbox is shown.
|
||||
@@ -40,7 +39,7 @@ export function dockToolbox(dock: boolean): Function {
|
||||
dispatch(
|
||||
setToolboxTimeout(
|
||||
() => dispatch(hideToolbox()),
|
||||
toolbarTimeout));
|
||||
timeoutMS));
|
||||
} else {
|
||||
dispatch(showToolbox());
|
||||
}
|
||||
@@ -74,9 +73,11 @@ export function fullScreenChanged(fullScreen: boolean) {
|
||||
export function hideToolbox(force: boolean = false): Function {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
|
||||
const { hovered } = state['features/toolbox'];
|
||||
const toolbarTimeout = getToolbarTimeout(state);
|
||||
const {
|
||||
alwaysVisible,
|
||||
hovered,
|
||||
timeoutMS
|
||||
} = state['features/toolbox'];
|
||||
|
||||
if (alwaysVisible) {
|
||||
return;
|
||||
@@ -94,7 +95,7 @@ export function hideToolbox(force: boolean = false): Function {
|
||||
dispatch(
|
||||
setToolboxTimeout(
|
||||
() => dispatch(hideToolbox()),
|
||||
toolbarTimeout));
|
||||
timeoutMS));
|
||||
} else {
|
||||
dispatch(setToolboxVisible(false));
|
||||
}
|
||||
@@ -127,13 +128,9 @@ export function showToolbox(timeout: number = 0): Object {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const {
|
||||
toolbarConfig: { initialTimeout, alwaysVisible },
|
||||
toolbarConfig
|
||||
} = state['features/base/config'];
|
||||
const toolbarTimeout = getToolbarTimeout(state);
|
||||
|
||||
const {
|
||||
alwaysVisible,
|
||||
enabled,
|
||||
timeoutMS,
|
||||
visible,
|
||||
overflowDrawer
|
||||
} = state['features/toolbox'];
|
||||
@@ -146,17 +143,11 @@ export function showToolbox(timeout: number = 0): Object {
|
||||
// If the Toolbox is always visible, there's no need for a timeout
|
||||
// to toggle its visibility.
|
||||
if (!alwaysVisible) {
|
||||
if (typeof initialTimeout === 'number') {
|
||||
// reset `initialTimeout` once it is consumed once
|
||||
dispatch(overwriteConfig({ toolbarConfig: {
|
||||
...toolbarConfig,
|
||||
initialTimeout: null
|
||||
} }));
|
||||
}
|
||||
dispatch(
|
||||
setToolboxTimeout(
|
||||
() => dispatch(hideToolbox()),
|
||||
timeout || initialTimeout || toolbarTimeout));
|
||||
timeout || timeoutMS));
|
||||
dispatch(setToolboxTimeoutMS(interfaceConfig.TOOLBAR_TIMEOUT));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -189,73 +180,9 @@ export function hideToolboxOnTileView() {
|
||||
const state = getState();
|
||||
const { overflowDrawer } = state['features/toolbox'];
|
||||
|
||||
|
||||
if (!overflowDrawer && isLayoutTileView(state)) {
|
||||
dispatch(hideToolbox(true));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that toolbox timeout should be cleared.
|
||||
*
|
||||
* @returns {{
|
||||
* type: CLEAR_TOOLBOX_TIMEOUT
|
||||
* }}
|
||||
*/
|
||||
export function clearToolboxTimeout(): Object {
|
||||
return {
|
||||
type: CLEAR_TOOLBOX_TIMEOUT
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows/hides the overflow menu.
|
||||
*
|
||||
* @param {boolean} visible - True to show it or false to hide it.
|
||||
* @returns {{
|
||||
* type: SET_OVERFLOW_MENU_VISIBLE,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setOverflowMenuVisible(visible: boolean): Object {
|
||||
return {
|
||||
type: SET_OVERFLOW_MENU_VISIBLE,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that toolbar is hovered value should be changed.
|
||||
*
|
||||
* @param {boolean} hovered - Flag showing whether toolbar is hovered.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBAR_HOVERED,
|
||||
* hovered: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolbarHovered(hovered: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBAR_HOVERED,
|
||||
hovered
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action which sets new timeout and clears the previous one.
|
||||
*
|
||||
* @param {Function} handler - Function to be invoked after the timeout.
|
||||
* @param {number} timeoutMS - Delay.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_TIMEOUT,
|
||||
* handler: Function,
|
||||
* timeoutMS: number
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxTimeout(handler: Function, timeoutMS: number): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_TIMEOUT,
|
||||
handler,
|
||||
timeoutMS
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -29,5 +29,3 @@ export const THRESHOLDS = [
|
||||
];
|
||||
|
||||
export const NOT_APPLICABLE = 'N/A';
|
||||
|
||||
export const TOOLBAR_TIMEOUT = 4000;
|
||||
|
||||
@@ -60,15 +60,12 @@ export function getMovableButtons(width: number): Set<string> {
|
||||
*/
|
||||
export function isToolboxVisible(stateful: Object | Function) {
|
||||
const state = toState(stateful);
|
||||
const { toolbarConfig } = state['features/base/config'];
|
||||
const { alwaysVisible } = toolbarConfig || {};
|
||||
const { enabled, visible } = state['features/toolbox'];
|
||||
const { alwaysVisible, enabled, visible } = state['features/toolbox'];
|
||||
const participantCount = getParticipantCountWithFake(state);
|
||||
const alwaysVisibleFlag = getFeatureFlag(state, TOOLBOX_ALWAYS_VISIBLE, false);
|
||||
const enabledFlag = getFeatureFlag(state, TOOLBOX_ENABLED, true);
|
||||
|
||||
return enabledFlag && enabled
|
||||
&& (alwaysVisible || visible || participantCount === 1 || alwaysVisibleFlag);
|
||||
return enabledFlag && enabled && (alwaysVisible || visible || participantCount === 1 || alwaysVisibleFlag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
import { getToolbarButtons } from '../base/config';
|
||||
import { hasAvailableDevices } from '../base/devices';
|
||||
|
||||
import { TOOLBAR_TIMEOUT } from './constants';
|
||||
|
||||
/**
|
||||
* Helper for getting the height of the toolbox.
|
||||
*
|
||||
@@ -39,9 +37,9 @@ export function isButtonEnabled(name: string, state: Object) {
|
||||
* otherwise.
|
||||
*/
|
||||
export function isToolboxVisible(state: Object) {
|
||||
const { iAmSipGateway, toolbarConfig } = state['features/base/config'];
|
||||
const { alwaysVisible } = toolbarConfig || {};
|
||||
const { iAmSipGateway } = state['features/base/config'];
|
||||
const {
|
||||
alwaysVisible,
|
||||
timeoutID,
|
||||
visible
|
||||
} = state['features/toolbox'];
|
||||
@@ -103,15 +101,3 @@ export function showOverflowDrawer(state: Object) {
|
||||
export function isToolboxEnabled(state: Object) {
|
||||
return state['features/toolbox'].enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the toolbar timeout from config or the default value.
|
||||
*
|
||||
* @param {Object} state - The state from the Redux store.
|
||||
* @returns {number} - Toolbar timeout in miliseconds.
|
||||
*/
|
||||
export function getToolbarTimeout(state: Object) {
|
||||
const { toolbarConfig: { timeout } } = state['features/base/config'];
|
||||
|
||||
return timeout || TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
@@ -8,66 +8,121 @@ import {
|
||||
SET_OVERFLOW_DRAWER,
|
||||
SET_OVERFLOW_MENU_VISIBLE,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_TOOLBOX_TIMEOUT_MS,
|
||||
SET_TOOLBOX_VISIBLE,
|
||||
TOGGLE_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* Initial state of toolbox's part of Redux store.
|
||||
* Returns initial state for toolbox's part of Redux store.
|
||||
*
|
||||
* @private
|
||||
* @returns {{
|
||||
* alwaysVisible: boolean,
|
||||
* enabled: boolean,
|
||||
* hovered: boolean,
|
||||
* overflowDrawer: boolean,
|
||||
* overflowMenuVisible: boolean,
|
||||
* timeoutID: number,
|
||||
* timeoutMS: number,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
const INITIAL_STATE = {
|
||||
function _getInitialState() {
|
||||
// Does the toolbar eventually fade out, or is it always visible?
|
||||
let alwaysVisible = false;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled: true,
|
||||
// Toolbar (initial) visibility.
|
||||
let visible = false;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether a Toolbar in the Toolbox is
|
||||
* hovered.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
hovered: false,
|
||||
// Default toolbox timeout for mobile app.
|
||||
let timeoutMS = 5000;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the overflow menu(s) are to be displayed as drawers.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowDrawer: false,
|
||||
if (typeof interfaceConfig !== 'undefined') {
|
||||
if (interfaceConfig.INITIAL_TOOLBAR_TIMEOUT) {
|
||||
timeoutMS = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
}
|
||||
if (typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE !== 'undefined') {
|
||||
alwaysVisible = interfaceConfig.TOOLBAR_ALWAYS_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the OverflowMenu is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowMenuVisible: false,
|
||||
// When the toolbar is always visible, it must initially be visible too.
|
||||
if (alwaysVisible === true) {
|
||||
visible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A number, non-zero value which identifies the timer created by a call
|
||||
* to setTimeout().
|
||||
*
|
||||
* @type {number|null}
|
||||
*/
|
||||
timeoutID: null,
|
||||
return {
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox should always be
|
||||
* visible. When false, the toolbar will fade out after timeoutMS.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
alwaysVisible,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled: true,
|
||||
|
||||
/**
|
||||
* The indicator that determines whether the Toolbox is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
visible: false
|
||||
};
|
||||
/**
|
||||
* The indicator which determines whether a Toolbar in the Toolbox is
|
||||
* hovered.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
hovered: false,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the overflow menu(s) are to be displayed as drawers.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowDrawer: false,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the OverflowMenu is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowMenuVisible: false,
|
||||
|
||||
/**
|
||||
* A number, non-zero value which identifies the timer created by a call
|
||||
* to setTimeout() with timeoutMS.
|
||||
*
|
||||
* @type {number|null}
|
||||
*/
|
||||
timeoutID: null,
|
||||
|
||||
/**
|
||||
* The delay in milliseconds before timeoutID executes (after its
|
||||
* initialization).
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
timeoutMS,
|
||||
|
||||
/**
|
||||
* The indicator that determines whether the Toolbox is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
ReducerRegistry.register(
|
||||
'features/toolbox',
|
||||
(state: Object = INITIAL_STATE, action: Object) => {
|
||||
(state: Object = _getInitialState(), action: Object) => {
|
||||
switch (action.type) {
|
||||
case CLEAR_TOOLBOX_TIMEOUT:
|
||||
return {
|
||||
@@ -99,6 +154,13 @@ ReducerRegistry.register(
|
||||
hovered: action.hovered
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_ALWAYS_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
alwaysVisible: action.alwaysVisible,
|
||||
visible: action.alwaysVisible === true ? true : state.visible
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_ENABLED:
|
||||
return {
|
||||
...state,
|
||||
@@ -108,14 +170,21 @@ ReducerRegistry.register(
|
||||
case SET_TOOLBOX_TIMEOUT:
|
||||
return {
|
||||
...state,
|
||||
timeoutID: action.timeoutID
|
||||
timeoutID: action.timeoutID,
|
||||
timeoutMS: action.timeoutMS
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_TIMEOUT_MS:
|
||||
return {
|
||||
...state,
|
||||
timeoutMS: action.timeoutMS
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_VISIBLE:
|
||||
return set(state, 'visible', action.visible);
|
||||
return set(state, 'visible', state.alwaysVisible || action.visible);
|
||||
|
||||
case TOGGLE_TOOLBOX_VISIBLE:
|
||||
return set(state, 'visible', !state.visible);
|
||||
return set(state, 'visible', state.alwaysVisible || !state.visible);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
sendAnalytics,
|
||||
VIDEO_MUTE
|
||||
} from '../analytics';
|
||||
import { rejectParticipantAudio, rejectParticipantVideo, showModeratedNotification } from '../av-moderation/actions';
|
||||
import { showModeratedNotification } from '../av-moderation/actions';
|
||||
import { shouldShowModeratedNotification } from '../av-moderation/functions';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
getRemoteParticipants,
|
||||
muteRemoteParticipant
|
||||
} from '../base/participants';
|
||||
import { toggleScreensharing } from '../base/tracks';
|
||||
import { isModerationNotificationDisplayed } from '../notifications';
|
||||
|
||||
declare var APP: Object;
|
||||
@@ -35,10 +34,9 @@ const logger = getLogger(__filename);
|
||||
*
|
||||
* @param {boolean} enable - Whether to mute or unmute.
|
||||
* @param {MEDIA_TYPE} mediaType - The type of the media channel to mute.
|
||||
* @param {boolean} stopScreenSharing - Whether or not to stop the screensharing.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function muteLocal(enable: boolean, mediaType: MEDIA_TYPE, stopScreenSharing: boolean = false) {
|
||||
export function muteLocal(enable: boolean, mediaType: MEDIA_TYPE) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const isAudio = mediaType === MEDIA_TYPE.AUDIO;
|
||||
|
||||
@@ -57,10 +55,6 @@ export function muteLocal(enable: boolean, mediaType: MEDIA_TYPE, stopScreenShar
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable && stopScreenSharing) {
|
||||
dispatch(toggleScreensharing(false, false, true));
|
||||
}
|
||||
|
||||
sendAnalytics(createToolbarEvent(isAudio ? AUDIO_MUTE : VIDEO_MUTE, { enable }));
|
||||
dispatch(isAudio ? setAudioMuted(enable, /* ensureTrack */ true)
|
||||
: setVideoMuted(enable, mediaType, VIDEO_MUTISM_AUTHORITY.USER, /* ensureTrack */ true));
|
||||
@@ -103,7 +97,7 @@ export function muteAllParticipants(exclude: Array<string>, mediaType: MEDIA_TYP
|
||||
const localId = getLocalParticipant(state).id;
|
||||
|
||||
if (!exclude.includes(localId)) {
|
||||
dispatch(muteLocal(true, mediaType, true));
|
||||
dispatch(muteLocal(true, mediaType));
|
||||
}
|
||||
|
||||
getRemoteParticipants(state).forEach((p, id) => {
|
||||
@@ -112,11 +106,6 @@ export function muteAllParticipants(exclude: Array<string>, mediaType: MEDIA_TYP
|
||||
}
|
||||
|
||||
dispatch(muteRemote(id, mediaType));
|
||||
if (mediaType === MEDIA_TYPE.AUDIO) {
|
||||
dispatch(rejectParticipantAudio(id));
|
||||
} else {
|
||||
dispatch(rejectParticipantVideo(id));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { requestDisableAudioModeration, requestEnableAudioModeration } from '../../av-moderation/actions';
|
||||
import { isEnabledFromState, isSupported } from '../../av-moderation/functions';
|
||||
import { isEnabledFromState } from '../../av-moderation/functions';
|
||||
import { Dialog } from '../../base/dialog';
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { getLocalParticipant, getParticipantDisplayName } from '../../base/participants';
|
||||
@@ -23,8 +23,7 @@ export type Props = AbstractProps & {
|
||||
exclude: Array<string>,
|
||||
title: string,
|
||||
showAdvancedModerationToggle: boolean,
|
||||
isAudioModerationEnabled: boolean,
|
||||
isModerationSupported: boolean
|
||||
isAudioModerationEnabled: boolean
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -106,7 +105,7 @@ export default class AbstractMuteEveryoneDialog<P: Props> extends AbstractMuteRe
|
||||
dispatch(muteAllParticipants(exclude, MEDIA_TYPE.AUDIO));
|
||||
if (this.state.audioModerationEnabled) {
|
||||
dispatch(requestEnableAudioModeration());
|
||||
} else if (this.state.audioModerationEnabled !== undefined) {
|
||||
} else {
|
||||
dispatch(requestDisableAudioModeration());
|
||||
}
|
||||
|
||||
@@ -136,7 +135,6 @@ export function abstractMapStateToProps(state: Object, ownProps: Props) {
|
||||
title: t('dialog.muteEveryoneElseTitle', { whom })
|
||||
} : {
|
||||
title: t('dialog.muteEveryoneTitle'),
|
||||
isAudioModerationEnabled: isEnabledFromState(MEDIA_TYPE.AUDIO, state),
|
||||
isModerationSupported: isSupported()(state)
|
||||
isAudioModerationEnabled: isEnabledFromState(MEDIA_TYPE.AUDIO, state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { requestDisableVideoModeration, requestEnableVideoModeration } from '../../av-moderation/actions';
|
||||
import { isEnabledFromState, isSupported } from '../../av-moderation/functions';
|
||||
import { isEnabledFromState } from '../../av-moderation/functions';
|
||||
import { Dialog } from '../../base/dialog';
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { getLocalParticipant, getParticipantDisplayName } from '../../base/participants';
|
||||
@@ -23,8 +23,7 @@ export type Props = AbstractProps & {
|
||||
exclude: Array<string>,
|
||||
title: string,
|
||||
showAdvancedModerationToggle: boolean,
|
||||
isVideoModerationEnabled: boolean,
|
||||
isModerationSupported: boolean
|
||||
isVideoModerationEnabled: boolean
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -107,7 +106,7 @@ export default class AbstractMuteEveryonesVideoDialog<P: Props>
|
||||
dispatch(muteAllParticipants(exclude, MEDIA_TYPE.VIDEO));
|
||||
if (this.state.moderationEnabled) {
|
||||
dispatch(requestEnableVideoModeration());
|
||||
} else if (this.state.moderationEnabled !== undefined) {
|
||||
} else {
|
||||
dispatch(requestDisableVideoModeration());
|
||||
}
|
||||
|
||||
@@ -138,7 +137,6 @@ export function abstractMapStateToProps(state: Object, ownProps: Props) {
|
||||
title: t('dialog.muteEveryoneElsesVideoTitle', { whom })
|
||||
} : {
|
||||
title: t('dialog.muteEveryonesVideoTitle'),
|
||||
isVideoModerationEnabled,
|
||||
isModerationSupported: isSupported()(state)
|
||||
isVideoModerationEnabled
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { Component } from 'react';
|
||||
|
||||
import { rejectParticipantVideo } from '../../av-moderation/actions';
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { muteRemote } from '../actions';
|
||||
|
||||
@@ -60,7 +59,6 @@ export default class AbstractMuteRemoteParticipantsVideoDialog<P:Props = Props,
|
||||
const { dispatch, participantID } = this.props;
|
||||
|
||||
dispatch(muteRemote(participantID, MEDIA_TYPE.VIDEO));
|
||||
dispatch(rejectParticipantVideo(participantID));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class MuteEveryoneDialog extends AbstractMuteEveryoneDialog<Props> {
|
||||
width = 'small'>
|
||||
<div className = 'mute-dialog'>
|
||||
{ this.state.content }
|
||||
{ this.props.isModerationSupported && this.props.exclude.length === 0 && (
|
||||
{this.props.exclude.length === 0 && (
|
||||
<>
|
||||
<div className = 'separator-line' />
|
||||
<div className = 'control-row'>
|
||||
|
||||
@@ -48,7 +48,7 @@ class MuteEveryonesVideoDialog extends AbstractMuteEveryonesVideoDialog<Props> {
|
||||
width = 'small'>
|
||||
<div className = 'mute-dialog'>
|
||||
{this.state.content}
|
||||
{ this.props.isModerationSupported && this.props.exclude.length === 0 && (
|
||||
{this.props.exclude.length === 0 && (
|
||||
<>
|
||||
<div className = 'separator-line' />
|
||||
<div className = 'control-row'>
|
||||
|
||||
@@ -229,6 +229,7 @@ function _updateReceiverVideoConstraints({ getState }) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Setting receiver video constraints to ${JSON.stringify(receiverConstraints)}`);
|
||||
try {
|
||||
conference.setReceiverConstraints(receiverConstraints);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Icon, IconPlusCircle } from '../../base/icons';
|
||||
import { VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants';
|
||||
import { resizeImage } from '../functions';
|
||||
import logger from '../logger';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Callback used to set the 'loading' state of the parent component.
|
||||
*/
|
||||
setLoading: Function,
|
||||
|
||||
/**
|
||||
* Callback used to set the options.
|
||||
*/
|
||||
setOptions: Function,
|
||||
|
||||
/**
|
||||
* Callback used to set the storedImages array.
|
||||
*/
|
||||
setStoredImages: Function,
|
||||
|
||||
/**
|
||||
* A list of images locally stored.
|
||||
*/
|
||||
storedImages: Array<Image>,
|
||||
|
||||
/**
|
||||
* If a label should be displayed alongside the button.
|
||||
*/
|
||||
showLabel: boolean,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used to upload an image.
|
||||
*
|
||||
* @param {Object} Props - The props of the component.
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
function UploadImageButton({
|
||||
setLoading,
|
||||
setOptions,
|
||||
setStoredImages,
|
||||
showLabel,
|
||||
storedImages,
|
||||
t
|
||||
}: Props) {
|
||||
const uploadImageButton: Object = useRef(null);
|
||||
const uploadImageKeyPress = useCallback(e => {
|
||||
if (uploadImageButton.current && (e.key === ' ' || e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
uploadImageButton.current.click();
|
||||
}
|
||||
}, [ uploadImageButton.current ]);
|
||||
|
||||
|
||||
const uploadImage = useCallback(async e => {
|
||||
const reader = new FileReader();
|
||||
const imageFile = e.target.files;
|
||||
|
||||
reader.readAsDataURL(imageFile[0]);
|
||||
reader.onload = async () => {
|
||||
const url = await resizeImage(reader.result);
|
||||
const uuId = uuid.v4();
|
||||
|
||||
setStoredImages([
|
||||
...storedImages,
|
||||
{
|
||||
id: uuId,
|
||||
src: url
|
||||
}
|
||||
]);
|
||||
setOptions({
|
||||
backgroundType: VIRTUAL_BACKGROUND_TYPE.IMAGE,
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: uuId
|
||||
});
|
||||
};
|
||||
logger.info('New virtual background image uploaded!');
|
||||
|
||||
reader.onerror = () => {
|
||||
setLoading(false);
|
||||
logger.error('Failed to upload virtual image!');
|
||||
};
|
||||
}, [ storedImages ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showLabel && <label
|
||||
aria-label = { t('virtualBackground.uploadImage') }
|
||||
className = 'file-upload-label'
|
||||
htmlFor = 'file-upload'
|
||||
onKeyPress = { uploadImageKeyPress }
|
||||
tabIndex = { 0 } >
|
||||
<Icon
|
||||
className = { 'add-background' }
|
||||
size = { 20 }
|
||||
src = { IconPlusCircle } />
|
||||
{t('virtualBackground.addBackground')}
|
||||
</label>}
|
||||
|
||||
<input
|
||||
accept = 'image/*'
|
||||
className = 'file-upload-btn'
|
||||
id = 'file-upload'
|
||||
onChange = { uploadImage }
|
||||
ref = { uploadImageButton }
|
||||
type = 'file' />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(UploadImageButton);
|
||||
@@ -3,11 +3,12 @@
|
||||
import Spinner from '@atlaskit/spinner';
|
||||
import Bourne from '@hapi/bourne';
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { Dialog, hideDialog, openDialog } from '../../base/dialog';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Icon, IconCloseSmall, IconShareDesktop } from '../../base/icons';
|
||||
import { Icon, IconCloseSmall, IconPlusCircle, IconShareDesktop } from '../../base/icons';
|
||||
import { browser, JitsiTrackErrors } from '../../base/lib-jitsi-meet';
|
||||
import { createLocalTrack } from '../../base/lib-jitsi-meet/functions';
|
||||
import { VIDEO_TYPE } from '../../base/media';
|
||||
@@ -17,19 +18,61 @@ import { Tooltip } from '../../base/tooltip';
|
||||
import { getLocalVideoTrack } from '../../base/tracks';
|
||||
import { showErrorNotification } from '../../notifications';
|
||||
import { toggleBackgroundEffect } from '../actions';
|
||||
import { IMAGES, BACKGROUNDS_LIMIT, VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants';
|
||||
import { toDataURL } from '../functions';
|
||||
import { VIRTUAL_BACKGROUND_TYPE } from '../constants';
|
||||
import { resizeImage, toDataURL } from '../functions';
|
||||
import logger from '../logger';
|
||||
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import VirtualBackgroundPreview from './VirtualBackgroundPreview';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The list of Images to choose from.
|
||||
*/
|
||||
_images: Array<Image>,
|
||||
type Image = {
|
||||
tooltip?: string,
|
||||
id: string,
|
||||
src: string
|
||||
}
|
||||
|
||||
// The limit of virtual background uploads is 24. When the number
|
||||
// of uploads is 25 we trigger the deleteStoredImage function to delete
|
||||
// the first/oldest uploaded background.
|
||||
const backgroundsLimit = 25;
|
||||
const images: Array<Image> = [
|
||||
{
|
||||
tooltip: 'image1',
|
||||
id: '1',
|
||||
src: 'images/virtual-background/background-1.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image2',
|
||||
id: '2',
|
||||
src: 'images/virtual-background/background-2.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image3',
|
||||
id: '3',
|
||||
src: 'images/virtual-background/background-3.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image4',
|
||||
id: '4',
|
||||
src: 'images/virtual-background/background-4.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image5',
|
||||
id: '5',
|
||||
src: 'images/virtual-background/background-5.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image6',
|
||||
id: '6',
|
||||
src: 'images/virtual-background/background-6.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image7',
|
||||
id: '7',
|
||||
src: 'images/virtual-background/background-7.jpg'
|
||||
}
|
||||
];
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current local flip x status.
|
||||
@@ -46,11 +89,6 @@ type Props = {
|
||||
*/
|
||||
_selectedThumbnail: string,
|
||||
|
||||
/**
|
||||
* If the upload button should be displayed or not.
|
||||
*/
|
||||
_showUploadButton: boolean,
|
||||
|
||||
/**
|
||||
* Returns the selected virtual background object.
|
||||
*/
|
||||
@@ -90,15 +128,11 @@ const onError = event => {
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { localFlipX } = state['features/base/settings'];
|
||||
const dynamicBrandingImages = state['features/dynamic-branding'].virtualBackgrounds;
|
||||
const hasBrandingImages = Boolean(dynamicBrandingImages.length);
|
||||
|
||||
return {
|
||||
_localFlipX: Boolean(localFlipX),
|
||||
_images: (hasBrandingImages && dynamicBrandingImages) || IMAGES,
|
||||
_virtualBackground: state['features/virtual-background'],
|
||||
_selectedThumbnail: state['features/virtual-background'].selectedThumbnail,
|
||||
_showUploadButton: !(hasBrandingImages || state['features/base/config'].disableAddingBackgroundImages),
|
||||
_jitsiTrack: getLocalVideoTrack(state['features/base/tracks'])?.jitsiTrack
|
||||
};
|
||||
}
|
||||
@@ -111,11 +145,9 @@ const VirtualBackgroundDialog = translate(connect(_mapStateToProps)(VirtualBackg
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function VirtualBackground({
|
||||
_images,
|
||||
_jitsiTrack,
|
||||
_localFlipX,
|
||||
_jitsiTrack,
|
||||
_selectedThumbnail,
|
||||
_showUploadButton,
|
||||
_virtualBackground,
|
||||
dispatch,
|
||||
initialOptions,
|
||||
@@ -126,7 +158,7 @@ function VirtualBackground({
|
||||
const localImages = jitsiLocalStorage.getItem('virtualBackgrounds');
|
||||
const [ storedImages, setStoredImages ] = useState<Array<Image>>((localImages && Bourne.parse(localImages)) || []);
|
||||
const [ loading, setLoading ] = useState(false);
|
||||
|
||||
const uploadImageButton: Object = useRef(null);
|
||||
const [ activeDesktopVideo ] = useState(_virtualBackground?.virtualSource?.videoType === VIDEO_TYPE.DESKTOP
|
||||
? _virtualBackground.virtualSource
|
||||
: null);
|
||||
@@ -154,7 +186,7 @@ function VirtualBackground({
|
||||
// Preventing localStorage QUOTA_EXCEEDED_ERR
|
||||
err && setStoredImages(storedImages.slice(1));
|
||||
}
|
||||
if (storedImages.length === BACKGROUNDS_LIMIT) {
|
||||
if (storedImages.length === backgroundsLimit) {
|
||||
setStoredImages(storedImages.slice(1));
|
||||
}
|
||||
}, [ storedImages ]);
|
||||
@@ -289,27 +321,61 @@ function VirtualBackground({
|
||||
|
||||
const setImageBackground = useCallback(async e => {
|
||||
const imageId = e.currentTarget.getAttribute('data-imageid');
|
||||
const image = _images.find(img => img.id === imageId);
|
||||
const image = images.find(img => img.id === imageId);
|
||||
|
||||
if (image) {
|
||||
try {
|
||||
const url = await toDataURL(image.src);
|
||||
const url = await toDataURL(image.src);
|
||||
|
||||
setOptions({
|
||||
backgroundType: 'image',
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: image.id
|
||||
});
|
||||
logger.info('Image set for virtual background preview!');
|
||||
} catch (err) {
|
||||
logger.error('Could not fetch virtual background image:', err);
|
||||
}
|
||||
setOptions({
|
||||
backgroundType: 'image',
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: image.id
|
||||
});
|
||||
logger.info('Image setted for virtual background preview!');
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const uploadImage = useCallback(async e => {
|
||||
const reader = new FileReader();
|
||||
const imageFile = e.target.files;
|
||||
|
||||
reader.readAsDataURL(imageFile[0]);
|
||||
reader.onload = async () => {
|
||||
const url = await resizeImage(reader.result);
|
||||
const uuId = uuid.v4();
|
||||
|
||||
setStoredImages([
|
||||
...storedImages,
|
||||
{
|
||||
id: uuId,
|
||||
src: url
|
||||
}
|
||||
]);
|
||||
setOptions({
|
||||
backgroundType: VIRTUAL_BACKGROUND_TYPE.IMAGE,
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: uuId
|
||||
});
|
||||
};
|
||||
logger.info('New virtual background image uploaded!');
|
||||
|
||||
reader.onerror = () => {
|
||||
setLoading(false);
|
||||
logger.error('Failed to upload virtual image!');
|
||||
};
|
||||
}, [ dispatch, storedImages ]);
|
||||
|
||||
const uploadImageKeyPress = useCallback(e => {
|
||||
if (uploadImageButton.current && (e.key === ' ' || e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
uploadImageButton.current.click();
|
||||
}
|
||||
}, [ uploadImageButton.current ]);
|
||||
|
||||
const setImageBackgroundKeyPress = useCallback(e => {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
@@ -382,13 +448,25 @@ function VirtualBackground({
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{_showUploadButton
|
||||
&& <UploadImageButton
|
||||
setLoading = { setLoading }
|
||||
setOptions = { setOptions }
|
||||
setStoredImages = { setStoredImages }
|
||||
showLabel = { previewIsLoaded }
|
||||
storedImages = { storedImages } />}
|
||||
{previewIsLoaded && <label
|
||||
aria-label = { t('virtualBackground.uploadImage') }
|
||||
className = 'file-upload-label'
|
||||
htmlFor = 'file-upload'
|
||||
onKeyPress = { uploadImageKeyPress }
|
||||
tabIndex = { 0 } >
|
||||
<Icon
|
||||
className = { 'add-background' }
|
||||
size = { 20 }
|
||||
src = { IconPlusCircle } />
|
||||
{t('virtualBackground.addBackground')}
|
||||
</label> }
|
||||
<input
|
||||
accept = 'image/*'
|
||||
className = 'file-upload-btn'
|
||||
id = 'file-upload'
|
||||
onChange = { uploadImage }
|
||||
ref = { uploadImageButton }
|
||||
type = 'file' />
|
||||
<div
|
||||
className = 'virtual-background-dialog'
|
||||
role = 'radiogroup'
|
||||
@@ -457,7 +535,7 @@ function VirtualBackground({
|
||||
src = { IconShareDesktop } />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{_images.map(image => (
|
||||
{images.map(image => (
|
||||
<Tooltip
|
||||
content = { image.tooltip && t(`virtualBackground.${image.tooltip}`) }
|
||||
key = { image.id }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* An enumeration of the different virtual background types.
|
||||
*
|
||||
@@ -11,54 +9,3 @@ export const VIRTUAL_BACKGROUND_TYPE = {
|
||||
BLUR: 'blur',
|
||||
NONE: 'none'
|
||||
};
|
||||
|
||||
|
||||
export type Image = {
|
||||
tooltip?: string,
|
||||
id: string,
|
||||
src: string
|
||||
}
|
||||
|
||||
// The limit of virtual background uploads is 24. When the number
|
||||
// of uploads is 25 we trigger the deleteStoredImage function to delete
|
||||
// the first/oldest uploaded background.
|
||||
export const BACKGROUNDS_LIMIT = 25;
|
||||
|
||||
|
||||
export const IMAGES: Array<Image> = [
|
||||
{
|
||||
tooltip: 'image1',
|
||||
id: '1',
|
||||
src: 'images/virtual-background/background-1.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image2',
|
||||
id: '2',
|
||||
src: 'images/virtual-background/background-2.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image3',
|
||||
id: '3',
|
||||
src: 'images/virtual-background/background-3.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image4',
|
||||
id: '4',
|
||||
src: 'images/virtual-background/background-4.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image5',
|
||||
id: '5',
|
||||
src: 'images/virtual-background/background-5.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image6',
|
||||
id: '6',
|
||||
src: 'images/virtual-background/background-6.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image7',
|
||||
id: '7',
|
||||
src: 'images/virtual-background/background-7.jpg'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -2,7 +2,6 @@ local get_room_by_name_and_subdomain = module:require 'util'.get_room_by_name_an
|
||||
local is_healthcheck_room = module:require 'util'.is_healthcheck_room;
|
||||
local internal_room_jid_match_rewrite = module:require "util".internal_room_jid_match_rewrite;
|
||||
local room_jid_match_rewrite = module:require "util".room_jid_match_rewrite;
|
||||
local array = require "util.array";
|
||||
local json = require 'util.json';
|
||||
local st = require 'util.stanza';
|
||||
|
||||
@@ -14,17 +13,6 @@ end
|
||||
|
||||
module:log('info', 'Starting av_moderation for %s', muc_component_host);
|
||||
|
||||
-- Returns the index of the given element in the table
|
||||
-- @param table in which to look
|
||||
-- @param elem the element for which to find the index
|
||||
function get_index_in_table(table, elem)
|
||||
for index, value in pairs(table) do
|
||||
if value == elem then
|
||||
return index
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Sends a json-message to the destination jid
|
||||
-- @param to_jid the destination jid
|
||||
-- @param json_message the message content to send
|
||||
@@ -58,26 +46,20 @@ function notify_occupants_enable(jid, enable, room, actorJid, mediaType)
|
||||
end
|
||||
end
|
||||
|
||||
-- Notifies about a change to the whitelist. Notifies all moderators and admin and the jid itself
|
||||
-- Notifies about a jid added to the whitelist. Notifies all moderators and admin and the jid itself
|
||||
-- @param jid the jid to notify about the change
|
||||
-- @param moderators whether to notify all moderators in the room
|
||||
-- @param room the room where to send it
|
||||
-- @param mediaType used only when a participant is approved (not sent to moderators)
|
||||
-- @param removed whether the jid is removed or added
|
||||
function notify_whitelist_change(jid, moderators, room, mediaType, removed)
|
||||
function notify_whitelist_change(jid, moderators, room, mediaType)
|
||||
local body_json = {};
|
||||
body_json.type = 'av_moderation';
|
||||
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
||||
body_json.whitelists = room.av_moderation;
|
||||
if removed then
|
||||
body_json.removed = true;
|
||||
end
|
||||
body_json.mediaType = mediaType;
|
||||
local moderators_body_json_str = json.encode(body_json);
|
||||
body_json.whitelists = nil;
|
||||
if not removed then
|
||||
body_json.approved = true; -- we want to send to participants only that they were approved to unmute
|
||||
end
|
||||
body_json.approved = true; -- we want to send to participants only that they were approved to unmute
|
||||
body_json.mediaType = mediaType;
|
||||
local participant_body_json_str = json.encode(body_json);
|
||||
|
||||
for _, occupant in room:each_occupant() do
|
||||
@@ -95,22 +77,6 @@ function notify_whitelist_change(jid, moderators, room, mediaType, removed)
|
||||
end
|
||||
end
|
||||
|
||||
-- Notifies jid that is approved. This is a moderator to jid message to ask to unmute,
|
||||
-- @param jid the jid to notify about the change
|
||||
-- @param from the jid that triggered this
|
||||
-- @param room the room where to send it
|
||||
-- @param mediaType the mediaType it was approved for
|
||||
function notify_jid_approved(jid, from, room, mediaType)
|
||||
local body_json = {};
|
||||
body_json.type = 'av_moderation';
|
||||
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
||||
body_json.approved = true; -- we want to send to participants only that they were approved to unmute
|
||||
body_json.mediaType = mediaType;
|
||||
body_json.from = from;
|
||||
|
||||
send_json_message(jid, json.encode(body_json));
|
||||
end
|
||||
|
||||
-- receives messages from clients to the component sending A/V moderation enable/disable commands or adding
|
||||
-- jids to the whitelist
|
||||
function on_message(event)
|
||||
@@ -172,7 +138,7 @@ function on_message(event)
|
||||
room.av_moderation = {};
|
||||
room.av_moderation_actors = {};
|
||||
end
|
||||
room.av_moderation[mediaType] = array{};
|
||||
room.av_moderation[mediaType] = {};
|
||||
room.av_moderation_actors[mediaType] = occupant.nick;
|
||||
end
|
||||
else
|
||||
@@ -200,7 +166,7 @@ function on_message(event)
|
||||
-- send message to all occupants
|
||||
notify_occupants_enable(nil, enabled, room, occupant.nick, mediaType);
|
||||
return true;
|
||||
elseif moderation_command.attr.jidToWhitelist then
|
||||
elseif moderation_command.attr.jidToWhitelist and room.av_moderation then
|
||||
local occupant_jid = moderation_command.attr.jidToWhitelist;
|
||||
-- check if jid is in the room, if so add it to whitelist
|
||||
-- inform all moderators and admins and the jid
|
||||
@@ -210,44 +176,16 @@ function on_message(event)
|
||||
return false;
|
||||
end
|
||||
|
||||
if room.av_moderation then
|
||||
local whitelist = room.av_moderation[mediaType];
|
||||
if not whitelist then
|
||||
whitelist = array{};
|
||||
room.av_moderation[mediaType] = whitelist;
|
||||
end
|
||||
whitelist:push(occupant_jid);
|
||||
|
||||
notify_whitelist_change(occupant_to_add.jid, true, room, mediaType, false);
|
||||
|
||||
return true;
|
||||
else
|
||||
-- this is a moderator asking the jid to unmute without enabling av moderation
|
||||
-- let's just send the event
|
||||
notify_jid_approved(occupant_to_add.jid, occupant.nick, room, mediaType);
|
||||
end
|
||||
elseif moderation_command.attr.jidToBlacklist then
|
||||
local occupant_jid = moderation_command.attr.jidToBlacklist;
|
||||
-- check if jid is in the room, if so remove it from the whitelist
|
||||
-- inform all moderators and admins
|
||||
local occupant_to_remove = room:get_occupant_by_nick(room_jid_match_rewrite(occupant_jid));
|
||||
if not occupant_to_remove then
|
||||
module:log('warn', 'No occupant %s found for %s', occupant_jid, room.jid);
|
||||
return false;
|
||||
local whitelist = room.av_moderation[mediaType];
|
||||
if not whitelist then
|
||||
whitelist = {};
|
||||
room.av_moderation[mediaType] = whitelist;
|
||||
end
|
||||
table.insert(whitelist, occupant_jid);
|
||||
|
||||
if room.av_moderation then
|
||||
local whitelist = room.av_moderation[mediaType];
|
||||
if whitelist then
|
||||
local index = get_index_in_table(whitelist, occupant_jid)
|
||||
if(index) then
|
||||
whitelist:pop(index);
|
||||
notify_whitelist_change(occupant_to_remove.jid, true, room, mediaType, true);
|
||||
end
|
||||
end
|
||||
notify_whitelist_change(occupant_to_add.jid, true, room, mediaType);
|
||||
|
||||
return true;
|
||||
end
|
||||
return true;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ local function get_poll_message(stanza)
|
||||
return nil;
|
||||
end
|
||||
local data = json.decode(json_data);
|
||||
if not data or (data.type ~= "new-poll" and data.type ~= "answer-poll") then
|
||||
if data.type ~= "new-poll" and data.type ~= "answer-poll" then
|
||||
return nil;
|
||||
end
|
||||
return data;
|
||||
@@ -42,7 +42,7 @@ end
|
||||
module:hook("muc-room-created", function(event)
|
||||
local room = event.room;
|
||||
if is_healthcheck_room(room.jid) then return end
|
||||
module:log("debug", "setting up polls in room %s", room.jid);
|
||||
module:log("debug", "setting up polls in room "..tostring(room));
|
||||
room.polls = {
|
||||
by_id = {};
|
||||
order = {};
|
||||
|
||||
@@ -128,9 +128,9 @@ function Util:get_public_key(keyId)
|
||||
local content = self.cache:get(keyId);
|
||||
if content == nil then
|
||||
-- If the key is not found in the cache.
|
||||
module:log("debug", "Cache miss for key: %s", keyId);
|
||||
module:log("debug", "Cache miss for key: "..keyId);
|
||||
local keyurl = path.join(self.asapKeyServer, hex.to(sha256(keyId))..'.pem');
|
||||
module:log("debug", "Fetching public key from: %s", keyurl);
|
||||
module:log("debug", "Fetching public key from: "..keyurl);
|
||||
content = http_get_with_retry(keyurl, nr_retries);
|
||||
if content ~= nil then
|
||||
self.cache:set(keyId, content);
|
||||
@@ -138,7 +138,7 @@ function Util:get_public_key(keyId)
|
||||
return content;
|
||||
else
|
||||
-- If the key is in the cache, use it.
|
||||
module:log("debug", "Cache hit for key: %s", keyId);
|
||||
module:log("debug", "Cache hit for key: "..keyId);
|
||||
return content;
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user