mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-11 17:20:18 +00:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3622a3698 | ||
|
|
4cdc193ac3 | ||
|
|
5c9b8a5a5f | ||
|
|
44bd70179c | ||
|
|
0973081fea | ||
|
|
006c491de4 | ||
|
|
24cfa7b4e1 | ||
|
|
b890aa33c3 | ||
|
|
9846228210 | ||
|
|
445eef97b3 | ||
|
|
298279a956 | ||
|
|
5ebf5adfa2 | ||
|
|
35554533d1 | ||
|
|
fb397db69f | ||
|
|
8fc295b385 | ||
|
|
ee8b62cf73 | ||
|
|
217b803652 | ||
|
|
2442a332e7 | ||
|
|
2e43706a4a | ||
|
|
e7db943b73 | ||
|
|
c1cbd37d87 | ||
|
|
ef56f1d23d | ||
|
|
2ab002650c | ||
|
|
867646da1a | ||
|
|
ff78bfb62f | ||
|
|
1827610fc1 | ||
|
|
0ae8051fb4 | ||
|
|
1b7668bbdc | ||
|
|
4c37e0d761 | ||
|
|
a6ff652a03 | ||
|
|
17f34878db | ||
|
|
37856e0fdb | ||
|
|
7c1de52f6a | ||
|
|
9e3438696b | ||
|
|
9c23cc0b70 | ||
|
|
af8157bc06 | ||
|
|
c21f84c15a | ||
|
|
1edf88e744 | ||
|
|
bad75b488e | ||
|
|
5bb3ba71d0 | ||
|
|
4e0001c9af | ||
|
|
ada6150971 | ||
|
|
62d1ee606a | ||
|
|
1e1de6acf0 | ||
|
|
e1e0fba4ff | ||
|
|
97be3fa399 | ||
|
|
fbdee85c4b | ||
|
|
b71b4c8c45 | ||
|
|
a8d123ede4 | ||
|
|
fb8e451e2e | ||
|
|
5dd7944bdb | ||
|
|
48732c6784 | ||
|
|
97146ed8a7 | ||
|
|
78b17c8d17 | ||
|
|
9732d1be86 | ||
|
|
a836187620 | ||
|
|
a0cad4c595 | ||
|
|
19b9eac84a | ||
|
|
068e33fc0f | ||
|
|
00c6bee2fd | ||
|
|
748ead7e13 | ||
|
|
174c4418fb | ||
|
|
64494cab81 | ||
|
|
50d0092e30 | ||
|
|
fdbd7239ff | ||
|
|
a30958ab23 | ||
|
|
aca55172e4 | ||
|
|
c6cce9253c | ||
|
|
c23684e11c | ||
|
|
30595584a3 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -101,6 +101,9 @@ tsconfig.json
|
||||
react-native-sdk/*.tgz
|
||||
react-native-sdk/android/src
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetReactNativePackage.java
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetOngoingConferenceService.java
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JMOngoingConferenceModule.java
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/RNOngoingNotification.java
|
||||
react-native-sdk/images
|
||||
react-native-sdk/ios
|
||||
react-native-sdk/lang
|
||||
|
||||
@@ -34,7 +34,7 @@ mobile apps:
|
||||
|
||||
| Android | Android (F-Droid) | iOS |
|
||||
|:-:|:-:|:-:|
|
||||
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
|
||||
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
|
||||
|
||||
If you are feeling adventurous and want to get an early scoop of the features as they are being
|
||||
developed you can also sign up for our open beta testing here:
|
||||
|
||||
@@ -20,7 +20,7 @@ ext {
|
||||
kotlinVersion = "1.9.24"
|
||||
buildToolsVersion = "34.0.0"
|
||||
compileSdkVersion = 34
|
||||
minSdkVersion = 24
|
||||
minSdkVersion = 26
|
||||
targetSdkVersion = 34
|
||||
supportLibVersion = "28.0.0"
|
||||
ndkVersion = "26.1.10909125"
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.telecom.CallAudioState;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
@@ -34,7 +33,6 @@ import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
* {@link AudioModeModule.AudioDeviceHandlerInterface} module implementing device handling for
|
||||
* Android versions >= O when ConnectionService is enabled.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class AudioDeviceHandlerConnectionService implements
|
||||
AudioModeModule.AudioDeviceHandlerInterface,
|
||||
RNConnectionService.CallAudioStateListener {
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.media.AudioAttributes;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -227,22 +226,17 @@ class AudioDeviceHandlerGeneric implements
|
||||
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
audioManager.setMicrophoneMute(false);
|
||||
|
||||
int gotFocus;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
gotFocus = audioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
||||
.setAudioAttributes(
|
||||
new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build()
|
||||
)
|
||||
.setAcceptsDelayedFocusGain(true)
|
||||
.setOnAudioFocusChangeListener(this)
|
||||
.build()
|
||||
);
|
||||
} else {
|
||||
gotFocus = audioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN);
|
||||
}
|
||||
int gotFocus = audioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
||||
.setAudioAttributes(
|
||||
new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build()
|
||||
)
|
||||
.setAcceptsDelayedFocusGain(true)
|
||||
.setOnAudioFocusChangeListener(this)
|
||||
.build()
|
||||
);
|
||||
|
||||
if (gotFocus == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
JitsiMeetLogger.w(TAG + " Audio focus request failed");
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
@@ -58,7 +57,6 @@ import java.util.concurrent.Executors;
|
||||
* Before a call has started and after it has ended the
|
||||
* {@code AudioModeModule.DEFAULT} mode should be used.
|
||||
*/
|
||||
@SuppressLint("AnnotateVersionCheck")
|
||||
@ReactModule(name = AudioModeModule.NAME)
|
||||
class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
public static final String NAME = "AudioMode";
|
||||
@@ -84,11 +82,10 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
/**
|
||||
* Whether or not the ConnectionService is used for selecting audio devices.
|
||||
*/
|
||||
private static final boolean supportsConnectionService = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
private static boolean useConnectionService_ = supportsConnectionService;
|
||||
private static boolean useConnectionService_ = true;
|
||||
|
||||
static boolean useConnectionService() {
|
||||
return supportsConnectionService && useConnectionService_;
|
||||
return useConnectionService_;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.Objects;
|
||||
*
|
||||
* @author Pawel Domas
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class ConnectionService extends android.telecom.ConnectionService {
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ public class JitsiMeetActivityDelegate {
|
||||
/**
|
||||
* Tells whether or not the permissions request is currently in progress.
|
||||
*
|
||||
* @return {@code true} if the permssions are being requested or {@code false} otherwise.
|
||||
* @return {@code true} if the permissions are being requested or {@code false} otherwise.
|
||||
*/
|
||||
static boolean arePermissionsBeingRequested() {
|
||||
return permissionListener != null;
|
||||
|
||||
@@ -83,11 +83,7 @@ public class JitsiMeetOngoingConferenceService extends Service implements Ongoin
|
||||
ComponentName componentName;
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
componentName = context.startForegroundService(intent);
|
||||
} else {
|
||||
componentName = context.startService(intent);
|
||||
}
|
||||
componentName = context.startForegroundService(intent);
|
||||
} catch (RuntimeException e) {
|
||||
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
|
||||
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
|
||||
|
||||
@@ -29,8 +29,6 @@ import android.content.Intent;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for creating the ongoing notification which is used with
|
||||
@@ -45,10 +43,6 @@ class OngoingNotification {
|
||||
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
|
||||
|
||||
static void createNotificationChannel(Activity context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
|
||||
return;
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.PictureInPictureParams;
|
||||
import android.os.Build;
|
||||
import android.util.Rational;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
@@ -53,7 +52,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
// Android Go devices don't support PiP. There doesn't seem to be a better way to detect it than
|
||||
// to use ActivityManager.isLowRamDevice().
|
||||
// https://stackoverflow.com/questions/58340558/how-to-detect-android-go
|
||||
isSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !am.isLowRamDevice();
|
||||
isSupported = !am.isLowRamDevice();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +81,6 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
* including when the activity is not visible (paused or stopped), if the
|
||||
* screen is locked or if the user has an activity pinned.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public void enterPictureInPicture() {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.jitsi.meet.sdk;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.telecom.DisconnectCause;
|
||||
import android.telecom.PhoneAccount;
|
||||
@@ -32,7 +31,6 @@ import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
*
|
||||
* @author Pawel Domas
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@ReactModule(name = RNConnectionService.NAME)
|
||||
class RNConnectionService extends ReactContextBaseJavaModule {
|
||||
|
||||
@@ -53,7 +51,6 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
||||
* @param audioRoute the new audio route to be set. See
|
||||
* {@link android.telecom.CallAudioState} constants prefixed with "ROUTE_".
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
static void setAudioRoute(int audioRoute) {
|
||||
for (ConnectionService.ConnectionImpl c
|
||||
: ConnectionService.getConnections()) {
|
||||
|
||||
@@ -1012,7 +1012,7 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Will be filled with values only when config.debug is enabled.
|
||||
* Will be filled with values only when config.testing.testMode is true.
|
||||
* Its used by torture to check audio levels.
|
||||
*/
|
||||
audioLevelsMap: {},
|
||||
@@ -1399,28 +1399,19 @@ export default {
|
||||
/**
|
||||
* Creates desktop (screensharing) {@link JitsiLocalTrack}
|
||||
*
|
||||
* @param {Object} [options] - Screen sharing options that will be passed to
|
||||
* createLocalTracks.
|
||||
* @param {Object} [options.desktopSharing]
|
||||
* @param {Object} [options.desktopStream] - An existing desktop stream to
|
||||
* use instead of creating a new desktop stream.
|
||||
* @return {Promise.<JitsiLocalTrack>} - A Promise resolved with
|
||||
* {@link JitsiLocalTrack} for the screensharing or rejected with
|
||||
* {@link JitsiTrackError}.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_createDesktopTrack(options = {}) {
|
||||
_createDesktopTrack() {
|
||||
const didHaveVideo = !this.isLocalVideoMuted();
|
||||
|
||||
const getDesktopStreamPromise = options.desktopStream
|
||||
? Promise.resolve([ options.desktopStream ])
|
||||
: createLocalTracksF({
|
||||
desktopSharingSourceDevice: options.desktopSharingSources
|
||||
? null : config._desktopSharingSourceDevice,
|
||||
desktopSharingSources: options.desktopSharingSources,
|
||||
devices: [ 'desktop' ]
|
||||
});
|
||||
const getDesktopStreamPromise = createLocalTracksF({
|
||||
desktopSharingSourceDevice: config._desktopSharingSourceDevice,
|
||||
devices: [ 'desktop' ]
|
||||
});
|
||||
|
||||
return getDesktopStreamPromise.then(desktopStreams => {
|
||||
// Stores the "untoggle" handler which remembers whether was
|
||||
@@ -1597,9 +1588,9 @@ export default {
|
||||
newLvl = 0;
|
||||
}
|
||||
|
||||
if (config.debug) {
|
||||
if (config.testing?.testMode) {
|
||||
this.audioLevelsMap[id] = newLvl;
|
||||
if (config.debugAudioLevels) {
|
||||
if (config.testing?.debugAudioLevels) {
|
||||
logger.log(`AudioLevel:${id}/${newLvl}`);
|
||||
}
|
||||
}
|
||||
|
||||
82
config.js
82
config.js
@@ -103,6 +103,12 @@ var config = {
|
||||
|
||||
// Dump transcripts to a <transcript> element for debugging.
|
||||
// dumpTranscript: false,
|
||||
|
||||
// Log the audio levels.
|
||||
// debugAudioLevels: true,
|
||||
|
||||
// Will replace ice candidates IPs with invalid ones in order to fail ice.
|
||||
// failICE: true,
|
||||
},
|
||||
|
||||
// Disables moderator indicators.
|
||||
@@ -362,9 +368,6 @@ var config = {
|
||||
|
||||
// Recording
|
||||
|
||||
// DEPRECATED. Use recordingService.enabled instead.
|
||||
// fileRecordingsEnabled: false,
|
||||
|
||||
// Enable the dropbox integration.
|
||||
// dropbox: {
|
||||
// appKey: '<APP_KEY>', // Specify your app key here.
|
||||
@@ -459,7 +462,7 @@ var config = {
|
||||
|
||||
// // Translation languages.
|
||||
// // Available languages can be found in
|
||||
// // ./src/react/features/transcribing/translation-languages.json.
|
||||
// // ./lang/translation-languages.json.
|
||||
// translationLanguages: ['en', 'es', 'fr', 'ro'],
|
||||
|
||||
// // Important languages to show on the top of the language list.
|
||||
@@ -785,6 +788,9 @@ var config = {
|
||||
// and microsoftApiApplicationClientID
|
||||
// enableCalendarIntegration: false,
|
||||
|
||||
// The client id for the google APIs used for the calendar integration, youtube livestreaming, etc.
|
||||
// googleApiApplicationClientID: '<client_id>',
|
||||
|
||||
// Configs for prejoin page.
|
||||
// prejoinConfig: {
|
||||
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
|
||||
@@ -819,10 +825,6 @@ var config = {
|
||||
// set or the lobby is not enabled.
|
||||
// enableInsecureRoomNameWarning: false,
|
||||
|
||||
// Whether to automatically copy invitation URL after creating a room.
|
||||
// Document should be focused for this option to work
|
||||
// enableAutomaticUrlCopy: false,
|
||||
|
||||
// Array with avatar URL prefixes that need to use CORS.
|
||||
// corsAvatarURLs: [ 'https://www.gravatar.com/avatar/' ],
|
||||
|
||||
@@ -907,7 +909,7 @@ var config = {
|
||||
// Overrides the buttons displayed in the main toolbar. Depending on the screen size the number of displayed
|
||||
// buttons varies from 2 buttons to 8 buttons. Every array in the mainToolbarButtons array will replace the
|
||||
// corresponding default buttons configuration matched by the number of buttons specified in the array. Arrays with
|
||||
// more than 8 buttons or less then 2 buttons will be ignored. When there there isn't an override for a cerain
|
||||
// more than 8 buttons or less then 2 buttons will be ignored. When there there isn't an override for a certain
|
||||
// configuration (for example when 3 buttons are displayed) the default jitsi-meet configuration will be used.
|
||||
// The order of the buttons in the array is preserved.
|
||||
// mainToolbarButtons: [
|
||||
@@ -1620,19 +1622,47 @@ var config = {
|
||||
// For external entities (e. g. email), the localStorage key holding the token value for directory authentication
|
||||
// peopleSearchTokenLocation: "mytoken",
|
||||
|
||||
|
||||
// Options related to visitors.
|
||||
// visitors: {
|
||||
// // Starts audio/video when the participant is promoted from visitor.
|
||||
// enableMediaOnPromote: {
|
||||
// audio: true,
|
||||
// video: true
|
||||
// },
|
||||
// },
|
||||
// The default type of desktop sharing sources that will be used in the electron app.
|
||||
// desktopSharingSources: ['screen', 'window'],
|
||||
|
||||
// Disables the echo cancelation for local audio tracks.
|
||||
// disableAEC: true,
|
||||
|
||||
// Disables the auto gain control for local audio tracks.
|
||||
// disableAGC: true,
|
||||
|
||||
// Disables the audio processing (echo cancelation, auto gain control and noise suppression) for local audio tracks.
|
||||
// disableAP: true,
|
||||
|
||||
// Disables the anoise suppression for local audio tracks.
|
||||
// disableNS: true,
|
||||
|
||||
// Replaces the display name with the JID of the participants.
|
||||
// displayJids: true,
|
||||
|
||||
// Enables disables talk while muted detection.
|
||||
// enableTalkWhileMuted: true,
|
||||
|
||||
// Sets the peer connection ICE transport policy to "relay".
|
||||
// forceTurnRelay: true,
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
debug
|
||||
debugAudioLevels
|
||||
deploymentInfo
|
||||
dialOutAuthUrl
|
||||
dialOutCodesUrl
|
||||
dialOutRegionUrl
|
||||
disableRemoteControl
|
||||
displayJids
|
||||
firefox_fake_device
|
||||
googleApiApplicationClientID
|
||||
iAmRecorder
|
||||
iAmSipGateway
|
||||
microsoftApiApplicationClientID
|
||||
@@ -1651,14 +1681,7 @@ var config = {
|
||||
_peerConnStatusRtcMuteTimeout
|
||||
avgRtpStatsN
|
||||
desktopSharingSources
|
||||
disableAEC
|
||||
disableAGC
|
||||
disableAP
|
||||
disableHPF
|
||||
disableLocalStats
|
||||
disableNS
|
||||
enableTalkWhileMuted
|
||||
forceTurnRelay
|
||||
hiddenDomain
|
||||
hiddenFromRecorderFeatureEnabled
|
||||
ignoreStartMuted
|
||||
@@ -1735,7 +1758,7 @@ var config = {
|
||||
// 'notify.participantsWantToJoin', // shown when lobby is enabled and participants request to join meeting
|
||||
// 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
|
||||
// 'notify.passwordSetRemotely', // shown when a password has been set remotely
|
||||
// 'notify.raisedHand', // shown when a partcipant used raise hand,
|
||||
// 'notify.raisedHand', // shown when a participant used raise hand,
|
||||
// 'notify.screenShareNoAudio', // shown when the audio could not be shared for the selected screen
|
||||
// 'notify.screenSharingAudioOnlyTitle', // shown when the best performance has been affected by screen sharing
|
||||
// 'notify.selfViewTitle', // show "You can always un-hide the self-view from settings"
|
||||
@@ -1766,7 +1789,7 @@ var config = {
|
||||
// disableFilmstripAutohiding: false,
|
||||
|
||||
// filmstrip: {
|
||||
// // Disable the vertical/horizonal filmstrip.
|
||||
// // Disable the vertical/horizontal filmstrip.
|
||||
// disabled: false,
|
||||
// // Disables user resizable filmstrip. Also, allows configuration of the filmstrip
|
||||
// // (width, tiles aspect ratios) through the interfaceConfig options.
|
||||
@@ -1825,9 +1848,10 @@ var config = {
|
||||
// //disableLogCollector: true,
|
||||
// // Individual loggers are customizable.
|
||||
// loggers: {
|
||||
// // The following are too verbose in their logging with the default level.
|
||||
// 'modules/RTC/TraceablePeerConnection.js': 'info',
|
||||
// 'modules/xmpp/strophe.util.js': 'log',
|
||||
// // The following are too verbose in their logging with the default level.
|
||||
// 'modules/RTC/TraceablePeerConnection.js': 'info',
|
||||
// 'modules/xmpp/strophe.util.js': 'log',
|
||||
// },
|
||||
// },
|
||||
|
||||
// Application logo url
|
||||
@@ -1885,12 +1909,6 @@ var config = {
|
||||
// hideLoginButton: true,
|
||||
};
|
||||
|
||||
// Temporary backwards compatibility with old mobile clients.
|
||||
config.flags = config.flags || {};
|
||||
config.flags.sourceNameSignaling = true;
|
||||
config.flags.sendMultipleVideoStreams = true;
|
||||
config.flags.receiveMultipleVideoStreams = true;
|
||||
|
||||
// Set the default values for JaaS customers
|
||||
if (enableJaaS) {
|
||||
config.dialInNumbersUrl = 'https://conference-mapper.jitsi.net/v1/access/dids';
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
flex-direction: column;
|
||||
|
||||
.description {
|
||||
color: #2f3237;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
margin-bottom: 16px;
|
||||
max-width: 436px;
|
||||
color: #2f3237;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
margin-bottom: 16px;
|
||||
max-width: 436px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,8 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.with-click-handler:hover {
|
||||
&.with-click-handler:hover,
|
||||
&.with-click-handler:focus {
|
||||
background-color: #c7ddff;
|
||||
}
|
||||
|
||||
@@ -155,14 +156,22 @@
|
||||
margin-right: 16px;
|
||||
position: absolute;
|
||||
|
||||
&> svg {
|
||||
&>svg {
|
||||
fill: #0074e0;
|
||||
}
|
||||
}
|
||||
|
||||
.item:hover, .item:focus, .item:focus-within {
|
||||
.item:hover,
|
||||
.item:focus,
|
||||
.item:focus-within {
|
||||
.delete-meeting {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.delete-meeting:hover {
|
||||
&>svg {
|
||||
fill: #4687ED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
debian/jitsi-meet-web-config.postinst
vendored
16
debian/jitsi-meet-web-config.postinst
vendored
@@ -175,22 +175,6 @@ case "$1" in
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fixes multi-stream flags to workaround problem with mobile joining a multi-stream call with multi-stream disabled
|
||||
FIX_MSG="// Temporary backwards compatibility with old mobile clients."
|
||||
if ! grep -q "^${FIX_MSG}" $JITSI_MEET_CONFIG; then
|
||||
echo $FIX_MSG >> $JITSI_MEET_CONFIG
|
||||
echo "config.flags = config.flags || {};" >> $JITSI_MEET_CONFIG
|
||||
fi
|
||||
if ! grep -q "^config.flags.sourceNameSignaling*" $JITSI_MEET_CONFIG; then
|
||||
echo "config.flags.sourceNameSignaling = true;" >> $JITSI_MEET_CONFIG
|
||||
fi
|
||||
if ! grep -q "^config.flags.sendMultipleVideoStreams*" $JITSI_MEET_CONFIG; then
|
||||
echo "config.flags.sendMultipleVideoStreams = true;" >> $JITSI_MEET_CONFIG
|
||||
fi
|
||||
if ! grep -q "^config.flags.receiveMultipleVideoStreams*" $JITSI_MEET_CONFIG; then
|
||||
echo "config.flags.receiveMultipleVideoStreams = true;" >> $JITSI_MEET_CONFIG
|
||||
fi
|
||||
|
||||
if [[ "$FORCE_OPENRESTY" = "true" ]]; then
|
||||
NGX_COMMON_CONF_PATH="/usr/local/openresty/nginx/conf/$JVB_HOSTNAME.conf"
|
||||
NGX_SVC_NAME=openresty
|
||||
|
||||
1
globals.d.ts
vendored
1
globals.d.ts
vendored
@@ -18,7 +18,6 @@ declare global {
|
||||
JITSI_MEET_LITE_SDK?: boolean;
|
||||
interfaceConfig?: any;
|
||||
JitsiMeetJS?: any;
|
||||
JitsiMeetElectron?: any;
|
||||
PressureObserver?: any;
|
||||
PressureRecord?: any;
|
||||
ReactNativeWebView?: any;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -41,14 +41,6 @@ var interfaceConfig = {
|
||||
*/
|
||||
DISABLE_PRESENCE_STATUS: false,
|
||||
|
||||
/**
|
||||
* Whether the ringing sound in the call/ring overlay is disabled. If
|
||||
* {@code undefined}, defaults to {@code false}.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
DISABLE_RINGING: false,
|
||||
|
||||
/**
|
||||
* Whether the speech to text transcription subtitles panel is disabled.
|
||||
* If {@code undefined}, defaults to {@code false}.
|
||||
@@ -70,9 +62,6 @@ var interfaceConfig = {
|
||||
|
||||
ENABLE_DIAL_OUT: true,
|
||||
|
||||
// DEPRECATED. Animation no longer supported.
|
||||
// ENABLE_FEEDBACK_ANIMATION: false,
|
||||
|
||||
FILM_STRIP_MAX_HEIGHT: 120,
|
||||
|
||||
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
|
||||
@@ -227,7 +216,7 @@ var interfaceConfig = {
|
||||
/**
|
||||
* Specify custom URL for downloading f droid app.
|
||||
*/
|
||||
// MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/',
|
||||
// MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/packages/org.jitsi.meet/',
|
||||
|
||||
// Connection indicators (
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
|
||||
. ${THIS_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh
|
||||
|
||||
export NODE_BINARY=$(command -v node)
|
||||
export ENTRY_FILE="${PROJECT_DIR}/../../index.ios.js"
|
||||
|
||||
@@ -69,7 +69,7 @@ platform :ios do
|
||||
end
|
||||
)
|
||||
|
||||
# Inrement the build number by 1
|
||||
# Increment the build number by 1
|
||||
increment_build_number(
|
||||
build_number: Time.now.to_i,
|
||||
xcodeproj: "app/app.xcodeproj"
|
||||
|
||||
@@ -54,7 +54,7 @@ pushd ${RELEASE_REPO}
|
||||
|
||||
# Put the new files in the repo
|
||||
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeetSDK.xcframework lite/Frameworks/
|
||||
cp -a ${PROJECT_REPO}/ios/sdk/out/hermes.xcframework lite/Frameworks/
|
||||
cp -a ${PROJECT_REPO}/ios/Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework lite/Frameworks/
|
||||
|
||||
# Add all files to git
|
||||
git add -A .
|
||||
|
||||
@@ -54,7 +54,7 @@ pushd ${RELEASE_REPO}
|
||||
|
||||
# Put the new files in the repo
|
||||
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeetSDK.xcframework Frameworks/
|
||||
cp -a ${PROJECT_REPO}/ios/sdk/out/hermes.xcframework Frameworks/
|
||||
cp -a ${PROJECT_REPO}/ios/Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework Frameworks/
|
||||
|
||||
# Add all files to git
|
||||
git add -A .
|
||||
|
||||
@@ -334,6 +334,7 @@
|
||||
"kickParticipantButton": "Expulser",
|
||||
"kickParticipantDialog": "Êtes-vous sûr(e) de vouloir expulser ce participant ?",
|
||||
"kickParticipantTitle": "Expulser ce participant ?",
|
||||
"kickSystemTitle": "Oups ! Vous avez été expulsé de la réunion",
|
||||
"kickTitle": "Oups ! vous avez été expulsé(e) par {{participantDisplayName}}",
|
||||
"linkMeeting": "Relier la conférence",
|
||||
"linkMeetingTitle": "Relier la conférence à Salesforce",
|
||||
@@ -439,7 +440,10 @@
|
||||
"shareScreenWarningD2": "vous devez arrêter le partage d'audio, démarrer le partage d'écran et cocher l'option \"Partager l'audio\".",
|
||||
"shareScreenWarningH1": "Si vous voulez partager uniquement votre écran:",
|
||||
"shareScreenWarningTitle": "Vous devez cesser de partager votre audio avant de partager votre écran",
|
||||
"shareVideoConfirmPlay": "Vous êtes sur le point d'ouvrir un site web externe. Voulez-vous continuer ?",
|
||||
"shareVideoConfirmPlayTitle": "{{name}} a partagé une vidéo avec vous.",
|
||||
"shareVideoLinkError": "Veuillez renseigner un lien de diffusion vidéo fonctionnel.",
|
||||
"shareVideoLinkStopped": "La vidéo de {{name}} a été arrêtée.",
|
||||
"shareVideoTitle": "Partager une vidéo",
|
||||
"shareYourScreen": "Partager votre écran",
|
||||
"shareYourScreenDisabled": "Le partage d'écran est désactivé.",
|
||||
@@ -638,6 +642,7 @@
|
||||
"on": "En direct",
|
||||
"onBy": "{{name}} a démarré la diffusion en direct",
|
||||
"pending": "Lancement du direct…",
|
||||
"policyError": "Vous avez essayé de démarrer une diffusion en direct trop rapidement. Veuillez réessayer plus tard !",
|
||||
"serviceName": "Service de diffusion en direct",
|
||||
"sessionAlreadyActive": "Cette session est déjà en cours d'enregistrement ou de diffusion.",
|
||||
"signIn": "Se connecter avec Google",
|
||||
@@ -736,6 +741,7 @@
|
||||
"connectedOneMember": "{{name}} a rejoint la réunion",
|
||||
"connectedThreePlusMembers": "{{name}} et {{count}} autres personnes ont rejoint la réunion",
|
||||
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
|
||||
"connectionFailed": "Connexion échouée. Veuillez réessayer plus tard !",
|
||||
"dataChannelClosed": "Qualité vidéo dégradée",
|
||||
"dataChannelClosedDescription": "Le canal de communication avec le Bridge a été interrompu, la qualité vidéo se trouve limitée à sa valeur la plus faible.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Le canal de pont est fermé, ce qui peut entraîner des perturbations de l'audio et de la vidéo.",
|
||||
@@ -750,6 +756,9 @@
|
||||
"gifsMenu": "GIPHY",
|
||||
"groupTitle": "Notifications",
|
||||
"hostAskedUnmute": "Le modérateur souhaite vous donner la parole",
|
||||
"invalidTenant": "Tenant invalide",
|
||||
"invalidTenantHyphenDescription": "Le tenant que vous utilisez est invalide (commence ou se termine par '-').",
|
||||
"invalidTenantLengthDescription": "Le tenant que vous utilisez est trop long.",
|
||||
"invitedOneMember": "{{name}} a été invité(e)",
|
||||
"invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités",
|
||||
"invitedTwoMembers": "{{first}} et {{second}} ont été invités",
|
||||
@@ -786,6 +795,7 @@
|
||||
"newDeviceAction": "Utiliser",
|
||||
"newDeviceAudioTitle": "Nouveau périphérique audio détecté",
|
||||
"newDeviceCameraTitle": "Nouvelle caméra détectée",
|
||||
"nextToSpeak": "Vous êtes le prochain à prendre la parole",
|
||||
"noiseSuppressionDesktopAudioDescription": "La suppression de bruit ne peut pas être activée en même temps que la partage audio du système, veuillez le désactiver et réessayer.",
|
||||
"noiseSuppressionFailedTitle": "Échec du démarrage de la suppression de bruit",
|
||||
"noiseSuppressionStereoDescription": "La suppression de bruit d’une source stéréo n’est pas encore supportée.",
|
||||
@@ -820,6 +830,7 @@
|
||||
"videoUnmuteBlockedDescription": "Le rétablissement de la vidéo a été bloqué temporairement en raison de limites système.",
|
||||
"videoUnmuteBlockedTitle": "Rétablissement de la caméra bloqué !",
|
||||
"viewLobby": "Voir la salle d'attente",
|
||||
"viewParticipants": "Voir les participants",
|
||||
"viewVisitors": "Voir les visiteurs",
|
||||
"waitingParticipants": "{{waitingParticipants}} personnes",
|
||||
"waitingVisitors": "Visiteurs en attente dans la file : {{waitingVisitors}}",
|
||||
@@ -839,6 +850,8 @@
|
||||
"breakoutRooms": "Salles annexes",
|
||||
"goLive": "Passer en direct",
|
||||
"invite": "Inviter quelqu'un",
|
||||
"lowerAllHands": "Abaisser toutes les mains",
|
||||
"lowerHand": "Abaisser la main",
|
||||
"moreModerationActions": "Options de modération supplémentaires",
|
||||
"moreModerationControls": "Options de modération supplémentaires",
|
||||
"moreParticipantOptions": "Options supplémentaires pour les participants",
|
||||
@@ -875,6 +888,7 @@
|
||||
"submit": "Envoyer"
|
||||
},
|
||||
"by": "Par {{ name }}",
|
||||
"closeButton": "Fermer le sondage",
|
||||
"create": {
|
||||
"addOption": "Ajouter une option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
@@ -914,9 +928,11 @@
|
||||
"configuringDevices": "Configuration des appareils…",
|
||||
"connectedWithAudioQ": "Êtes-vous connecté avec le microphone ?",
|
||||
"connection": {
|
||||
"failed": "Le test de connexion a échoué !",
|
||||
"good": "Votre connexion Internet est bonne !",
|
||||
"nonOptimal": "Votre connexion n'est pas optimale",
|
||||
"poor": "Vous avez une mauvaise connexion"
|
||||
"poor": "Vous avez une mauvaise connexion",
|
||||
"running": "Exécution du test de connexion…"
|
||||
},
|
||||
"connectionDetails": {
|
||||
"audioClipping": "Attendez vous à ce que votre son soit coupé.",
|
||||
@@ -925,6 +941,7 @@
|
||||
"goodQuality": "Impressionnant ! La qualité de vos médias sera excellente",
|
||||
"noMediaConnectivity": "Nous n'avons pas pu trouver un moyen d'établir une connectivité multimédia pour ce test. Cela est généralement causé par un pare-feu ou un NAT.",
|
||||
"noVideo": "Attendez vous à ce que votre qualité vidéo soit très mauvaise.",
|
||||
"testFailed": "Le test de connexion a rencontré des problèmes inattendus, mais cela pourrait ne pas affecter votre expérience.",
|
||||
"undetectable": "Si vous ne parvenez toujours pas à passer des appels dans le navigateur, nous vous recommandons de vous assurer que vos haut-parleurs, microphone et caméra sont correctement configurés, que vous avez accordé à votre navigateur les droits d'utiliser votre microphone et votre caméra et que la version de votre navigateur est à jour. Si vous rencontrez toujours des difficultés pour appeler, vous devez contacter le développeur de l'application Web.",
|
||||
"veryPoorConnection": "Attendez vous à ce que la qualité de votre appel soit très mauvaise",
|
||||
"videoFreezing": "Attendez vous à ce que votre vidéo saute, soit noire, et pixelisée.",
|
||||
@@ -1040,6 +1057,7 @@
|
||||
"onBy": "{{name}} a démarré l'enregistrement",
|
||||
"onlyRecordSelf": "Enregistrer seulement mon audio et ma vidéo.",
|
||||
"pending": "Préparation de l'enregistrement de la réunion…",
|
||||
"policyError": "Vous avez essayé de démarrer un enregistrement trop rapidement. Veuillez réessayer plus tard !",
|
||||
"recordAudioAndVideo": "Enregistrer l'audio et la vidéo",
|
||||
"recordTranscription": "Enregistrer la transcription",
|
||||
"saveLocalRecording": "Sauvegarder l’enregistrement local (Beta)",
|
||||
@@ -1088,6 +1106,7 @@
|
||||
"desktopShareWarning": "Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
|
||||
"devices": "Périphériques",
|
||||
"followMe": "Tout le monde me suit",
|
||||
"followMeRecorder": "L'enregistreur me suit",
|
||||
"framesPerSecond": "images par seconde",
|
||||
"incomingMessage": "un message arrive",
|
||||
"language": "Langue",
|
||||
@@ -1234,6 +1253,7 @@
|
||||
"lobbyButton": "Activer / Désactiver le mode salle d'attente",
|
||||
"localRecording": "Activer / Désactiver les contrôles d'enregistrement local",
|
||||
"lockRoom": "Activer / Désactiver le mot de passe de la réunion",
|
||||
"love": "Cœur",
|
||||
"lowerHand": "Baisser la main",
|
||||
"moreActions": "Activer / Désactiver le menu d'actions supplémentaires",
|
||||
"moreActionsMenu": "Menu d'actions supplémentaires",
|
||||
@@ -1251,6 +1271,7 @@
|
||||
"privateMessage": "Envoyer un message privé",
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever la main",
|
||||
"react": "Réactions aux messages",
|
||||
"reactions": "Réactions",
|
||||
"reactionsMenu": "Ouvrir / fermer le menu réactions",
|
||||
"recording": "Activer / Désactiver l'enregistrement",
|
||||
@@ -1322,6 +1343,7 @@
|
||||
"lobbyButtonEnable": "Activer le mode salle d'attente / contrôle des participant(e)s",
|
||||
"login": "Connexion",
|
||||
"logout": "Déconnexion",
|
||||
"love": "Cœur",
|
||||
"lowerYourHand": "Baisser la main",
|
||||
"moreActions": "Plus d'actions",
|
||||
"moreOptions": "Plus d'options",
|
||||
@@ -1347,6 +1369,7 @@
|
||||
"raiseYourHand": "Lever la main",
|
||||
"reactionBoo": "Envoyer réaction huer",
|
||||
"reactionClap": "Envoyer réaction applaudir",
|
||||
"reactionHeart": "Envoyer une réaction en forme de cœur",
|
||||
"reactionLaugh": "Envoyer réaction rire",
|
||||
"reactionLike": "Envoyer réaction approuver",
|
||||
"reactionSilence": "Envoyer réaction silence",
|
||||
@@ -1380,7 +1403,7 @@
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
|
||||
"expandedLabel": "La transcription est actuellement activée",
|
||||
"failedToStart": "Échec de démarrage de la transcription",
|
||||
"failed": "La transcription a échoué",
|
||||
"labelToolTip": "La transcription de la réunion est en cours",
|
||||
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
|
||||
"sourceLanguageHere": "ici",
|
||||
@@ -1481,10 +1504,15 @@
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(visiteur)",
|
||||
"labelTooltip": "Nombre de Visiteurs",
|
||||
"joinMeeting": {
|
||||
"description": "Vous êtes actuellement un observateur dans cette conférence.",
|
||||
"raiseHand": "Levez la main",
|
||||
"title": "Rejoindre la réunion",
|
||||
"wishToSpeak": "Si vous souhaitez prendre la parole, veuillez lever la main ci-dessous et attendre l'approbation du modérateur."
|
||||
},
|
||||
"labelTooltip": "Nombre de Visiteurs: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Envoyé ici par {{actor}}, levez la main pour participer",
|
||||
"description": "Pour participer lever la main.",
|
||||
"noMainParticipantsDescription": "Un participant doit démarrer la réunion. Veuillez réessayer dans un moment.",
|
||||
"noMainParticipantsTitle": "Cette réunion n'a pas encore commencé.",
|
||||
"noVisitorLobby": "Vous ne pouvez pas rejoindre tant qu'une salle d'attente est activée pour la réunion.",
|
||||
|
||||
@@ -334,6 +334,7 @@
|
||||
"kickParticipantButton": "Izraidīt",
|
||||
"kickParticipantDialog": "Vai esat pārliecināti, ka vēlaties izraidīt šo dalībnieku?",
|
||||
"kickParticipantTitle": "Izraidīt šo dalībnieku?",
|
||||
"kickSystemTitle": "Ak! Jūs izraidīja no sapulces",
|
||||
"kickTitle": "Ak! {{participantDisplayName}} izraidīja jūs no sapulces",
|
||||
"linkMeeting": "Sasaistīt sapulci",
|
||||
"linkMeetingTitle": "Sasaistīt sapulci ar Salesforce",
|
||||
|
||||
@@ -410,6 +410,7 @@
|
||||
"sendPrivateMessageTitle": "私信回复?",
|
||||
"serviceUnavailable": "服务不可用",
|
||||
"sessTerminated": "通话已结束",
|
||||
"sessTerminatedReason": "会议已经结束",
|
||||
"sessionRestarted": "由于连接问题,呼叫重新启动。",
|
||||
"shareAudio": "继续",
|
||||
"shareAudioTitle": "如何分享音频",
|
||||
|
||||
@@ -307,8 +307,8 @@
|
||||
"contactSupport": "Contact support",
|
||||
"copied": "Copied",
|
||||
"copy": "Copy",
|
||||
"demoteParticipantDialog": "Are you sure you want to move this participant to visitor?",
|
||||
"demoteParticipantTitle": "Move to visitor",
|
||||
"demoteParticipantDialog": "Are you sure you want to move this participant to viewer?",
|
||||
"demoteParticipantTitle": "Move to viewer",
|
||||
"dismiss": "Dismiss",
|
||||
"displayNameRequired": "Hi! What’s your name?",
|
||||
"done": "Done",
|
||||
@@ -831,9 +831,9 @@
|
||||
"videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!",
|
||||
"viewLobby": "View lobby",
|
||||
"viewParticipants": "View participants",
|
||||
"viewVisitors": "View visitors",
|
||||
"viewVisitors": "View viewers",
|
||||
"waitingParticipants": "{{waitingParticipants}} people",
|
||||
"waitingVisitors": "Visitors waiting in queue: {{waitingVisitors}}",
|
||||
"waitingVisitors": "Viewers waiting in queue: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "The meeting is not live yet!",
|
||||
"whiteboardLimitDescription": "Please save your progress, as the user limit will soon be reached and the whiteboard will close.",
|
||||
"whiteboardLimitTitle": "Whiteboard usage"
|
||||
@@ -870,7 +870,7 @@
|
||||
"participantsList": "Meeting participants ({{count}})",
|
||||
"visitorInQueue": " (waiting {{count}})",
|
||||
"visitorRequests": " (requests {{count}})",
|
||||
"visitors": "Visitors {{count}}",
|
||||
"visitors": "Viewers {{count}}",
|
||||
"waitingLobby": "Waiting in lobby ({{count}})"
|
||||
},
|
||||
"search": "Search participants",
|
||||
@@ -1083,7 +1083,7 @@
|
||||
"about": "You can add a $t(lockRoomPassword) to your meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
|
||||
"aboutReadOnly": "Moderator participants can add a $t(lockRoomPassword) to the meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
|
||||
"insecureRoomNameWarningNative": "The room name is unsafe. Unwanted participants may join your meeting. {{recommendAction}} Learn more about securing your meeting ",
|
||||
"insecureRoomNameWarningWeb": "The room name is unsafe. Unwanted participants may join your meeting. {{recommendAction}} Learn more about securing you meeting <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">here</a>.",
|
||||
"insecureRoomNameWarningWeb": "The room name is unsafe. Unwanted participants may join your meeting. {{recommendAction}} Learn more about securing your meeting <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">here</a>.",
|
||||
"title": "Security Options",
|
||||
"unsafeRoomActions": {
|
||||
"meeting": "Consider securing your meeting using the security button.",
|
||||
@@ -1183,6 +1183,7 @@
|
||||
"fearful": "Fearful",
|
||||
"happy": "Happy",
|
||||
"hours": "{{count}}h",
|
||||
"labelTooltip": "Number of participants: {{count}}",
|
||||
"minutes": "{{count}}m",
|
||||
"name": "Name",
|
||||
"neutral": "Neutral",
|
||||
@@ -1451,7 +1452,7 @@
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Connection Info",
|
||||
"demote": "Move to visitor",
|
||||
"demote": "Move to viewer",
|
||||
"domute": "Mute",
|
||||
"domuteOthers": "Mute everyone else",
|
||||
"domuteVideo": "Disable camera",
|
||||
@@ -1503,21 +1504,21 @@
|
||||
"webAssemblyWarningDescription": "WebAssembly disabled or not supported by this browser"
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(visitor)",
|
||||
"chatIndicator": "(viewer)",
|
||||
"joinMeeting": {
|
||||
"description": "You're currently an observer in this conference.",
|
||||
"description": "You're currently a viewer in this conference.",
|
||||
"raiseHand": "Raise your hand",
|
||||
"title": "Joining meeting",
|
||||
"wishToSpeak": "If you wish to speak, please raise your hand below and wait for the moderator's approval."
|
||||
},
|
||||
"labelTooltip": "Number of visitors: {{count}}",
|
||||
"labelTooltip": "Number of viewers: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
|
||||
"noMainParticipantsDescription": "A participant needs to start the meeting. Please try again in a bit.",
|
||||
"noMainParticipantsTitle": "This meeting hasn’t started yet.",
|
||||
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
|
||||
"notAllowedPromotion": "A participant needs to allow your request first.",
|
||||
"title": "You are a visitor in the meeting"
|
||||
"title": "You are a viewer in the meeting"
|
||||
},
|
||||
"waitingMessage": "You'll join the meeting as soon as it is live!"
|
||||
},
|
||||
|
||||
@@ -115,7 +115,7 @@ import { toggleScreenshotCaptureSummary } from '../../react/features/screenshot-
|
||||
import { isScreenshotCaptureEnabled } from '../../react/features/screenshot-capture/functions';
|
||||
import SettingsDialog from '../../react/features/settings/components/web/SettingsDialog';
|
||||
import { SETTINGS_TABS } from '../../react/features/settings/constants';
|
||||
import { playSharedVideo, stopSharedVideo } from '../../react/features/shared-video/actions.any';
|
||||
import { playSharedVideo, stopSharedVideo } from '../../react/features/shared-video/actions';
|
||||
import { extractYoutubeIdOrURL } from '../../react/features/shared-video/functions';
|
||||
import { setRequestingSubtitles, toggleRequestingSubtitles } from '../../react/features/subtitles/actions';
|
||||
import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/functions';
|
||||
@@ -590,7 +590,7 @@ function initCommands() {
|
||||
* @param { string } arg.title - Notification title.
|
||||
* @param { string } arg.description - Notification description.
|
||||
* @param { string } arg.uid - Optional unique identifier for the notification.
|
||||
* @param { string } arg.type - Notification type, either `error`, `info`, `normal`, `success` or `warning`.
|
||||
* @param { string } arg.type - Notification type, either `error`, `normal`, `success` or `warning`.
|
||||
* Defaults to "normal" if not provided.
|
||||
* @param { string } arg.timeout - Timeout type, either `short`, `medium`, `long` or `sticky`.
|
||||
* Defaults to "short" if not provided.
|
||||
@@ -1571,8 +1571,8 @@ class API {
|
||||
formattedArgument += `${arg.toString()}: ${arg.stack}`;
|
||||
} else if (typeof arg === 'object') {
|
||||
// NOTE: The non-enumerable properties of the objects wouldn't be included in the string after
|
||||
// JSON.strigify. For example Map instance will be translated to '{}'. So I think we have to eventually
|
||||
// do something better for parsing the arguments. But since this option for strigify is part of the
|
||||
// JSON.stringify. For example Map instance will be translated to '{}'. So I think we have to eventually
|
||||
// do something better for parsing the arguments. But since this option for stringify is part of the
|
||||
// public interface and I think it could be useful in some cases I will it for now.
|
||||
try {
|
||||
formattedArgument += JSON.stringify(arg);
|
||||
|
||||
96
modules/API/external/external_api.js
vendored
96
modules/API/external/external_api.js
vendored
@@ -194,80 +194,6 @@ function changeParticipantNumber(APIInstance, number) {
|
||||
APIInstance._numberOfParticipants += number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the URL for the iframe.
|
||||
*
|
||||
* @param {string} domain - The domain name of the server that hosts the
|
||||
* conference.
|
||||
* @param {string} [options] - Another optional parameters.
|
||||
* @param {Object} [options.configOverwrite] - Object containing configuration
|
||||
* options defined in config.js to be overridden.
|
||||
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
|
||||
* configuration options defined in interface_config.js to be overridden.
|
||||
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
|
||||
* authentication.
|
||||
* @param {string} [options.lang] - The meeting's default language.
|
||||
* @param {string} [options.roomName] - The name of the room to join.
|
||||
* @returns {string} The URL.
|
||||
*/
|
||||
function generateURL(domain, options = {}) {
|
||||
return urlObjectToString({
|
||||
...options,
|
||||
url: `https://${domain}/#jitsi_meet_external_api_id=${id}`
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the arguments passed to the constructor. If the old format is used
|
||||
* the function translates the arguments to the new format.
|
||||
*
|
||||
* @param {Array} args - The arguments to be parsed.
|
||||
* @returns {Object} JS object with properties.
|
||||
*/
|
||||
function parseArguments(args) {
|
||||
if (!args.length) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const firstArg = args[0];
|
||||
|
||||
switch (typeof firstArg) {
|
||||
case 'string': // old arguments format
|
||||
case 'undefined': {
|
||||
// Not sure which format but we are trying to parse the old
|
||||
// format because if the new format is used everything will be undefined
|
||||
// anyway.
|
||||
const [
|
||||
roomName,
|
||||
width,
|
||||
height,
|
||||
parentNode,
|
||||
configOverwrite,
|
||||
interfaceConfigOverwrite,
|
||||
jwt,
|
||||
onload,
|
||||
lang
|
||||
] = args;
|
||||
|
||||
return {
|
||||
roomName,
|
||||
width,
|
||||
height,
|
||||
parentNode,
|
||||
configOverwrite,
|
||||
interfaceConfigOverwrite,
|
||||
jwt,
|
||||
onload,
|
||||
lang
|
||||
};
|
||||
}
|
||||
case 'object': // new arguments format
|
||||
return args[0];
|
||||
default:
|
||||
throw new Error('Can\'t parse the arguments!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute valid values for height and width. If a number is specified it's
|
||||
* treated as pixel units. If the value is expressed in px, em, pt or
|
||||
@@ -336,7 +262,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* @param {string} [options.release] - The key used for specifying release if enabled on the backend.
|
||||
* @param {string} [options.sandbox] - Sandbox directive for the created iframe, if desired.
|
||||
*/
|
||||
constructor(domain, ...args) {
|
||||
constructor(domain, options = {}) {
|
||||
super();
|
||||
const {
|
||||
roomName = '',
|
||||
@@ -345,21 +271,22 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
parentNode = document.body,
|
||||
configOverwrite = {},
|
||||
interfaceConfigOverwrite = {},
|
||||
jwt = undefined,
|
||||
lang = undefined,
|
||||
onload = undefined,
|
||||
jwt,
|
||||
lang,
|
||||
onload,
|
||||
invitees,
|
||||
iceServers,
|
||||
devices,
|
||||
userInfo,
|
||||
e2eeKey,
|
||||
release,
|
||||
sandbox = ''
|
||||
} = parseArguments(args);
|
||||
sandbox
|
||||
} = options;
|
||||
const localStorageContent = jitsiLocalStorage.getItem('jitsiLocalStorage');
|
||||
|
||||
this._parentNode = parentNode;
|
||||
this._url = generateURL(domain, {
|
||||
|
||||
this._url = urlObjectToString({
|
||||
configOverwrite,
|
||||
iceServers,
|
||||
interfaceConfigOverwrite,
|
||||
@@ -371,7 +298,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
appData: {
|
||||
localStorageContent
|
||||
},
|
||||
release
|
||||
release,
|
||||
url: `https://${domain}/#jitsi_meet_external_api_id=${id}`
|
||||
});
|
||||
|
||||
this._createIFrame(height, width, sandbox);
|
||||
@@ -1313,6 +1241,10 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* Returns the state of availability electron share screen via external api.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*
|
||||
* TODO: should be removed after we make sure that all Electron clients use only versions
|
||||
* after with the legacy SS suport was removed from the electron SDK. If we remove it now the SS for Electron
|
||||
* clients with older versions wont work.
|
||||
*/
|
||||
_isNewElectronScreensharingSupported() {
|
||||
return this._transport.sendRequest({
|
||||
|
||||
4081
package-lock.json
generated
4081
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
@@ -26,7 +26,7 @@
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rnnoise-wasm": "0.1.0",
|
||||
"@jitsi/rnnoise-wasm": "0.2.0",
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
@@ -68,7 +68,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1890.0.0+144b0cab/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1901.0.0+22daa357/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -76,6 +76,7 @@
|
||||
"optional-require": "1.0.3",
|
||||
"pixelmatch": "5.3.0",
|
||||
"promise.allsettled": "1.0.4",
|
||||
"promise.withresolvers": "1.0.3",
|
||||
"punycode": "2.3.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
@@ -134,14 +135,16 @@
|
||||
"@babel/plugin-transform-private-methods": "7.25.9",
|
||||
"@babel/preset-env": "7.25.9",
|
||||
"@babel/preset-react": "7.25.9",
|
||||
"@jitsi/eslint-config": "4.1.10",
|
||||
"@jitsi/eslint-config": "5.0.9",
|
||||
"@react-native/metro-config": "0.75.4",
|
||||
"@stylistic/eslint-plugin": "2.12.1",
|
||||
"@types/amplitude-js": "8.16.5",
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/dom-screen-wake-lock": "1.0.1",
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/jsonwebtoken": "9.0.7",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/minimatch": "5.1.2",
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/moment-duration-format": "2.2.6",
|
||||
"@types/offscreencanvas": "2019.7.2",
|
||||
@@ -160,8 +163,8 @@
|
||||
"@types/w3c-image-capture": "1.0.6",
|
||||
"@types/w3c-web-hid": "1.0.3",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.5",
|
||||
"@typescript-eslint/parser": "5.59.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.19.1",
|
||||
"@typescript-eslint/parser": "8.19.1",
|
||||
"@wdio/allure-reporter": "9.4.3",
|
||||
"@wdio/cli": "9.4.3",
|
||||
"@wdio/globals": "9.4.3",
|
||||
@@ -173,12 +176,12 @@
|
||||
"circular-dependency-plugin": "5.2.0",
|
||||
"clean-css-cli": "4.3.0",
|
||||
"css-loader": "6.8.1",
|
||||
"eslint": "8.40.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-jsdoc": "46.2.6",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"eslint-plugin-react-native": "4.0.0",
|
||||
"eslint-plugin-typescript-sort-keys": "2.3.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-jsdoc": "50.6.1",
|
||||
"eslint-plugin-react": "7.37.3",
|
||||
"eslint-plugin-react-native": "5.0.0",
|
||||
"eslint-plugin-typescript-sort-keys": "3.3.0",
|
||||
"jetifier": "1.6.4",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"metro-react-native-babel-preset": "0.77.0",
|
||||
@@ -220,9 +223,11 @@
|
||||
"tsc-test:native": "tsc --project tsconfig.native.json --listFilesOnly | grep -v node_modules | grep web",
|
||||
"start": "make dev",
|
||||
"test": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts",
|
||||
"test-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts --spec",
|
||||
"test-ff": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.firefox.conf.ts",
|
||||
"test-dev": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.dev.conf.ts",
|
||||
"test-grid": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts"
|
||||
"test-grid": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts",
|
||||
"test-grid-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts --spec"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.14",
|
||||
|
||||
@@ -69,7 +69,7 @@ cd ios && pod install && cd ..
|
||||
|
||||
## Android
|
||||
|
||||
- In your build.gradle have at least `minSdkVersion = 24`
|
||||
- In your build.gradle have at least `minSdkVersion = 26`
|
||||
- In `android/app/src/debug/AndroidManifest.xml` and `android/app/src/main/AndroidManifest.xml`, under the `</application>` tag, include
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||
import static android.Manifest.permission.RECORD_AUDIO;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements a ReactModule and it's
|
||||
* responsible for launching/aborting a service when a conference is in progress.
|
||||
*/
|
||||
@ReactModule(name = JMOngoingConferenceModule.NAME)
|
||||
class JMOngoingConferenceModule extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "JMOngoingConference";
|
||||
|
||||
private static final int PERMISSIONS_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
|
||||
|
||||
public JMOngoingConferenceModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void launch() {
|
||||
Context context = getReactApplicationContext();
|
||||
ReactActivity reactActivity = (ReactActivity) getCurrentActivity();
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
JMOngoingConferenceService.launch(context);
|
||||
|
||||
JitsiMeetLogger.i(NAME + " launch");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PermissionListener listener = new PermissionListener() {
|
||||
@Override
|
||||
public boolean onRequestPermissionsResult(int i, String[] strings, int[] results) {
|
||||
JitsiMeetLogger.i(NAME + " Permission callback received");
|
||||
|
||||
if (results == null || results.length == 0) {
|
||||
JitsiMeetLogger.w(NAME + " Permission results are null or empty");
|
||||
return true;
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
for (int result : results) {
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
JitsiMeetLogger.i(NAME + " Permissions granted: " + counter + "/" + results.length);
|
||||
|
||||
if (counter == results.length) {
|
||||
JitsiMeetLogger.i(NAME + " All permissions granted, launching service");
|
||||
JMOngoingConferenceService.launch(context);
|
||||
} else {
|
||||
JitsiMeetLogger.w(NAME + " Not all permissions were granted");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
JitsiMeetLogger.i(NAME + " Checking Tiramisu permissions");
|
||||
|
||||
List<String> permissionsList = new ArrayList<>();
|
||||
|
||||
permissionsList.add(POST_NOTIFICATIONS);
|
||||
permissionsList.add(RECORD_AUDIO);
|
||||
|
||||
String[] permissionsArray = new String[ permissionsList.size() ];
|
||||
permissionsArray = permissionsList.toArray( permissionsArray );
|
||||
|
||||
try {
|
||||
JitsiMeetLogger.i(NAME + " Requesting permissions: " + Arrays.toString(permissionsArray));
|
||||
reactActivity.requestPermissions(permissionsArray, PERMISSIONS_REQUEST_CODE, listener);
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.e(e, NAME + " Error requesting permissions");
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void abort() {
|
||||
Context context = getReactApplicationContext();
|
||||
JMOngoingConferenceService.abort(context);
|
||||
|
||||
JitsiMeetLogger.i(NAME + " abort");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class implements an Android {@link Service}, a foreground one specifically, and it's
|
||||
* responsible for presenting an ongoing notification when a conference is in progress.
|
||||
* The service will help keep the app running while in the background.
|
||||
*
|
||||
* See: https://developer.android.com/guide/components/services
|
||||
*/
|
||||
public class JMOngoingConferenceService extends Service {
|
||||
private static final String TAG = JMOngoingConferenceService.class.getSimpleName();
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
|
||||
public static void launch(Context context) {
|
||||
try {
|
||||
RNOngoingNotification.createOngoingConferenceNotificationChannel(context);
|
||||
JitsiMeetLogger.i(TAG + " Notification channel creation completed");
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.e(e, TAG + " Error creating notification channel");
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, JMOngoingConferenceService.class);
|
||||
|
||||
try {
|
||||
context.startForegroundService(intent);
|
||||
JitsiMeetLogger.i(TAG + " Starting foreground service");
|
||||
} catch (RuntimeException e) {
|
||||
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
|
||||
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
|
||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JMOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
JitsiMeetLogger.i(TAG + " onCreate called");
|
||||
|
||||
try {
|
||||
Notification notification = RNOngoingNotification.buildOngoingConferenceNotification(this);
|
||||
JitsiMeetLogger.i(TAG + " Notification build result: " + (notification != null));
|
||||
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
JitsiMeetLogger.i(TAG + " Starting service in foreground with notification");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE);
|
||||
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.e(e, TAG + " Error in onCreate");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public class JitsiMeetReactNativePackage implements ReactPackage {
|
||||
new AndroidSettingsModule(reactContext),
|
||||
new AppInfoModule(reactContext),
|
||||
new AudioModeModule(reactContext),
|
||||
new JMOngoingConferenceModule(reactContext),
|
||||
new JavaScriptSandboxModule(reactContext),
|
||||
new LocaleDetector(reactContext),
|
||||
new LogBridgeModule(reactContext),
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for creating the ongoing notification which is used with
|
||||
* {@link JMOngoingConferenceService}. It allows the user to easily get back to the app
|
||||
* and to hangup from within the notification itself.
|
||||
*/
|
||||
class RNOngoingNotification {
|
||||
private static final String TAG = RNOngoingNotification.class.getSimpleName();
|
||||
|
||||
static final String RN_ONGOING_CONFERENCE_CHANNEL_ID = "OngoingConferenceChannel";
|
||||
|
||||
static void createOngoingConferenceNotificationChannel(Context context) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Service.NOTIFICATION_SERVICE);
|
||||
NotificationChannel channel = notificationManager.getNotificationChannel(RN_ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
|
||||
if (channel != null) {
|
||||
JitsiMeetLogger.i(TAG + " Notification channel already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
channel = new NotificationChannel(
|
||||
RN_ONGOING_CONFERENCE_CHANNEL_ID,
|
||||
context.getString(R.string.ongoing_notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
|
||||
channel.enableVibration(true);
|
||||
channel.setShowBadge(true);
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
JitsiMeetLogger.i(TAG + " Notification channel created with importance: " + channel.getImportance());
|
||||
}
|
||||
|
||||
static Notification buildOngoingConferenceNotification(Context context) {
|
||||
if (context == null) {
|
||||
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
|
||||
return null;
|
||||
}
|
||||
|
||||
JitsiMeetLogger.i(TAG + " Creating notification with context: " + context);
|
||||
|
||||
// Creating an intent to launch app's main activity
|
||||
Intent intent = context.getPackageManager()
|
||||
.getLaunchIntentForPackage(context.getPackageName());
|
||||
assert intent != null;
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
// Creating PendingIntent
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, RN_ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
|
||||
builder
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setContentTitle(context.getString(R.string.ongoing_notification_title))
|
||||
.setContentText(context.getString(R.string.ongoing_notification_text))
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setOngoing(true)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setUsesChronometer(true)
|
||||
.setAutoCancel(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()))
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.setContentIntent(pendingIntent);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
"moment-duration-format": "0.0.0",
|
||||
"optional-require": "0.0.0",
|
||||
"promise.allsettled": "0.0.0",
|
||||
"promise.withresolvers": "0.0.0",
|
||||
"punycode": "0.0.0",
|
||||
"react-emoji-render": "0.0.0",
|
||||
"react-i18next": "0.0.0",
|
||||
|
||||
@@ -14,24 +14,14 @@ module.exports = {
|
||||
project: [ './tsconfig.web.json', './tsconfig.native.json' ]
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
'selector': 'interface',
|
||||
'format': [ 'PascalCase' ],
|
||||
'custom': {
|
||||
'regex': '^I[A-Z]',
|
||||
'match': true
|
||||
}
|
||||
}
|
||||
]
|
||||
// TODO: Remove these and fix the warnings
|
||||
'@typescript-eslint/no-unsafe-function-type': 0,
|
||||
'@typescript-eslint/no-wrapper-object-types': 0,
|
||||
|
||||
'@typescript-eslint/no-require-imports': 0
|
||||
}
|
||||
}
|
||||
],
|
||||
'rules': {
|
||||
// XXX remove this eventually.
|
||||
'react/jsx-indent-props': 0
|
||||
},
|
||||
'settings': {
|
||||
'react': {
|
||||
'version': 'detect'
|
||||
|
||||
@@ -4,8 +4,10 @@ import ReactDOM from 'react-dom';
|
||||
import AlwaysOnTop from './AlwaysOnTop';
|
||||
|
||||
// Render the main/root Component.
|
||||
/* eslint-disable-next-line react/no-deprecated */
|
||||
ReactDOM.render(<AlwaysOnTop />, document.getElementById('react'));
|
||||
|
||||
window.addEventListener(
|
||||
'beforeunload',
|
||||
/* eslint-disable-next-line react/no-deprecated */
|
||||
() => ReactDOM.unmountComponentAtNode(document.getElementById('react') ?? document.body));
|
||||
|
||||
@@ -311,7 +311,7 @@ export function createInviteDialogEvent(
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function createNetworkInfoEvent({ isOnline, networkType, details }:
|
||||
{ details?: Object; isOnline: boolean; networkType?: string; }) {
|
||||
{ details?: Object; isOnline: boolean; networkType?: string; }) {
|
||||
const attributes: {
|
||||
details?: Object;
|
||||
isOnline: boolean;
|
||||
|
||||
@@ -154,6 +154,30 @@ export async function createHandlers({ getState }: IStore) {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a url is a data URL or not.
|
||||
*
|
||||
* @param {string} url - The URL to be checked.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDataURL(url?: string): boolean {
|
||||
if (typeof url !== 'string') { // The icon will be ignored
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const urlObject = new URL(url);
|
||||
|
||||
if (urlObject.protocol === 'data:') {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits JitsiMeetJS.analytics by setting permanent properties and setting the handlers from the loaded scripts.
|
||||
* NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be null.
|
||||
@@ -185,13 +209,21 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
|
||||
inIframe?: boolean;
|
||||
isPromotedFromVisitor?: boolean;
|
||||
isVisitor?: boolean;
|
||||
overwritesCustomButtonsWithURL?: boolean;
|
||||
overwritesCustomParticipantButtonsWithURL?: boolean;
|
||||
overwritesDefaultLogoUrl?: boolean;
|
||||
overwritesDeploymentUrls?: boolean;
|
||||
overwritesEtherpadBase?: boolean;
|
||||
overwritesHosts?: boolean;
|
||||
overwritesIceServers?: boolean;
|
||||
overwritesLiveStreamingUrls?: boolean;
|
||||
overwritesPeopleSearchUrl?: boolean;
|
||||
overwritesPrejoinConfigICEUrl?: boolean;
|
||||
overwritesSalesforceUrl?: boolean;
|
||||
overwritesSupportUrl?: boolean;
|
||||
overwritesWatchRTCConfigParams?: boolean;
|
||||
overwritesWatchRTCProxyUrl?: boolean;
|
||||
overwritesWatchRTCWSUrl?: boolean;
|
||||
server?: string;
|
||||
tenant?: string;
|
||||
wasLobbyVisible?: boolean;
|
||||
@@ -235,6 +267,21 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
|
||||
permanentProperties.overwritesSalesforceUrl = 'config.salesforceUrl' in params;
|
||||
permanentProperties.overwritesPeopleSearchUrl = 'config.peopleSearchUrl' in params;
|
||||
permanentProperties.overwritesDefaultLogoUrl = 'config.defaultLogoUrl' in params;
|
||||
permanentProperties.overwritesEtherpadBase = 'config.etherpad_base' in params;
|
||||
const hosts = params['config.hosts'] ?? {};
|
||||
const hostsProps = [ 'anonymousdomain', 'authdomain', 'domain', 'focus', 'muc', 'visitorFocus' ];
|
||||
|
||||
permanentProperties.overwritesHosts = 'config.hosts' in params
|
||||
|| Boolean(hostsProps.find(p => `config.hosts.${p}` in params || (typeof hosts === 'object' && p in hosts)));
|
||||
|
||||
permanentProperties.overwritesWatchRTCConfigParams = 'config.watchRTCConfigParams' in params;
|
||||
const watchRTCConfigParams = params['config.watchRTCConfigParams'] ?? {};
|
||||
|
||||
permanentProperties.overwritesWatchRTCProxyUrl = ('config.watchRTCConfigParams.proxyUrl' in params)
|
||||
|| (typeof watchRTCConfigParams === 'object' && 'proxyUrl' in watchRTCConfigParams);
|
||||
permanentProperties.overwritesWatchRTCWSUrl = ('config.watchRTCConfigParams.wsUrl' in params)
|
||||
|| (typeof watchRTCConfigParams === 'object' && 'wsUrl' in watchRTCConfigParams);
|
||||
|
||||
const prejoinConfig = params['config.prejoinConfig'] ?? {};
|
||||
|
||||
permanentProperties.overwritesPrejoinConfigICEUrl = ('config.prejoinConfig.preCallTestICEUrl' in params)
|
||||
@@ -260,6 +307,18 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
|
||||
)
|
||||
);
|
||||
|
||||
permanentProperties.overwritesIceServers = Boolean(Object.keys(params).find(k => k.startsWith('iceServers')));
|
||||
|
||||
const customToolbarButtons = params['config.customToolbarButtons'] ?? [];
|
||||
|
||||
permanentProperties.overwritesCustomButtonsWithURL = Boolean(
|
||||
customToolbarButtons.find(({ icon }: { icon: string; }) => isDataURL(icon)));
|
||||
|
||||
const customParticipantMenuButtons = params['config.customParticipantMenuButtons'] ?? [];
|
||||
|
||||
permanentProperties.overwritesCustomParticipantButtonsWithURL = Boolean(
|
||||
customParticipantMenuButtons.find(({ icon }: { icon: string; }) => isDataURL(icon)));
|
||||
|
||||
// Optionally, include local deployment information based on the
|
||||
// contents of window.config.deploymentInfo.
|
||||
if (deploymentInfo) {
|
||||
|
||||
@@ -158,4 +158,3 @@ export function maybeRedirectToTokenAuthUrl(
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,11 +27,9 @@ export interface IProps {
|
||||
*/
|
||||
export class AbstractApp<P extends IProps = IProps> extends BaseApp<P> {
|
||||
/**
|
||||
* The deferred for the initialisation {{promise, resolve, reject}}.
|
||||
* The deferred for the initialization {{promise, resolve, reject}}.
|
||||
*/
|
||||
_init: {
|
||||
promise: Promise<any>;
|
||||
};
|
||||
_init: PromiseWithResolvers<any>;
|
||||
|
||||
/**
|
||||
* Initializes the app.
|
||||
|
||||
@@ -140,10 +140,7 @@ function _getWebWelcomePageRoute(state: IReduxState) {
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _getEmptyRoute(): {
|
||||
component: React.ReactNode;
|
||||
href?: string;
|
||||
} {
|
||||
function _getEmptyRoute(): { component: React.ReactNode; href?: string; } {
|
||||
return {
|
||||
component: BlankPage,
|
||||
href: undefined
|
||||
|
||||
@@ -113,8 +113,7 @@ function _isMaybeSplitBrainError(getState: IStore['getState'], action: AnyAction
|
||||
const { error } = action;
|
||||
const isShardChangedError = error
|
||||
&& error.message === 'item-not-found'
|
||||
&& error.details
|
||||
&& error.details.shard_changed;
|
||||
&& error.details?.shard_changed;
|
||||
|
||||
if (isShardChangedError) {
|
||||
const state = getState();
|
||||
|
||||
@@ -121,7 +121,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
if (isTokenAuthEnabled(config)
|
||||
&& config.tokenAuthUrlAutoRedirect
|
||||
&& state['features/base/jwt'].jwt) {
|
||||
// auto redirect is turned on and we have succesfully logged in
|
||||
// auto redirect is turned on and we have successfully logged in
|
||||
// let's mark that
|
||||
dispatch(setTokenAuthUrlSuccess(true));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import PersistenceRegistry from '../../redux/PersistenceRegistry';
|
||||
import ReducerRegistry from '../../redux/ReducerRegistry';
|
||||
import StateListenerRegistry from '../../redux/StateListenerRegistry';
|
||||
import SoundCollection from '../../sounds/components/SoundCollection';
|
||||
import { createDeferred } from '../../util/helpers';
|
||||
import { appWillMount, appWillUnmount } from '../actions';
|
||||
import logger from '../logger';
|
||||
|
||||
@@ -46,9 +45,7 @@ export default class BaseApp<P> extends Component<P, IState> {
|
||||
/**
|
||||
* The deferred for the initialisation {{promise, resolve, reject}}.
|
||||
*/
|
||||
_init: {
|
||||
promise: Promise<any>;
|
||||
};
|
||||
_init: PromiseWithResolvers<any>;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code BaseApp} instance.
|
||||
@@ -79,7 +76,7 @@ export default class BaseApp<P> extends Component<P, IState> {
|
||||
* @see {@link #_initStorage}
|
||||
* @type {Promise}
|
||||
*/
|
||||
this._init = createDeferred<void>();
|
||||
this._init = Promise.withResolvers();
|
||||
|
||||
try {
|
||||
await this._initStorage();
|
||||
|
||||
@@ -248,8 +248,7 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
}
|
||||
|
||||
!error.recoverable
|
||||
&& conference
|
||||
&& conference.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch((reason: Error) => {
|
||||
&& conference?.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch((reason: Error) => {
|
||||
// Even though we don't care too much about the failure, it may be
|
||||
// good to know that it happen, so log it (on the info level).
|
||||
logger.info('JitsiConference.leave() rejected with:', reason);
|
||||
@@ -430,7 +429,7 @@ function _connectionFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
} as INotificationProps;
|
||||
|
||||
const { locationURL = { href: '' } as URL } = getState()['features/base/connection'];
|
||||
const { tenant } = parseURIString(locationURL.href) || {};
|
||||
const { tenant = '' } = parseURIString(locationURL.href) || {};
|
||||
|
||||
if (tenant.startsWith('-') || tenant.endsWith('-')) {
|
||||
notificationProps.descriptionKey = 'notify.invalidTenantHyphenDescription';
|
||||
@@ -551,7 +550,7 @@ function _pinParticipant({ getState }: IStore, next: Function, action: AnyAction
|
||||
const actionName = id ? ACTION_PINNED : ACTION_UNPINNED;
|
||||
const local
|
||||
= participantById?.local
|
||||
|| (!id && pinnedParticipant && pinnedParticipant.local);
|
||||
|| (!id && pinnedParticipant?.local);
|
||||
let participantIdForEvent;
|
||||
|
||||
if (local) {
|
||||
|
||||
@@ -495,7 +495,7 @@ function _conferenceJoined(state: IConferenceState, { conference }: { conference
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _conferenceLeftOrWillLeave(state: IConferenceState, { conference, type }:
|
||||
{ conference: IJitsiConference; type: string; }) {
|
||||
{ conference: IJitsiConference; type: string; }) {
|
||||
const nextState = { ...state };
|
||||
|
||||
// The redux action CONFERENCE_LEFT is the last time that we should be
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ToolbarButton } from '../../toolbox/types';
|
||||
import { ILoggingConfig } from '../logging/types';
|
||||
import { DesktopSharingSourceType } from '../tracks/types';
|
||||
|
||||
type ButtonsWithNotifyClick = 'camera' |
|
||||
'chat' |
|
||||
@@ -215,6 +217,10 @@ export interface IConfig {
|
||||
hideAutoAssignButton?: boolean;
|
||||
hideJoinRoomButton?: boolean;
|
||||
};
|
||||
bridgeChannel?: {
|
||||
ignoreDomain?: string;
|
||||
preferSctp?: boolean;
|
||||
};
|
||||
buttonsWithNotifyClick?: Array<ButtonsWithNotifyClick | {
|
||||
key: ButtonsWithNotifyClick;
|
||||
preventExecution: boolean;
|
||||
@@ -275,11 +281,15 @@ export interface IConfig {
|
||||
max?: number;
|
||||
min?: number;
|
||||
};
|
||||
desktopSharingSources?: Array<DesktopSharingSourceType>;
|
||||
dialInConfCodeUrl?: string;
|
||||
dialInNumbersUrl?: string;
|
||||
dialOutAuthUrl?: string;
|
||||
dialOutRegionUrl?: string;
|
||||
disable1On1Mode?: boolean | null;
|
||||
disableAEC?: boolean;
|
||||
disableAGC?: boolean;
|
||||
disableAP?: boolean;
|
||||
disableAddingBackgroundImages?: boolean;
|
||||
disableAudioLevels?: boolean;
|
||||
disableBeforeUnloadHandlers?: boolean;
|
||||
@@ -294,11 +304,13 @@ export interface IConfig {
|
||||
disableJoinLeaveSounds?: boolean;
|
||||
disableLocalVideoFlip?: boolean;
|
||||
disableModeratorIndicator?: boolean;
|
||||
disableNS?: boolean;
|
||||
disablePolls?: boolean;
|
||||
disableProfile?: boolean;
|
||||
disableReactions?: boolean;
|
||||
disableReactionsModeration?: boolean;
|
||||
disableRecordAudioNotification?: boolean;
|
||||
disableRemoteControl?: boolean;
|
||||
disableRemoteMute?: boolean;
|
||||
disableRemoveRaisedHandOnFocus?: boolean;
|
||||
disableResponsiveTiles?: boolean;
|
||||
@@ -316,6 +328,7 @@ export interface IConfig {
|
||||
disableVirtualBackground?: boolean;
|
||||
disabledNotifications?: Array<string>;
|
||||
disabledSounds?: Array<Sounds>;
|
||||
displayJids?: boolean;
|
||||
doNotFlipLocalVideo?: boolean;
|
||||
doNotStoreRoom?: boolean;
|
||||
dropbox?: {
|
||||
@@ -345,7 +358,6 @@ export interface IConfig {
|
||||
maxMessagesPerSecond?: number;
|
||||
numRequests?: number;
|
||||
};
|
||||
enableAutomaticUrlCopy?: boolean;
|
||||
enableCalendarIntegration?: boolean;
|
||||
enableClosePage?: boolean;
|
||||
enableDisplayNameInStats?: boolean;
|
||||
@@ -359,6 +371,7 @@ export interface IConfig {
|
||||
enableOpusRed?: boolean;
|
||||
enableRemb?: boolean;
|
||||
enableSaveLogs?: boolean;
|
||||
enableTalkWhileMuted?: boolean;
|
||||
enableTcc?: boolean;
|
||||
enableWebHIDFeature?: boolean;
|
||||
enableWelcomePage?: boolean;
|
||||
@@ -372,7 +385,6 @@ export interface IConfig {
|
||||
faceCenteringThreshold?: number;
|
||||
};
|
||||
feedbackPercentage?: number;
|
||||
fileRecordingsEnabled?: boolean;
|
||||
fileRecordingsServiceEnabled?: boolean;
|
||||
fileRecordingsServiceSharingEnabled?: boolean;
|
||||
filmstrip?: {
|
||||
@@ -382,11 +394,11 @@ export interface IConfig {
|
||||
disabled?: boolean;
|
||||
minParticipantCountForTopPanel?: number;
|
||||
};
|
||||
firefox_fake_device?: string;
|
||||
flags?: {
|
||||
ssrcRewritingEnabled: boolean;
|
||||
};
|
||||
focusUserJid?: string;
|
||||
forceTurnRelay?: boolean;
|
||||
gatherStats?: boolean;
|
||||
giphy?: {
|
||||
displayMode?: 'all' | 'tile' | 'chat';
|
||||
@@ -426,6 +438,7 @@ export interface IConfig {
|
||||
};
|
||||
iAmRecorder?: boolean;
|
||||
iAmSipGateway?: boolean;
|
||||
ignoreStartMuted?: boolean;
|
||||
inviteAppName?: string | null;
|
||||
inviteServiceCallFlowsUrl?: string;
|
||||
inviteServiceUrl?: string;
|
||||
@@ -458,6 +471,7 @@ export interface IConfig {
|
||||
};
|
||||
localSubject?: string;
|
||||
locationURL?: URL;
|
||||
logging?: ILoggingConfig;
|
||||
mainToolbarButtons?: Array<Array<string>>;
|
||||
maxFullResolutionParticipants?: number;
|
||||
microsoftApiApplicationClientID?: string;
|
||||
@@ -479,6 +493,7 @@ export interface IConfig {
|
||||
enabled?: boolean;
|
||||
iceTransportPolicy?: string;
|
||||
mobileCodecPreferenceOrder?: Array<string>;
|
||||
mobileScreenshareCodec?: string;
|
||||
stunServers?: Array<{ urls: string; }>;
|
||||
};
|
||||
participantMenuButtonsWithNotifyClick?: Array<string | ParticipantMenuButtonsWithNotifyClick | {
|
||||
@@ -571,7 +586,9 @@ export interface IConfig {
|
||||
subject?: string;
|
||||
testing?: {
|
||||
assumeBandwidth?: boolean;
|
||||
debugAudioLevels?: boolean;
|
||||
dumpTranscript?: boolean;
|
||||
failICE?: boolean;
|
||||
noAutoPlayVideo?: boolean;
|
||||
p2pTestMode?: boolean;
|
||||
skipInterimTranscriptions?: boolean;
|
||||
@@ -619,6 +636,13 @@ export interface IConfig {
|
||||
mobileCodecPreferenceOrder?: Array<string>;
|
||||
persist?: boolean;
|
||||
};
|
||||
visitors?: {
|
||||
enableMediaOnPromote?: {
|
||||
audio?: boolean;
|
||||
video?: boolean;
|
||||
};
|
||||
queueService: string;
|
||||
};
|
||||
watchRTCConfigParams?: IWatchRTCConfiguration;
|
||||
webhookProxyUrl?: string;
|
||||
webrtcIceTcpDisable?: boolean;
|
||||
|
||||
@@ -22,6 +22,7 @@ export default [
|
||||
'apiLogLevels',
|
||||
'avgRtpStatsN',
|
||||
'backgroundAlpha',
|
||||
'brandingRoomAlias',
|
||||
'breakoutRooms',
|
||||
'bridgeChannel',
|
||||
'buttonsWithNotifyClick',
|
||||
@@ -76,10 +77,6 @@ export default [
|
||||
'channelLastN',
|
||||
'connectionIndicators',
|
||||
'constraints',
|
||||
'customToolbarButtons',
|
||||
'brandingRoomAlias',
|
||||
'debug',
|
||||
'debugAudioLevels',
|
||||
'deeplinking.disabled',
|
||||
'deeplinking.desktop.enabled',
|
||||
'defaultLocalDisplayName',
|
||||
@@ -99,7 +96,6 @@ export default [
|
||||
'disabledSounds',
|
||||
'disableFilmstripAutohiding',
|
||||
'disableInitialGUM',
|
||||
'disableHPF',
|
||||
'disableInviteFunctions',
|
||||
'disableIncomingMessageSound',
|
||||
'disableJoinLeaveSounds',
|
||||
@@ -148,14 +144,9 @@ export default [
|
||||
'enableNoAudioDetection',
|
||||
'enableNoisyMicDetection',
|
||||
'enableTcc',
|
||||
'enableAutomaticUrlCopy',
|
||||
'etherpad_base',
|
||||
'faceLandmarks',
|
||||
'failICE',
|
||||
'feedbackPercentage',
|
||||
'fileRecordingsEnabled',
|
||||
'filmstrip',
|
||||
'firefox_fake_device',
|
||||
'flags',
|
||||
'forceTurnRelay',
|
||||
'gatherStats',
|
||||
@@ -172,7 +163,6 @@ export default [
|
||||
'hideAddRoomButton',
|
||||
'hideEmailInSettings',
|
||||
'hideLobbyButton',
|
||||
'hosts',
|
||||
'iAmRecorder',
|
||||
'iAmSipGateway',
|
||||
'ignoreStartMuted',
|
||||
@@ -195,6 +185,7 @@ export default [
|
||||
'p2p.enabled',
|
||||
'p2p.iceTransportPolicy',
|
||||
'p2p.mobileCodecPreferenceOrder',
|
||||
'p2p.mobileScreenshareCodec',
|
||||
'participantMenuButtonsWithNotifyClick',
|
||||
'participantsPane',
|
||||
'pcStatsInterval',
|
||||
@@ -235,8 +226,18 @@ export default [
|
||||
'useHostPageLocalStorage',
|
||||
'useTurnUdp',
|
||||
'videoQuality',
|
||||
'visitors',
|
||||
'watchRTCConfigParams',
|
||||
'visitors.enableMediaOnPromote',
|
||||
'watchRTCConfigParams.allowBrowserLogCollection',
|
||||
'watchRTCConfigParams.collectionInterval',
|
||||
'watchRTCConfigParams.console',
|
||||
'watchRTCConfigParams.debug',
|
||||
'watchRTCConfigParams.keys',
|
||||
'watchRTCConfigParams.logGetStats',
|
||||
'watchRTCConfigParams.rtcApiKey',
|
||||
'watchRTCConfigParams.rtcPeerId',
|
||||
'watchRTCConfigParams.rtcRoomId',
|
||||
'watchRTCConfigParams.rtcTags',
|
||||
'watchRTCConfigParams.rtcToken',
|
||||
'webrtcIceTcpDisable',
|
||||
'webrtcIceUdpDisable',
|
||||
'whiteboard.enabled'
|
||||
|
||||
@@ -89,7 +89,7 @@ export function _setDeeplinkingDefaults(deeplinking: IDeeplinkingConfig) {
|
||||
android.downloadLink = android.downloadLink
|
||||
|| 'https://play.google.com/store/apps/details?id=org.jitsi.meet';
|
||||
android.appPackage = android.appPackage || 'org.jitsi.meet';
|
||||
android.fDroidUrl = android.fDroidUrl || 'https://f-droid.org/en/packages/org.jitsi.meet/';
|
||||
android.fDroidUrl = android.fDroidUrl || 'https://f-droid.org/packages/org.jitsi.meet/';
|
||||
if (android.dynamicLink) {
|
||||
android.dynamicLink.apn = android.dynamicLink.apn || 'org.jitsi.meet';
|
||||
android.dynamicLink.appCode = android.dynamicLink.appCode || 'w2atb';
|
||||
|
||||
@@ -22,15 +22,12 @@ export default [
|
||||
'DISABLE_DOMINANT_SPEAKER_INDICATOR',
|
||||
'DISABLE_FOCUS_INDICATOR',
|
||||
'DISABLE_PRIVATE_MESSAGES',
|
||||
'DISABLE_RINGING',
|
||||
'DISABLE_TRANSCRIPTION_SUBTITLES',
|
||||
'DISABLE_VIDEO_BACKGROUND',
|
||||
'DISPLAY_WELCOME_PAGE_CONTENT',
|
||||
'ENABLE_DIAL_OUT',
|
||||
'ENABLE_FEEDBACK_ANIMATION',
|
||||
'FILM_STRIP_MAX_HEIGHT',
|
||||
'GENERATE_ROOMNAMES_ON_WELCOME_PAGE',
|
||||
'HIDE_INVITE_MORE_HEADER',
|
||||
'INDICATOR_FONT_SIZES',
|
||||
'INITIAL_TOOLBAR_TIMEOUT',
|
||||
'LANG_DETECTION',
|
||||
|
||||
@@ -63,7 +63,6 @@ export interface IConfigState extends IConfig {
|
||||
analysis?: {
|
||||
obfuscateRoomName?: boolean;
|
||||
};
|
||||
disableRemoteControl?: boolean;
|
||||
error?: Error;
|
||||
oldConfig?: {
|
||||
bosh?: string;
|
||||
@@ -75,13 +74,6 @@ export interface IConfigState extends IConfig {
|
||||
p2p?: object;
|
||||
websocket?: string;
|
||||
};
|
||||
visitors?: {
|
||||
enableMediaOnPromote?: {
|
||||
audio?: boolean;
|
||||
video?: boolean;
|
||||
};
|
||||
queueService: string;
|
||||
};
|
||||
}
|
||||
|
||||
ReducerRegistry.register<IConfigState>('features/base/config', (state = _getInitialState(), action): IConfigState => {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/**
|
||||
* Prefixes of devices that will be filtered from the device list.
|
||||
*
|
||||
* NOTE: Currently we filter only 'Microsoft Teams Audio Device' virtual device. It seems that it can't be set
|
||||
* NOTE: It seems that the filtered devices can't be set
|
||||
* as default device on the OS level and this use case is not handled in the code. If we add more device prefixes that
|
||||
* can be default devices we should make sure to handle the default device use case.
|
||||
*/
|
||||
export const DEVICE_LABEL_PREFIXES_TO_IGNORE = [ 'Microsoft Teams Audio Device' ];
|
||||
export const DEVICE_LABEL_PREFIXES_TO_IGNORE = [
|
||||
'Microsoft Teams Audio Device',
|
||||
'ZoomAudioDevice'
|
||||
];
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
OPEN_SHEET
|
||||
} from './actionTypes';
|
||||
import { isDialogOpen } from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* Signals Dialog to close its dialog.
|
||||
@@ -23,6 +24,8 @@ import { isDialogOpen } from './functions';
|
||||
* }}
|
||||
*/
|
||||
export function hideDialog(component?: ComponentType<any>) {
|
||||
logger.info(`Hide dialog: ${getComponentDisplayName(component)}`);
|
||||
|
||||
return {
|
||||
type: HIDE_DIALOG,
|
||||
component
|
||||
@@ -55,6 +58,8 @@ export function hideSheet() {
|
||||
* }}
|
||||
*/
|
||||
export function openDialog(component: ComponentType<any>, componentProps?: Object) {
|
||||
logger.info(`Open dialog: ${getComponentDisplayName(component)}`);
|
||||
|
||||
return {
|
||||
type: OPEN_DIALOG,
|
||||
component,
|
||||
@@ -101,3 +106,21 @@ export function toggleDialog(component: ComponentType<any>, componentProps?: Obj
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a printable name for a dialog component.
|
||||
*
|
||||
* @param {Object} component - The component to extract the name for.
|
||||
*
|
||||
* @returns {string} The display name.
|
||||
*/
|
||||
function getComponentDisplayName(component?: ComponentType<any>) {
|
||||
if (!component) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const name = component.displayName ?? component.name ?? 'Component';
|
||||
|
||||
return name.replace('withI18nextTranslation(Connect(', '') // dialogs with translations
|
||||
.replace('))', ''); // dialogs with translations suffix
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
? action.value
|
||||
: store.getState()['features/dynamic-branding'];
|
||||
|
||||
if (language && labels && labels[language]) {
|
||||
if (language && labels?.[language]) {
|
||||
changeLanguageBundle(language, labels[language])
|
||||
.catch(err => {
|
||||
logger.log('Error setting dynamic language bundle', err);
|
||||
|
||||
@@ -237,7 +237,7 @@ function _undoOverwriteLocalParticipant(
|
||||
* }}
|
||||
*/
|
||||
function _user2participant({ avatar, avatarUrl, email, id, name, 'hidden-from-recorder': hiddenFromRecorder }:
|
||||
{ avatar?: string; avatarUrl?: string; email: string; 'hidden-from-recorder': string | boolean;
|
||||
{ avatar?: string; avatarUrl?: string; email: string; 'hidden-from-recorder': string | boolean;
|
||||
id: string; name: string; }) {
|
||||
const participant: {
|
||||
avatarURL?: string;
|
||||
|
||||
@@ -25,10 +25,6 @@ export function createLocalTrack(type: string, deviceId: string | null, timeout?
|
||||
JitsiMeetJS.createLocalTracks({
|
||||
cameraDeviceId: deviceId,
|
||||
devices: [ type ],
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
firefox_fake_device:
|
||||
window.config?.firefox_fake_device,
|
||||
micDeviceId: deviceId,
|
||||
timeout,
|
||||
...additionalOptions
|
||||
|
||||
@@ -5,10 +5,11 @@ import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
import { equals, set } from '../redux/functions';
|
||||
|
||||
import { SET_LOGGING_CONFIG, SET_LOG_COLLECTOR } from './actionTypes';
|
||||
import { ILoggingConfig, LogLevel } from './types';
|
||||
|
||||
const DEFAULT_LOGGING_CONFIG = {
|
||||
const DEFAULT_LOGGING_CONFIG: ILoggingConfig = {
|
||||
// default log level for the app and lib-jitsi-meet
|
||||
defaultLogLevel: 'trace' as LogLevel,
|
||||
defaultLogLevel: 'trace',
|
||||
|
||||
// Option to disable LogCollector (which stores the logs)
|
||||
// disableLogCollector: true,
|
||||
@@ -16,8 +17,8 @@ const DEFAULT_LOGGING_CONFIG = {
|
||||
loggers: {
|
||||
// The following are too verbose in their logging with the
|
||||
// {@link #defaultLogLevel}:
|
||||
'modules/RTC/TraceablePeerConnection.js': 'info' as LogLevel,
|
||||
'modules/xmpp/strophe.util.js': 'log' as LogLevel
|
||||
'modules/RTC/TraceablePeerConnection.js': 'info',
|
||||
'modules/xmpp/strophe.util.js': 'log'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -39,11 +40,11 @@ const DEFAULT_STATE = {
|
||||
|
||||
// Reduce default verbosity on mobile, it kills performance.
|
||||
if (navigator.product === 'ReactNative') {
|
||||
const RN_LOGGERS = {
|
||||
'modules/sdp/SDPUtil.js': 'info' as LogLevel,
|
||||
'modules/xmpp/ChatRoom.js': 'warn' as LogLevel,
|
||||
'modules/xmpp/JingleSessionPC.js': 'info' as LogLevel,
|
||||
'modules/xmpp/strophe.jingle.js': 'info' as LogLevel
|
||||
const RN_LOGGERS: { [key: string]: LogLevel; } = {
|
||||
'modules/sdp/SDPUtil.js': 'info',
|
||||
'modules/xmpp/ChatRoom.js': 'warn',
|
||||
'modules/xmpp/JingleSessionPC.js': 'info',
|
||||
'modules/xmpp/strophe.jingle.js': 'info'
|
||||
};
|
||||
|
||||
DEFAULT_STATE.config.loggers = {
|
||||
@@ -52,16 +53,8 @@ if (navigator.product === 'ReactNative') {
|
||||
};
|
||||
}
|
||||
|
||||
type LogLevel = 'trace' | 'log' | 'info' | 'warn' | 'error';
|
||||
|
||||
export interface ILoggingState {
|
||||
config: {
|
||||
defaultLogLevel: LogLevel;
|
||||
disableLogCollector?: boolean;
|
||||
loggers: {
|
||||
[key: string]: LogLevel;
|
||||
};
|
||||
};
|
||||
config: ILoggingConfig;
|
||||
logCollector?: {
|
||||
flush: () => void;
|
||||
start: () => void;
|
||||
|
||||
9
react/features/base/logging/types.ts
Normal file
9
react/features/base/logging/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export type LogLevel = 'trace' | 'log' | 'info' | 'warn' | 'error';
|
||||
|
||||
export interface ILoggingConfig {
|
||||
defaultLogLevel?: LogLevel;
|
||||
disableLogCollector?: boolean;
|
||||
loggers?: {
|
||||
[key: string]: LogLevel;
|
||||
};
|
||||
}
|
||||
@@ -228,7 +228,7 @@ class AudioTrack extends Component<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_detachTrack(track?: ITrack) {
|
||||
if (this._ref?.current && track && track.jitsiTrack) {
|
||||
if (this._ref?.current && track?.jitsiTrack) {
|
||||
clearTimeout(this._playTimeout);
|
||||
this._playTimeout = undefined;
|
||||
track.jitsiTrack.detach(this._ref.current);
|
||||
|
||||
@@ -358,7 +358,7 @@ class Video extends Component<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_detachTrack(videoTrack?: Partial<ITrack>) {
|
||||
if (this._videoElement && videoTrack && videoTrack.jitsiTrack) {
|
||||
if (this._videoElement && videoTrack?.jitsiTrack) {
|
||||
videoTrack.jitsiTrack.detach(this._videoElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ export const MEDIA_TYPE: {
|
||||
AUDIO: MediaType;
|
||||
SCREENSHARE: MediaType;
|
||||
VIDEO: MediaType;
|
||||
} = {
|
||||
AUDIO: 'audio',
|
||||
SCREENSHARE: 'screenshare',
|
||||
VIDEO: 'video'
|
||||
};
|
||||
} = {
|
||||
AUDIO: 'audio',
|
||||
SCREENSHARE: 'screenshare',
|
||||
VIDEO: 'video'
|
||||
};
|
||||
|
||||
|
||||
/* eslint-disable no-bitwise */
|
||||
|
||||
@@ -3,7 +3,6 @@ import { AnyAction, combineReducers } from 'redux';
|
||||
import { CONFERENCE_FAILED, CONFERENCE_LEFT } from '../conference/actionTypes';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
import { TRACK_REMOVED } from '../tracks/actionTypes';
|
||||
import { DefferedPromise, createDeferred } from '../util/helpers';
|
||||
|
||||
import {
|
||||
GUM_PENDING,
|
||||
@@ -93,17 +92,18 @@ function _audio(state: IAudioState = _AUDIO_INITIAL_MEDIA_STATE, action: AnyActi
|
||||
// initial track creation haven't been started we would wait for it to finish before starting to join the room.
|
||||
// NOTE: The previous implementation was using the GUM promise from conference.init. But it turned out that connect
|
||||
// may finish even before conference.init is executed.
|
||||
const DEFAULT_INITIAL_PROMISE_STATE = createDeferred<IInitialGUMPromiseResult>();
|
||||
const DEFAULT_INITIAL_PROMISE_STATE = Promise.withResolvers<IInitialGUMPromiseResult>();
|
||||
|
||||
/**
|
||||
* Reducer fot the common properties in media state.
|
||||
* Reducer for the common properties in media state.
|
||||
*
|
||||
* @param {ICommonState} state - Common media state.
|
||||
* @param {Object} action - Action object.
|
||||
* @param {string} action.type - Type of action.
|
||||
* @returns {ICommonState}
|
||||
*/
|
||||
function _initialGUMPromise(state: DefferedPromise<IInitialGUMPromiseResult> | null = DEFAULT_INITIAL_PROMISE_STATE,
|
||||
function _initialGUMPromise(
|
||||
state: PromiseWithResolvers<IInitialGUMPromiseResult> | null = DEFAULT_INITIAL_PROMISE_STATE,
|
||||
action: AnyAction) {
|
||||
if (action.type === SET_INITIAL_GUM_PROMISE) {
|
||||
return action.promise ?? null;
|
||||
@@ -294,7 +294,7 @@ interface IVideoState {
|
||||
|
||||
export interface IMediaState {
|
||||
audio: IAudioState;
|
||||
initialGUMPromise: DefferedPromise<IInitialGUMPromiseResult> | null;
|
||||
initialGUMPromise: PromiseWithResolvers<IInitialGUMPromiseResult> | null;
|
||||
screenshare: IScreenshareState;
|
||||
video: IVideoState;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ export function participantJoined(participant: IParticipant) {
|
||||
// conference. The following check is really necessary because a
|
||||
// JitsiConference may have moved into leaving but may still manage to
|
||||
// sneak a PARTICIPANT_JOINED in if its leave is delayed for any purpose
|
||||
// (which is not outragous given that leaving involves network
|
||||
// (which is not outrageous given that leaving involves network
|
||||
// requests.)
|
||||
const stateFeaturesBaseConference
|
||||
= getState()['features/base/conference'];
|
||||
|
||||
@@ -15,7 +15,6 @@ import i18next from '../i18n/i18next';
|
||||
import { MEDIA_TYPE, MediaType, VIDEO_TYPE } from '../media/constants';
|
||||
import { toState } from '../redux/functions';
|
||||
import { getScreenShareTrack, isLocalTrackMuted } from '../tracks/functions.any';
|
||||
import { createDeferred } from '../util/helpers';
|
||||
|
||||
import {
|
||||
JIGASI_PARTICIPANT_ICON,
|
||||
@@ -127,7 +126,7 @@ export function getActiveSpeakersToBeDisplayed(stateful: IStateful) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getFirstLoadableAvatarUrl(participant: IParticipant, store: IStore) {
|
||||
const deferred: any = createDeferred();
|
||||
const deferred: any = Promise.withResolvers();
|
||||
const fullPromise = deferred.promise
|
||||
.then(() => _getFirstLoadableAvatarUrl(participant, store))
|
||||
.then((result: any) => {
|
||||
|
||||
@@ -52,7 +52,7 @@ StateListenerRegistry.register(
|
||||
|
||||
/**
|
||||
* Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
|
||||
* tiles accodingly.
|
||||
* tiles accordingly.
|
||||
*
|
||||
* @param {Array<string>} oldScreenshareSourceNames - List of old screenshare source names.
|
||||
* @param {Array<string>} newScreenshareSourceNames - Current list of screenshare source names.
|
||||
|
||||
@@ -340,9 +340,7 @@ class Popover extends Component<IProps, IState> {
|
||||
_onTouchStart(event: TouchEvent) {
|
||||
if (this.props.visible
|
||||
&& !this.props.overflowDrawer
|
||||
&& this._contextMenuRef
|
||||
&& this._contextMenuRef.contains
|
||||
&& !this._contextMenuRef.contains(event.target as Node)
|
||||
&& !this._contextMenuRef?.contains?.(event.target as Node)
|
||||
&& !this._containerRef?.current?.contains(event.target as Node)) {
|
||||
this._onHideDialog();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { ReactNode } from 'react';
|
||||
import React, { ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
@@ -11,6 +11,7 @@ import { isButtonEnabled } from '../../../../toolbox/functions.web';
|
||||
import { getConferenceName } from '../../../conference/functions';
|
||||
import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from '../../../config/constants';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
import Tooltip from '../../../tooltip/components/Tooltip';
|
||||
import { isPreCallTestEnabled } from '../../functions';
|
||||
|
||||
import ConnectionStatus from './ConnectionStatus';
|
||||
@@ -160,15 +161,20 @@ const useStyles = makeStyles()(theme => {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
roomNameContainer: {
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
marginBottom: theme.spacing(4)
|
||||
},
|
||||
|
||||
roomName: {
|
||||
...withPixelLineHeight(theme.typography.heading5),
|
||||
color: theme.palette.text01,
|
||||
marginBottom: theme.spacing(4),
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
textAlign: 'center',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
width: '100%'
|
||||
maxWidth: '100%',
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -195,6 +201,19 @@ const PreMeetingScreen = ({
|
||||
backgroundSize: 'cover'
|
||||
} : {};
|
||||
|
||||
const roomNameRef = useRef<HTMLSpanElement | null>(null);
|
||||
const [ isOverflowing, setIsOverflowing ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (roomNameRef.current) {
|
||||
const element = roomNameRef.current;
|
||||
const elementStyles = window.getComputedStyle(element);
|
||||
const elementWidth = Math.floor(parseFloat(elementStyles.width));
|
||||
|
||||
setIsOverflowing(element.scrollWidth > elementWidth + 1);
|
||||
}
|
||||
}, [ _roomName ]);
|
||||
|
||||
return (
|
||||
<div className = { clsx('premeeting-screen', classes.container, className) }>
|
||||
<div style = { style }>
|
||||
@@ -206,8 +225,22 @@ const PreMeetingScreen = ({
|
||||
{title}
|
||||
</h1>
|
||||
{_roomName && (
|
||||
<span className = { classes.roomName }>
|
||||
{_roomName}
|
||||
<span className = { classes.roomNameContainer }>
|
||||
{isOverflowing ? (
|
||||
<Tooltip content = { _roomName }>
|
||||
<span
|
||||
className = { classes.roomName }
|
||||
ref = { roomNameRef }>
|
||||
{_roomName}
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span
|
||||
className = { classes.roomName }
|
||||
ref = { roomNameRef }>
|
||||
{_roomName}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{children}
|
||||
@@ -259,3 +292,4 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(PreMeetingScreen);
|
||||
|
||||
|
||||
@@ -227,14 +227,14 @@ class MeetingsList extends Component<IProps> {
|
||||
<Container
|
||||
className = { rootClassName }
|
||||
key = { index }
|
||||
onClick = { onPress }>
|
||||
onClick = { onPress }
|
||||
tabIndex = { 0 }>
|
||||
<Container className = 'right-column'>
|
||||
<Text
|
||||
className = 'title'
|
||||
onClick = { onPress }
|
||||
onKeyPress = { onKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
role = 'button'>
|
||||
{ title }
|
||||
</Text>
|
||||
{
|
||||
|
||||
@@ -41,8 +41,8 @@ export function formatURLText(text = '') {
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
// This will be the case for invalid URLs or URLs without a host (emails for example). In this case beacuse
|
||||
// of the issue with PunycodeJS that truncates parts of the text when there is '@' we split the text by '@'
|
||||
// This will be the case for invalid URLs or URLs without a host (emails for example). In this case due to
|
||||
// the issue with PunycodeJS that truncates parts of the text when there is '@' we split the text by '@'
|
||||
// and use punycode for every separate part to prevent homograph attacks.
|
||||
result = text.split('@').map(punycode.toASCII)
|
||||
.join('@');
|
||||
@@ -59,7 +59,7 @@ export function formatURLText(text = '') {
|
||||
*/
|
||||
export function getSupportUrl(stateful: IStateful) {
|
||||
|
||||
// TODO: Once overwriting trough interface config is completelly gone we should think of a way to be able to set
|
||||
// TODO: Once overwriting through interface config is completely gone we should think of a way to be able to set
|
||||
// the value in the branding and not return the default value from interface config.
|
||||
return toState(stateful)['features/dynamic-branding'].supportUrl || interfaceConfig?.SUPPORT_URL;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ declare let __DEV__: any;
|
||||
/**
|
||||
* Mixed type of the element (subtree) config. If it's a {@code boolean} (and is
|
||||
* {@code true}), we persist the entire subtree. If it's an {@code Object}, we
|
||||
* perist a filtered subtree based on the properties of the config object.
|
||||
* persist a filtered subtree based on the properties of the config object.
|
||||
*/
|
||||
declare type ElementConfig = boolean | Object;
|
||||
|
||||
|
||||
@@ -96,8 +96,8 @@ class StateListenerRegistry {
|
||||
* @returns {void}
|
||||
*/
|
||||
_listener({ prevSelections, store }: {
|
||||
prevSelections: Map<SelectorListener, any>;
|
||||
store: Store<any, any>;
|
||||
prevSelections: Map<SelectorListener, any>;
|
||||
store: Store<any, any>;
|
||||
}) {
|
||||
for (const selectorListener of this._selectorListeners) {
|
||||
const prevSelection = prevSelections.get(selectorListener);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { setAspectRatio, setReducedUI } from './actions';
|
||||
|
||||
|
||||
/**
|
||||
* Middleware that handles widnow dimension changes and updates the aspect ratio and
|
||||
* Middleware that handles window dimension changes and updates the aspect ratio and
|
||||
* reduced UI modes accordingly.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
|
||||
@@ -375,7 +375,7 @@ export default class AbstractButton<P extends IProps, S = any> extends Component
|
||||
|
||||
// blur after click to release focus from button to allow PTT.
|
||||
// @ts-ignore
|
||||
e?.currentTarget?.blur && e.currentTarget.blur();
|
||||
e?.currentTarget?.blur?.();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ export * from './functions.any';
|
||||
* and/or 'video'.
|
||||
* @param {string|null} [options.micDeviceId] - Microphone device id or
|
||||
* {@code undefined} to use app's settings.
|
||||
* @param {number|undefined} [oprions.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
|
||||
* @param {number|undefined} [options.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
|
||||
* @param {IStore} store - The redux store in the context of which the function
|
||||
* is to execute and from which state such as {@code config} is to be retrieved.
|
||||
* @returns {Promise<JitsiLocalTrack[]>}
|
||||
|
||||
@@ -31,7 +31,7 @@ export * from './functions.any';
|
||||
* and/or 'video'.
|
||||
* @param {string|null} [options.micDeviceId] - Microphone device id or
|
||||
* {@code undefined} to use app's settings.
|
||||
* @param {number|undefined} [oprions.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
|
||||
* @param {number|undefined} [options.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
|
||||
* @param {IStore} store - The redux store in the context of which the function
|
||||
* is to execute and from which state such as {@code config} is to be retrieved.
|
||||
* @param {boolean} recordTimeMetrics - If true time metrics will be recorded.
|
||||
@@ -60,7 +60,6 @@ export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore,
|
||||
|
||||
const {
|
||||
desktopSharingFrameRate,
|
||||
firefox_fake_device, // eslint-disable-line camelcase
|
||||
resolution
|
||||
} = state['features/base/config'];
|
||||
const constraints = options.constraints ?? state['features/base/config'].constraints;
|
||||
@@ -86,7 +85,6 @@ export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore,
|
||||
devices: options.devices?.slice(0),
|
||||
effects,
|
||||
facingMode: options.facingMode || getCameraFacingMode(state),
|
||||
firefox_fake_device, // eslint-disable-line camelcase
|
||||
micDeviceId,
|
||||
resolution,
|
||||
timeout
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface ITrackOptions {
|
||||
};
|
||||
};
|
||||
desktopSharingSourceDevice?: string;
|
||||
desktopSharingSources?: string[];
|
||||
desktopSharingSources?: Array<DesktopSharingSourceType>;
|
||||
devices?: string[];
|
||||
facingMode?: string;
|
||||
micDeviceId?: string | null;
|
||||
@@ -67,9 +67,11 @@ export interface IToggleScreenSharingOptions {
|
||||
shareOptions: IShareOptions;
|
||||
}
|
||||
|
||||
export type DesktopSharingSourceType = 'screen' | 'window';
|
||||
|
||||
export interface IShareOptions {
|
||||
desktopSharingSourceDevice?: string;
|
||||
desktopSharingSources?: string[];
|
||||
desktopSharingSources?: Array<DesktopSharingSourceType>;
|
||||
desktopStream?: any;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,9 +244,9 @@ const ContextMenu = ({
|
||||
list: Element | null,
|
||||
currentFocus: Element | null,
|
||||
traversalFunction: (
|
||||
list: Element | null,
|
||||
currentFocus: Element | null
|
||||
) => Element | null
|
||||
list: Element | null,
|
||||
currentFocus: Element | null
|
||||
) => Element | null
|
||||
) => {
|
||||
let wrappedOnce = false;
|
||||
let nextFocus = traversalFunction(list, currentFocus);
|
||||
|
||||
@@ -141,7 +141,7 @@ const useStyles = makeStyles()(theme => {
|
||||
});
|
||||
|
||||
interface IObject {
|
||||
[key: string]: string | string[] | boolean | number | number[] | {} | undefined;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface IDialogTab<P> {
|
||||
|
||||
@@ -98,6 +98,9 @@ const useStyles = makeStyles()(theme => {
|
||||
|
||||
'&.error': {
|
||||
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
|
||||
},
|
||||
'&.clearable-input': {
|
||||
paddingRight: '46px'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -121,10 +124,6 @@ const useStyles = makeStyles()(theme => {
|
||||
cursor: 'pointer'
|
||||
},
|
||||
|
||||
clearableInput: {
|
||||
paddingRight: '46px'
|
||||
},
|
||||
|
||||
clearButton: {
|
||||
position: 'absolute',
|
||||
right: '16px',
|
||||
@@ -185,6 +184,7 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
}: IProps, ref) => {
|
||||
const { classes: styles, cx } = useStyles();
|
||||
const isMobile = isMobileBrowser();
|
||||
const showClearIcon = clearable && value !== '' && !disabled;
|
||||
|
||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
|
||||
onChange?.(e.target.value), []);
|
||||
@@ -211,7 +211,7 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { cx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
|
||||
error && 'error', showClearIcon && 'clearable-input', icon && 'icon-input') }
|
||||
disabled = { disabled }
|
||||
id = { id }
|
||||
maxLength = { maxLength }
|
||||
@@ -232,7 +232,7 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { cx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
|
||||
error && 'error', showClearIcon && 'clearable-input', icon && 'icon-input') }
|
||||
data-testid = { testId }
|
||||
disabled = { disabled }
|
||||
id = { id }
|
||||
@@ -252,7 +252,7 @@ const Input = React.forwardRef<any, IProps>(({
|
||||
type = { type }
|
||||
value = { value } />
|
||||
)}
|
||||
{clearable && !disabled && value !== '' && <button className = { styles.clearButton }>
|
||||
{showClearIcon && <button className = { styles.clearButton }>
|
||||
<Icon
|
||||
onClick = { clearInput }
|
||||
size = { 20 }
|
||||
|
||||
@@ -19,12 +19,12 @@ interface ISelectProps {
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Wether or not the select is disabled.
|
||||
* Whether or not the select is disabled.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Wether or not the select is in the error state.
|
||||
* Whether or not the select is in the error state.
|
||||
*/
|
||||
error?: boolean;
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { makeStyles } from 'tss-react/mui';
|
||||
import { TEXT_OVERFLOW_TYPES } from '../../constants.web';
|
||||
|
||||
interface ITextWithOverflowProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
overflowType?: TEXT_OVERFLOW_TYPES;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
overflowType?: TEXT_OVERFLOW_TYPES;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles<{ translateDiff: number; }>()((_, { translateDiff }) => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { SECURITY_URL } from './contants';
|
||||
*
|
||||
* @param {IReduxState} state - The redux state.
|
||||
* @param {Function} t - The translation function.
|
||||
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warining.
|
||||
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warning.
|
||||
* @returns {Text}
|
||||
*/
|
||||
export default function getUnsafeRoomText(state: IReduxState, t: Function, context: 'meeting' | 'prejoin' | 'welcome') {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SECURITY_URL } from './contants';
|
||||
* Gets the unsafe room text for the given context.
|
||||
*
|
||||
* @param {Function} t - The translation function.
|
||||
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warining.
|
||||
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warning.
|
||||
* @returns {string}
|
||||
*/
|
||||
export default function getUnsafeRoomText(t: Function, context: 'meeting' | 'prejoin' | 'welcome') {
|
||||
|
||||
@@ -22,27 +22,6 @@ export function assignIfDefined(target: Object, source: Object) {
|
||||
return to;
|
||||
}
|
||||
|
||||
export type DefferedPromise<T> = {
|
||||
promise: Promise<T>;
|
||||
reject: (reason?: any) => void;
|
||||
resolve: (value: T) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a deferred object.
|
||||
*
|
||||
* @returns {{promise, resolve, reject}}
|
||||
*/
|
||||
export function createDeferred<T>() {
|
||||
const deferred = {} as DefferedPromise<T>;
|
||||
|
||||
deferred.promise = new Promise<T>((resolve, reject) => {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
|
||||
return deferred;
|
||||
}
|
||||
|
||||
const MATCH_OPERATOR_REGEXP = /[|\\{}()[\]^$+*?.-]/g;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import CalendarListContent from './CalendarListContent.native';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* The tyoe of the React {@code Component} props of {@link CalendarList}.
|
||||
* The type of the React {@code Component} props of {@link CalendarList}.
|
||||
*/
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ export default createStyleSheet({
|
||||
},
|
||||
|
||||
/**
|
||||
* The container for all the lines if the norification.
|
||||
* The container for all the lines if the notification.
|
||||
*/
|
||||
notificationTextContainer: {
|
||||
flexDirection: 'column',
|
||||
|
||||
@@ -5,8 +5,6 @@ import { v4 as uuidV4 } from 'uuid';
|
||||
import { findWindows } from 'windows-iana';
|
||||
import { IanaName } from 'windows-iana/dist/enums';
|
||||
|
||||
// @ts-expect-error
|
||||
import { createDeferred } from '../../../../modules/util/helpers';
|
||||
import { IStore } from '../../app/types';
|
||||
import { parseURLParams } from '../../base/util/parseURLParams';
|
||||
import { parseStandardURIString } from '../../base/util/uri';
|
||||
@@ -153,8 +151,7 @@ export const microsoftCalendarApi = {
|
||||
return Promise.reject('Sign in already in progress.');
|
||||
}
|
||||
|
||||
const signInDeferred = createDeferred();
|
||||
|
||||
const signInDeferred = Promise.withResolvers();
|
||||
const guids = {
|
||||
authState: uuidV4(),
|
||||
authNonce: uuidV4()
|
||||
@@ -229,7 +226,7 @@ export const microsoftCalendarApi = {
|
||||
userSigninName: tokenParts.userSigninName
|
||||
}));
|
||||
|
||||
signInDeferred.resolve();
|
||||
signInDeferred.resolve(undefined);
|
||||
}
|
||||
|
||||
window.addEventListener('message', handleAuth);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Theme } from '@mui/material';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
@@ -264,7 +264,7 @@ const ChatMessage = ({
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
*/
|
||||
const renderReactions = () => {
|
||||
const renderReactions = useMemo(() => {
|
||||
if (!message.reactions || message.reactions.size === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -321,7 +321,7 @@ const ChatMessage = ({
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
}, [ message?.reactions, isHovered, isReactionsOpen ]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -366,7 +366,7 @@ const ChatMessage = ({
|
||||
<div className = { classes.chatMessageFooterLeft }>
|
||||
{message.reactions && message.reactions.size > 0 && (
|
||||
<>
|
||||
{renderReactions()}
|
||||
{renderReactions}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -117,7 +117,7 @@ class DisplayNameForm extends Component<IProps, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSubmit(event: any) {
|
||||
event?.preventDefault && event.preventDefault();
|
||||
event?.preventDefault?.();
|
||||
|
||||
// Store display name in settings
|
||||
this.props.dispatch(updateSettings({
|
||||
|
||||
@@ -349,8 +349,8 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
|
||||
*/
|
||||
function _onConferenceMessageReceived(store: IStore,
|
||||
{ displayName, isGuest, message, messageId, participantId, privateMessage, timestamp }: {
|
||||
displayName?: string; isGuest?: boolean; message: string; messageId?: string;
|
||||
participantId: string; privateMessage: boolean; timestamp: number; }
|
||||
displayName?: string; isGuest?: boolean; message: string; messageId?: string;
|
||||
participantId: string; privateMessage: boolean; timestamp: number; }
|
||||
) {
|
||||
|
||||
const isGif = isGifEnabled(store.getState()) && isGifMessage(message);
|
||||
@@ -479,8 +479,8 @@ function getLobbyChatDisplayName(state: IReduxState, participantId: string) {
|
||||
*/
|
||||
function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
{ displayName, isGuest, lobbyChat, message, messageId, participantId, privateMessage, timestamp }: {
|
||||
displayName?: string; isGuest?: boolean; lobbyChat: boolean; message: string;
|
||||
messageId?: string; participantId: string; privateMessage: boolean; timestamp: number; },
|
||||
displayName?: string; isGuest?: boolean; lobbyChat: boolean; message: string;
|
||||
messageId?: string; participantId: string; privateMessage: boolean; timestamp: number; },
|
||||
shouldPlaySound = true,
|
||||
isReaction = false
|
||||
) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { DISMISS_CALENDAR_NOTIFICATION } from './actionTypes';
|
||||
*
|
||||
* @param {JitsiParticipant} participant - The {@link JitsiParticipant}
|
||||
* instance which initiated the kick event.
|
||||
* @param {?Function} submit - The function to execute after submiting the dialog.
|
||||
* @param {?Function} submit - The function to execute after submitting the dialog.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function notifyKickedOut(participant: any, submit?: Function) {
|
||||
@@ -38,7 +38,7 @@ export function notifyKickedOut(participant: any, submit?: Function) {
|
||||
* Notify that we've been kicked out of the conference.
|
||||
*
|
||||
* @param {string} reasonKey - The translation key for the reason why the conference failed.
|
||||
* @param {?Function} submit - The function to execute after submiting the dialog.
|
||||
* @param {?Function} submit - The function to execute after submitting the dialog.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function notifyConferenceFailed(reasonKey: string, submit?: Function) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
@@ -7,6 +8,7 @@ import { IconUsers } from '../../../base/icons/svg';
|
||||
import Label from '../../../base/label/components/web/Label';
|
||||
import { COLORS } from '../../../base/label/constants';
|
||||
import { getParticipantCount } from '../../../base/participants/functions';
|
||||
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||
import SpeakerStats from '../../../speaker-stats/components/web/SpeakerStats';
|
||||
import { isSpeakerStatsDisabled } from '../../../speaker-stats/functions';
|
||||
import { iAmVisitor } from '../../../visitors/functions';
|
||||
@@ -23,6 +25,7 @@ function SpeakerStatsLabel() {
|
||||
const iAmVisitorState = useSelector(iAmVisitor);
|
||||
const _isSpeakerStatsDisabled = useSelector(isSpeakerStatsDisabled);
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// visitor has hidden its own video and should not count itself
|
||||
if (iAmVisitorState) {
|
||||
@@ -38,13 +41,17 @@ function SpeakerStatsLabel() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
color = { COLORS.white }
|
||||
icon = { IconUsers }
|
||||
iconColor = '#fff'
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { onClick }
|
||||
text = { `${count}` } />
|
||||
<Tooltip
|
||||
content = { t('speakerStats.labelTooltip', { count }) }
|
||||
position = { 'bottom' }>
|
||||
<Label
|
||||
color = { COLORS.white }
|
||||
icon = { IconUsers }
|
||||
iconColor = '#fff'
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { onClick }
|
||||
text = { `${count}` } />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ export const IFRAME_DISABLED_TIMEOUT_MINUTES = 5;
|
||||
/**
|
||||
* A list of allowed location to embed iframe.
|
||||
*/
|
||||
/* eslint-disable-next-line no-extra-parens*/
|
||||
/* eslint-disable-next-line no-extra-parens */
|
||||
export const IFRAME_EMBED_ALLOWED_LOCATIONS = ([] as string[]).concat(ADDITIONAL_LOCATIONS);
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
import { DesktopSharingSourceType } from '../base/tracks/types';
|
||||
|
||||
import DesktopPicker from './components/DesktopPicker';
|
||||
|
||||
type Options = {
|
||||
desktopSharingSources?: Array<DesktopSharingSourceType>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Signals to open a dialog with the DesktopPicker component.
|
||||
*
|
||||
@@ -10,7 +15,7 @@ import DesktopPicker from './components/DesktopPicker';
|
||||
* a DesktopCapturerSource has been chosen.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function showDesktopPicker(options: { desktopSharingSources?: any; } = {}, onSourceChoose: Function) {
|
||||
export function showDesktopPicker(options: Options = {}, onSourceChoose: Function) {
|
||||
const { desktopSharingSources } = options;
|
||||
|
||||
return openDialog(DesktopPicker, {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
|
||||
import { IStore } from '../../app/types';
|
||||
import { hideDialog } from '../../base/dialog/actions';
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import { DesktopSharingSourceType } from '../../base/tracks/types';
|
||||
import Dialog from '../../base/ui/components/web/Dialog';
|
||||
import Tabs from '../../base/ui/components/web/Tabs';
|
||||
import { THUMBNAIL_SIZE } from '../constants';
|
||||
@@ -32,7 +33,7 @@ const TAB_LABELS = {
|
||||
window: 'dialog.applicationWindow'
|
||||
};
|
||||
|
||||
const VALID_TYPES = Object.keys(TAB_LABELS);
|
||||
const VALID_TYPES = Object.keys(TAB_LABELS) as DesktopSharingSourceType[];
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link DesktopPicker}.
|
||||
@@ -42,7 +43,7 @@ interface IProps extends WithTranslation {
|
||||
/**
|
||||
* An array with desktop sharing sources to be displayed.
|
||||
*/
|
||||
desktopSharingSources: Array<string>;
|
||||
desktopSharingSources: Array<DesktopSharingSourceType>;
|
||||
|
||||
/**
|
||||
* Used to request DesktopCapturerSources.
|
||||
@@ -84,7 +85,7 @@ interface IState {
|
||||
/**
|
||||
* The desktop source types to fetch previews for.
|
||||
*/
|
||||
types: Array<string>;
|
||||
types: Array<DesktopSharingSourceType>;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +113,7 @@ class DesktopPicker extends PureComponent<IProps, IState> {
|
||||
* @private
|
||||
* @returns {Array<string>} The filtered types.
|
||||
*/
|
||||
static _getValidTypes(types: string[] = []) {
|
||||
static _getValidTypes(types: DesktopSharingSourceType[] = []) {
|
||||
return types.filter(
|
||||
type => VALID_TYPES.includes(type));
|
||||
}
|
||||
@@ -346,10 +347,10 @@ class DesktopPicker extends PureComponent<IProps, IState> {
|
||||
= types.map(
|
||||
type => {
|
||||
return {
|
||||
accessibilityLabel: t(TAB_LABELS[type as keyof typeof TAB_LABELS]),
|
||||
accessibilityLabel: t(TAB_LABELS[type]),
|
||||
id: `${type}`,
|
||||
controlsId: `${type}-panel`,
|
||||
label: t(TAB_LABELS[type as keyof typeof TAB_LABELS])
|
||||
label: t(TAB_LABELS[type])
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import logger from './logger';
|
||||
import { ElectronWindowType } from './types';
|
||||
|
||||
|
||||
/**
|
||||
* Begins a request to get available DesktopCapturerSources.
|
||||
@@ -13,23 +11,6 @@ import { ElectronWindowType } from './types';
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function obtainDesktopSources(options: { thumbnailSize?: Object; types: string[]; }) {
|
||||
const { JitsiMeetElectron } = window as ElectronWindowType;
|
||||
|
||||
// TODO: delete this after 2 releases
|
||||
if (JitsiMeetElectron?.obtainDesktopStreams) {
|
||||
return new Promise((resolve, reject) => {
|
||||
JitsiMeetElectron.obtainDesktopStreams(
|
||||
(sources: Array<{ id: string; }>) => resolve(_separateSourcesByType(sources)),
|
||||
(error: Error) => {
|
||||
logger.error(
|
||||
`Error while obtaining desktop sources: ${error}`);
|
||||
reject(error);
|
||||
},
|
||||
options
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return APP.API.requestDesktopSources(options).then(
|
||||
({ sources, error }: { error: Error; sources: Array<{ id: string; }>; }) => {
|
||||
if (sources) {
|
||||
@@ -43,21 +24,6 @@ export function obtainDesktopSources(options: { thumbnailSize?: Object; types: s
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check usage of old jitsi meet electron version.
|
||||
*
|
||||
* @returns {boolean} True if we use old jitsi meet electron, otherwise false.
|
||||
*/
|
||||
export function oldJitsiMeetElectronUsage() {
|
||||
const { JitsiMeetElectron } = window as ElectronWindowType;
|
||||
|
||||
if (JitsiMeetElectron?.obtainDesktopStreams) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of DesktopCapturerSources to an object with types for keys
|
||||
* and values being an array with sources of the key's type.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export type ElectronWindowType = {
|
||||
JitsiMeetElectron?: {
|
||||
obtainDesktopStreams: Function;
|
||||
} ;
|
||||
} & typeof window;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user