Compare commits

...

69 Commits

Author SHA1 Message Date
Calinteodor
0c45d87d1a react-native-sdk(android): screen share updates (#14440)
* react-native-sdk(android): removed related modules, services to screen-share feature and updated peerDeps
2024-03-06 17:33:08 +02:00
damencho
7140a90201 fix(visitors): Fixes demoting correct participant.
Moderators receive all demote messages so they can show notification if we need to.
2024-03-05 08:22:32 -06:00
Jaya Allamsetty
0a846606fc chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1786.0.0+0129be6c...v1789.0.0+a8f8666b
2024-03-05 09:02:32 -05:00
Avram Tudor
68dc111e3c fix: decouple local recording from needing a valid jwt (#14434) 2024-03-05 12:41:44 +02:00
Saúl Ibarra Corretgé
c81184df69 fix(ios) sync SDK and Lite SDK building commands 2024-03-05 12:09:03 +02:00
damencho
9b0747a0d9 feat(visitors): Demote a visitor for mobile. 2024-03-04 13:18:04 -06:00
damencho
c8cd80a8df feat(visitors): Checks for visitors support per room. 2024-03-04 13:18:04 -06:00
damencho
f1d4332668 feat(visitors): Adds an option to demote participants to visitors. 2024-03-04 13:18:04 -06:00
damencho
55b3256dc4 fix: Changes jwt error dialog to be sticky. 2024-03-04 13:18:04 -06:00
damencho
aa8bb55f3e feat(visitors): Drops not-used messages. 2024-03-04 13:18:04 -06:00
damencho
58b73e21de fix(visitors): Fixes missing import. 2024-03-04 13:18:04 -06:00
damencho
b1c955890a feat(visitors): Admit all function. 2024-03-04 13:18:04 -06:00
damencho
6ab945c2cb fix(visitors): Fixes wrong text in notification on multiple promote requests. 2024-03-04 13:18:04 -06:00
damencho
7291e1ef00 feat(lobby): Approve multiple participants. 2024-03-04 08:13:27 -06:00
Дамян Минков
43e075d48e feat: Rate limits update (#14429)
* feat: Introduces new rate limit setting.

No can have two different values per ip that is used to limit session creation and one that is used when that stanza rate limit is exceeded.

* feat: Introduces unthrottle logic.

* fix: Bumps default iq rate limits.

* feat: Prints how many times a session hits the rates.

* Update resources/prosody-plugins/mod_rate_limit.lua

Co-authored-by: Aaron van Meerten <aaron.van.meerten@8x8.com>

---------

Co-authored-by: Aaron van Meerten <aaron.van.meerten@8x8.com>
2024-03-01 13:23:04 -06:00
Jaya Allamsetty
885e1afdaa chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1784.0.0+639ad566...v1786.0.0+0129be6c
2024-02-29 23:34:47 -05:00
Hristo Terezov
e2ec4842a1 fix(toolbarButtons): filter visitor buttons in redux.
Filters the toolbarButtons in redux depending on the visitor state instead of filtering them every time in mapStateToProps. This will prevent unnecessary rerenders of the toolbar.

Additionally:
 - Moves visitor buttons const from features/config in features/toolbox.
 - Removes dublicate functions isButtonEnabled and isToolbarButtonEnabled.
 - Adds more buttons to the visitor allowed buttons which functionality has been any way accessible trough shortcuts or somewhere else.
 - Enables customButtons to be visible for visitors.
2024-02-29 18:51:47 -06:00
Hristo Terezov
ea075d9bae fix(toolbarButtons): Store all buttons in redux.
The previous version of getToolbarButtons function was actually adding the custom buttons on every call to the config toolbarButtons array, effectively creating dublicates of every custom button. The PR fixes this issue.

Also now we will be running the getToolbarButtons calculation only when needed.
2024-02-29 16:36:52 -06:00
qnafin
68f7448624 Update build.gradle / jsRootDir = file("../")
When newArchEnabled=true. Refers to a non-existent category
2024-02-29 19:58:14 +01:00
damencho
954ef6df4f fix: Drops inspect print. 2024-02-29 12:52:19 -06:00
Calinteodor
6a3c12b316 feat(android): fix screen sharing for android 14 (#14419)
* feat(android): media projection is now done through react native webrtc
2024-02-29 16:34:24 +02:00
Calin-Teodor
5be616a224 chore(deps, rn-webrtc): updated to 118.0.2 2024-02-29 15:56:20 +02:00
Mihaela Dumitru
58d8f3be12 chore(deps) update excalidraw version (#14420) 2024-02-29 15:30:44 +02:00
Saúl Ibarra Corretgé
8ae6ba76cc feat(rn,deps) update react-native-webrtc@118.0.1
Adds UVC camera support to Android.
2024-02-29 12:02:51 +01:00
bgrozev
a06c2e056c feat: Send room_metadata updates to jicofo. (#14417)
Include the room jid in the message.
2024-02-28 15:41:55 -08:00
Saúl Ibarra Corretgé
fa0b06b859 fix(live-streaming) fix handling bogus URLs 2024-02-28 13:58:13 +01:00
Saúl Ibarra Corretgé
4c3512b375 fix(etherpad) fix handling certain URL types 2024-02-28 13:58:13 +01:00
Jaya Allamsetty
92e3a1ff99 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1783.0.0+ff4dd993...v1784.0.0+639ad566
2024-02-27 13:32:43 -05:00
Saúl Ibarra Corretgé
701e3d2b43 fix(recording) restore labels when recording locally 2024-02-27 09:17:24 -06:00
Saúl Ibarra Corretgé
2626a894e2 fix(recording) remove duplicated check 2024-02-27 09:17:24 -06:00
Saúl Ibarra Corretgé
a6f6b3a2d2 fix(subtitles) keep subtitles state when recording 2024-02-27 09:17:24 -06:00
Hristo Terezov
05aa48774a feat(recorder-transcription): Handle correctly in the UI.
Until this commit we didn't make difference between transcriptions from the recording dialog and subtitles. Now subtitles are not considered recording anymore and only the transcriptions started from recording dialog are considered recording.
2024-02-27 09:17:24 -06:00
Calinteodor
e82fc480f0 feat(tracks): disable pip in screen-share based on flag value (#14413)
* feat(tracks): disable pip in screen-share based on flag value
2024-02-27 14:59:55 +02:00
Horatiu Muresan
9f8662c0a1 chore(eslint) Update eslint (#14412) 2024-02-27 12:56:05 +02:00
Jaya Allamsetty
5b860c20b1 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1782.0.0+3ff4b0d1...v1783.0.0+ff4dd993
2024-02-26 10:25:05 -05:00
Saúl Ibarra Corretgé
a4c52b02e7 fix(recording) set transcribing metadata when stopping 2024-02-26 13:30:23 +01:00
Joseph Garrone
73952ee1f7 refactor(styles): Migrate withStyles to tss-react, remove @mui/styles dependency (#14310) 2024-02-26 14:10:07 +02:00
Hristo Terezov
0a9f6c2e4d fix(overflow-menu): disabled buttons styles 2024-02-23 17:53:29 -06:00
Jaya Allamsetty
94f9ccfe1c chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1781.0.0+98bcd4e6...v1782.0.0+3ff4b0d1
2024-02-23 17:08:34 -05:00
Hristo Terezov
ff9f767cfd fix(recording/livestreaming): labels
Display only livestreaming label when the livestreaming and the transcribtions are on.
2024-02-23 15:55:23 -06:00
JJ Asghar
5a5ec9e4d9 fix(Typo in comment) (#14077) 2024-02-23 22:16:21 +01:00
Calin-Teodor
aff671b44a feat(config/flag): enable/disable participants pane 2024-02-23 14:54:37 +02:00
Hristo Terezov
495b925fa0 fix(Livestreaming): label 2024-02-22 18:13:21 -06:00
Hristo Terezov
2f6b11a6ac fix(visitors): gum pending on promotion. 2024-02-22 14:57:51 -06:00
Jaya Allamsetty
2b7052995b feat: Enable ssrc-rewriting by default. (#14395)
* feat: Enable ssrc-rewriting by default.

* chore(deps): Update lib-jitsi-meet.
2024-02-22 15:16:50 -05:00
Calin-Teodor
5c69baefbf feat(dynamic-branding): fix label customisation 2024-02-21 14:51:37 +02:00
dependabot[bot]
8b25265f9e chore(deps): bump ip from 1.1.5 to 1.1.9
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 09:21:37 +01:00
Hristo Terezov
f43020f650 fix(gumPending): Handle ignored muted tracks. 2024-02-20 16:54:05 -06:00
Hristo Terezov
ba95ea3ac8 feat(conference): Add init debug log 2024-02-20 09:42:07 -06:00
Besnik Bleta
7dcd9c21bc fix(lang) update for sq translation (#14387) 2024-02-20 15:23:14 +01:00
damencho
990d21038e fix: Fixes initializing cached keys. 2024-02-16 11:36:07 -06:00
Mihaela Dumitru
a4d53f271f fix(ui) center conference info (#14379) 2024-02-16 14:37:12 +02:00
bgrozev
2a1f472873 Add a second shared secret to allow rotation if necessary. (#14378) 2024-02-15 15:00:30 -08:00
damencho
8679119677 fix: Change whitelist to support only domains. 2024-02-15 16:36:37 -06:00
damencho
b02c072ba7 fix: Handles few more cases for updating cached tokens. 2024-02-15 12:20:02 -06:00
Saúl Ibarra Corretgé
e8317fccfe fix(local-recordings) fix on Chrome
WebM + VP8 seems to be the one thing that works, requesting H.264 starts
the recorder in Matroska + AV1 format (WAT) but no data is received at
all.
2024-02-15 17:49:45 +01:00
Saúl Ibarra Corretgé
bbc9c64978 fix(recording) only show advanced section for cloud recordings 2024-02-15 17:49:45 +01:00
Saúl Ibarra Corretgé
8d2b8bc772 fix(recording) fix checking if we can skip the recording dialog 2024-02-15 15:15:38 +01:00
damencho
52c2911350 feat: Adds stats around room token validation. 2024-02-15 07:13:12 -06:00
damencho
a1ebcd559b feat: Adds a print for timing out http requests after the retry. 2024-02-15 07:13:12 -06:00
damencho
5bc47ec16a feat: Adds stat when a rayo IQ is filtered. 2024-02-15 07:13:12 -06:00
damencho
e1ac7d1609 feat: Adds stats around token validation. 2024-02-15 07:13:12 -06:00
damencho
8a596f1ba2 fix: Drops unused token validation. 2024-02-15 07:13:12 -06:00
damencho
38e7c65836 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1776.0.0+f5f8a137...v1777.0.0+3898d7aa
2024-02-14 22:52:49 -05:00
Jaya Allamsetty
9602a939d8 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1775.0.0+c7495280...v1776.0.0+f5f8a137
2024-02-14 12:32:39 -05:00
Calinteodor
07b01b1371 feat(conference/recordin): Fix always on labels (#14368)
* feat(conference/recording): condition live stream label visibility and fix labels tooltip translations
2024-02-14 13:11:57 +02:00
damencho
a1549086aa chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1773.0.0+cc9b0e60...v1775.0.0+c7495280
2024-02-13 14:02:28 -06:00
Saúl Ibarra Corretgé
7e0b00ba5f feat(prejoin) default to enabled (#14236)
* feat(prejoin) default to enabled

* squash: Fixes is prejoin config check.

---------

Co-authored-by: damencho <damencho@jitsi.org>
2024-02-13 12:47:59 -06:00
Calin-Teodor
3016853d81 feat(android): replaced NotificationChannels with NotificationUtils 2024-02-13 20:03:36 +02:00
164 changed files with 2243 additions and 2118 deletions

4
.gitignore vendored
View File

@@ -99,10 +99,6 @@ tsconfig.json
#
react-native-sdk/*.tgz
react-native-sdk/android/src
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetOngoingConferenceService.java
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetReactNativePackage.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

View File

@@ -19,9 +19,9 @@ buildscript {
ext {
kotlinVersion = "1.7.0"
buildToolsVersion = "33.0.2"
compileSdkVersion = 33
compileSdkVersion = 34
minSdkVersion = 24
targetSdkVersion = 33
targetSdkVersion = 34
supportLibVersion = "28.0.0"
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.

View File

@@ -12,7 +12,6 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-feature
@@ -51,10 +50,6 @@
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
android:foregroundServiceType="mediaPlayback" />
<service
android:name="org.jitsi.meet.sdk.JitsiMeetMediaProjectionService"
android:foregroundServiceType="mediaProjection" />
<provider
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
android:authorities="${applicationId}.fileprovider"

View File

@@ -1,44 +0,0 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
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;
@ReactModule(name = JitsiMeetMediaProjectionModule.NAME)
class JitsiMeetMediaProjectionModule
extends ReactContextBaseJavaModule {
public static final String NAME = "JitsiMeetMediaProjectionModule";
public JitsiMeetMediaProjectionModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@ReactMethod
public void launch() {
Context context = getReactApplicationContext();
Activity currentActivity = getCurrentActivity();
JitsiMeetMediaProjectionService.launch(context, currentActivity);
}
@ReactMethod
public void abort() {
Context context = getReactApplicationContext();
JitsiMeetMediaProjectionService.abort(context);
}
@NonNull
@Override
public String getName() {
return NAME;
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Notification;
import android.app.Service;
import android.content.ComponentName;
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 JitsiMeetMediaProjectionService extends Service {
private static final String TAG = JitsiMeetMediaProjectionService.class.getSimpleName();
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
public static void launch(Context context, Activity currentActivity) {
NotificationUtils.createNotificationChannel(currentActivity);
Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);
ComponentName componentName;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
componentName = context.startForegroundService(intent);
} else {
componentName = context.startService(intent);
}
} 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 + "Media projection service not started", e);
return;
}
if (componentName == null) {
JitsiMeetLogger.w(TAG + "Media projection service not started");
}
}
public static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);
context.stopService(intent);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
return START_NOT_STICKY;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
} else {
startForeground(NOTIFICATION_ID, notification);
}
return START_NOT_STICKY;
}
}

View File

@@ -58,8 +58,8 @@ public class JitsiMeetOngoingConferenceService extends Service
public static void launch(Context context, HashMap<String, Object> extraData) {
NotificationUtils.createNotificationChannel((Activity) context);
OngoingNotification.createNotificationChannel((Activity) context);
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);

View File

@@ -1,58 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
import android.app.Notification;
import android.content.Context;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
/**
* Helper class for creating the media projection notification which is used with
* {@link JitsiMeetMediaProjectionService}.
*/
class MediaProjectionNotification {
private static final String TAG = MediaProjectionNotification.class.getSimpleName();
static Notification buildMediaProjectionNotification(Context context) {
if (context == null) {
JitsiMeetLogger.d(TAG, " Cannot create notification: no current context");
return null;
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
builder
.setCategory(NotificationCompat.CATEGORY_CALL)
.setContentTitle(context.getString(R.string.media_projection_notification_title))
.setContentText(context.getString(R.string.media_projection_notification_text))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(false)
.setUsesChronometer(false)
.setAutoCancel(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
return builder.build();
}
}

View File

@@ -1,10 +0,0 @@
package org.jitsi.meet.sdk;
import java.util.ArrayList;
import java.util.List;
public class NotificationChannels {
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
}

View File

@@ -1,45 +0,0 @@
package org.jitsi.meet.sdk;
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
class NotificationUtils {
private static final String TAG = NotificationUtils.class.getSimpleName();
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;
}
NotificationManager notificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
}
}

View File

@@ -16,8 +16,11 @@
package org.jitsi.meet.sdk;
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -26,7 +29,8 @@ import android.content.Intent;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import android.os.Build;
/**
* Helper class for creating the ongoing notification which is used with
@@ -38,6 +42,37 @@ class OngoingNotification {
private static long startingTime = 0;
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;
}
NotificationManager notificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
}
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context) {
if (context == null) {

View File

@@ -37,6 +37,7 @@ import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
import org.devio.rn.splashscreen.SplashScreenModule;
import org.webrtc.EglBase;
import org.webrtc.Logging;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
@@ -67,7 +68,6 @@ class ReactInstanceManagerHolder {
new DropboxModule(reactContext),
new ExternalAPIModule(reactContext),
new JavaScriptSandboxModule(reactContext),
new JitsiMeetMediaProjectionModule(reactContext),
new LocaleDetector(reactContext),
new LogBridgeModule(reactContext),
new SplashScreenModule(reactContext),
@@ -241,6 +241,8 @@ class ReactInstanceManagerHolder {
options.videoDecoderFactory = new H264AndSoftwareVideoDecoderFactory(eglContext);
options.videoEncoderFactory = new H264AndSoftwareVideoEncoderFactory(eglContext);
options.enableMediaProjectionService = true;
// options.loggingSeverity = Logging.Severity.LS_INFO;
Log.d(TAG, "initializing RN with Activity");

View File

@@ -91,7 +91,7 @@ import {
setVideoMuted,
setVideoUnmutePermissions
} from './react/features/base/media/actions';
import { MEDIA_TYPE } from './react/features/base/media/constants';
import { MEDIA_TYPE, VIDEO_TYPE } from './react/features/base/media/constants';
import {
getStartWithAudioMuted,
getStartWithVideoMuted,
@@ -165,7 +165,6 @@ import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/Au
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
import { muteLocal } from './react/features/video-menu/actions.any';
import { iAmVisitor } from './react/features/visitors/functions';
import UIEvents from './service/UI/UIEvents';
const logger = Logger.getLogger(__filename);
@@ -405,7 +404,13 @@ function disconnect() {
* @returns {void}
*/
function setGUMPendingStateOnFailedTracks(tracks) {
const tracksTypes = tracks.map(track => track.getType());
const tracksTypes = tracks.map(track => {
if (track.getVideoType() === VIDEO_TYPE.DESKTOP) {
return MEDIA_TYPE.SCREENSHARE;
}
return track.getType();
});
const nonPendingTracks = [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ].filter(type => !tracksTypes.includes(type));
APP.store.dispatch(gumPending(nonPendingTracks, IGUMPendingState.NONE));
@@ -679,6 +684,8 @@ export default {
startWithVideoMuted: getStartWithVideoMuted(state) || isUserInteractionRequiredForUnmute(state)
};
logger.debug(`Executed conference.init with roomName: ${roomName}`);
this.roomName = roomName;
try {
@@ -697,10 +704,6 @@ export default {
const handleInitialTracks = (options, tracks) => {
let localTracks = tracks;
// No local tracks are added when user joins as a visitor.
if (iAmVisitor(state)) {
return [];
}
if (options.startWithAudioMuted || room?.isStartAudioMuted()) {
// Always add the track on Safari because of a known issue where audio playout doesn't happen
// if the user joins audio and video muted, i.e., if there is no local media capture.
@@ -1253,7 +1256,24 @@ export default {
room = APP.connection.initJitsiConference(APP.conference.roomName, this._getConferenceOptions());
// Filter out the tracks that are muted (except on Safari).
const tracks = browser.isWebKitBased() ? localTracks : localTracks.filter(track => !track.isMuted());
let tracks = localTracks;
if (!browser.isWebKitBased()) {
const mutedTrackTypes = [];
tracks = localTracks.filter(track => {
if (!track.isMuted()) {
return true;
}
if (track.getVideoType() !== VIDEO_TYPE.DESKTOP) {
mutedTrackTypes.push(track.getType());
}
return false;
});
APP.store.dispatch(gumPending(mutedTrackTypes, IGUMPendingState.NONE));
}
this._setLocalAudioVideoStreams(tracks);
this._room = room; // FIXME do not use this

View File

@@ -650,7 +650,7 @@ var config = {
// // Whether to disable welcome page. In case it's disabled a random room
// // will be joined when no room is specified.
// disabled: false,
// // If set,landing page will redirect to this URL.
// // If set, landing page will redirect to this URL.
// customUrl: ''
// },
@@ -723,7 +723,7 @@ var config = {
// Configs for prejoin page.
// prejoinConfig: {
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
// // This replaces `prejoinPageEnabled`.
// // This replaces `prejoinPageEnabled`. Defaults to true.
// enabled: true,
// // Hides the participant name editing field in the prejoin screen.
// // If requireDisplayName is also set as true, a name should still be provided through
@@ -1300,6 +1300,8 @@ var config = {
// remoteVideoMenu: {
// // Whether the remote video context menu to be rendered or not.
// disabled: true,
// // If set to true the 'Switch to visitor' button will be disabled.
// disableDemote: true,
// // If set to true the 'Kick out' button will be disabled.
// disableKick: true,
// // If set to true the 'Grant moderator' button will be disabled.
@@ -1388,6 +1390,8 @@ var config = {
// Options related to the participants pane.
// participantsPane: {
// // Enables feature
// enabled: true,
// // Hides the moderator settings tab.
// hideModeratorSettingsTab: false,
// // Hides the more actions button.

View File

@@ -41,7 +41,6 @@
position: absolute;
top: 0;
height: 48px;
max-width: calc(100% - 24px);
}
@keyframes hideSubject {

View File

@@ -453,7 +453,7 @@ PODS:
- react-native-video/Video (6.0.0-alpha.11):
- PromisesSwift
- React-Core
- react-native-webrtc (118.0.0):
- react-native-webrtc (118.0.2):
- JitsiWebRTC (~> 118.0.0)
- React-Core
- react-native-webview (13.5.1):
@@ -881,7 +881,7 @@ SPEC CHECKSUMS:
react-native-slider: 1cdd6ba29675df21f30544253bf7351d3c2d68c4
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
react-native-video: 472b7c366eaaaa0207e546d9a50410df89790bcf
react-native-webrtc: c8d9ad3c152105b2720ca2851d04b49659551992
react-native-webrtc: a0a8a1730b6cc5a5bda8a6e2166a74c9b78029e2
react-native-webview: 8baa0f5c6d336d6ba488e942bcadea5bf51f050a
React-NativeModulesApple: 4225ac31a26696c02c54b471052b3e85e74a9a0c
React-perflogger: cb433f318c6667060fc1f62e26eb58d6eb30a627

View File

@@ -629,7 +629,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\nexport NODE_ARGS=\"--max_old_space_size=4096\"\n../../node_modules/react-native/scripts/react-native-xcode.sh\n";
shellScript = "WITH_ENVIRONMENT=\"../../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
};
DE9A016B289A9A9A00E41CBB /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;

View File

@@ -1,5 +1,8 @@
{
"addPeople": {
"accessibilityLabel": {
"meetingLink": "Lidhje takimi: {{url}}"
},
"add": "Ftoni",
"addContacts": "Ftoni kontaktet tuaja",
"contacts": "kontakte",
@@ -39,6 +42,18 @@
"audioOnly": {
"audioOnly": "Gjerësi e ulët bande"
},
"bandwidthSettings": {
"assumedBandwidthBps": "p.sh. 10000000 për 10 Mbps",
"assumedBandwidthBpsWarning": "Vlera më të mëdha mund të shkaktojnë probleme rrjeti.",
"customValue": "vlerë vetjake",
"customValueEffect": "për të ujdisur vlerën e tanishme bps",
"leaveEmpty": "lëreni të zbrazët",
"leaveEmptyEffect": "për të lejuar kryerje vlerësimesh",
"possibleValues": "Vlera të mundshme",
"setAssumedBandwidthBps": "Gjerësi bande e supozuar (bps)",
"title": "Rregullime gjerësi bande",
"zeroEffect": "për të çaktivizuar videon"
},
"breakoutRooms": {
"actions": {
"add": "Shtoni aneks konsultimesh",
@@ -48,15 +63,22 @@
"leaveBreakoutRoom": "Dilni nga aneks konsultimesh",
"more": "Më tepër",
"remove": "Hiqe",
"rename": "Riemërtojeni",
"renameBreakoutRoom": "Riemërtoni aneks konsultimesh",
"sendToBreakoutRoom": "Dërgoje pjesëmarrësin te:"
},
"breakoutList": "Listë aneksesh",
"buttonLabel": "Dhoma aneks konsultimesh",
"defaultName": "Dhomë aneks konsultimesh #{{index}}",
"hideParticipantList": "Fshihe listën e pjesëmarrësve",
"mainRoom": "Dhoma kryesore",
"notifications": {
"joined": "Po hyhet te dhomë aneks konsultimesh \"{{name}}\"",
"joinedMainRoom": "Po hyhet te dhoma kryesore",
"joinedTitle": "Dhoma Aneks Konsultimesh"
}
},
"showParticipantList": "Shfaq listë pjesëmarrësish",
"title": "Dhoma Aneks Konsultimesh"
},
"calendarSync": {
"addMeetingURL": "Shtoni një lidhje takimi",
@@ -68,9 +90,9 @@
},
"join": "Merrni pjesë",
"joinTooltip": "Merrni pjesë në takim",
"nextMeeting": "takimi pasues",
"nextMeeting": "Takimi pasues",
"noEvents": "S’ka veprimtari të ardhshme të vëna në plan.",
"ongoingMeeting": "takim në zhvillim e sipër",
"ongoingMeeting": "Takim në zhvillim e sipër",
"permissionButton": "Hapni rregullimet",
"permissionMessage": "Që të shihni në aplikacion takimet tuaja, janë të domosdoshme lejet mbi Kalendarin.",
"refresh": "Rifresko kalendarin",
@@ -156,6 +178,7 @@
"localport_plural": "Porta vendore:",
"maxEnabledResolution": "maksimum dërgimi",
"more": "Shfaq më tepër",
"no": "jo",
"packetloss": "Humbje paketesh:",
"participant_id": "ID pjesëmarrësi:",
"quality": {
@@ -174,7 +197,8 @@
"status": "Lidhje:",
"transport": "Transport:",
"transport_plural": "Transporte:",
"video_ssrc": "SSRC Videoje:"
"video_ssrc": "Video SSRC:",
"yes": "po"
},
"dateUtils": {
"earlier": "Më herët",
@@ -195,7 +219,9 @@
"joinInBrowser": "Hyni që nga shfletues",
"launchMeetingLabel": "Si doni të hyhet në këtë takim?",
"launchWebButton": "Nise në web",
"noDesktopApp": "S’e keni aplikacionin?",
"noMobileApp": "S’e keni aplikacionin?",
"or": "OR",
"termsAndConditions": "Duke vazhduar, pajtoheni me <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>terms & conditions.</a> tona",
"title": "Po niset takimi juaj në {{app}}…",
"titleNew": "Po niset takimi juaj…",
@@ -220,7 +246,7 @@
"noPermission": "S’u akordua leje",
"previewUnavailable": "S’bëhet dot paraparje",
"selectADevice": "Përzgjidhni një pajisje",
"testAudio": "Luaj një tingull, për provë"
"testAudio": "Testojeni"
},
"dialIn": {
"screenTitle": "Përmbledhje rënieje numrash"
@@ -231,21 +257,28 @@
"dialog": {
"Back": "Mbrapsht",
"Cancel": "Anuloje",
"IamHost": "Jam organizatori",
"IamHost": "Hyni",
"Ok": "OK",
"Remove": "Hiqe",
"Share": "Ndaje",
"Submit": "Parashtroje",
"WaitForHostMsg": "Konferenca s’ka nisur ende. Nëse jeni organizatori, ju lutemi, bëni mirëfilltësimin. Përndryshe, ju lutemi, pritni që të mbërrijë organizatori.",
"WaitingForHostTitle": "Po pritet për organizatorin…",
"WaitForHostMsg": "Konferenca s’ka nisur, ngaqë s’ka mbërritur ende ndonjë moderator. Nëse dëshironi të bëheni një moderator, ju lutemi, bëni hyrjen. Përndryshe, ju lutemi, pritni.",
"WaitingForHostButton": "Prit për moderator",
"WaitingForHostTitle": "Po pritet për një moderator…",
"Yes": "Po",
"accessibilityLabel": {
"liveStreaming": "Transmetim i Drejtpërdrejtë"
"Cancel": "Anuloje (dilni nga dialogu)",
"Ok": "OK (ruajeni dhe dilni nga dialogu)",
"close": "Mbylle dialogun",
"liveStreaming": "Transmetim i Drejtpërdrejtë",
"sharingTabs": "Mundësi ndarjeje me të tjerë"
},
"add": "Shtoni",
"addMeetingNote": "Shtoni një shënim rreth këtij takimi",
"addOptionalNote": "Shtoni një shënim (në daçi):",
"allow": "Lejoje",
"allowToggleCameraDialog": "A e lejoni {{initiatorName}} të ndryshojë anën nga sheh kamera juaj?",
"allowToggleCameraTitle": "Të lejohet ndryshimi i kamerës?",
"alreadySharedVideoMsg": "Një tjetër pjesëmarrës po ndan me të tjerët një video. Kjo konferencë lejon vetëm një ndarje videoje në herë.",
"alreadySharedVideoTitle": "Lejohet vetëm një ndarje videoje me të tjerët në herë",
"applicationWindow": "Dritare aplikacioni",
@@ -306,7 +339,8 @@
"lockRoom": "Shtoni takim $t(lockRoomPasswordUppercase)",
"lockTitle": "Kyçja dështoi",
"login": "Hyrje",
"logoutQuestion": "Jeni i sigurt se doni të dilet dhe të ndalet konferenca?",
"loginQuestion": "Jeni i sigurt se doni të hyhet dhe të braktiset konferenca?",
"logoutQuestion": "Jeni i sigurt se doni të dilet dhe të braktiset konferenca?",
"logoutTitle": "Dalje",
"maxUsersLimitReached": "U mbërrit në kufi numri maksimum pjesëmarrësish. Konferenca është plot. Ju lutemi, lidhuni me të zotin e takimit, ose provoni më vonë!",
"maxUsersLimitReachedTitle": "U mbërrit në kufi numri maksimum pjesëmarrësish",
@@ -334,7 +368,7 @@
"muteEveryonesVideoTitle": "Të ndalet videoja e gjithkujt?",
"muteParticipantBody": "S’do jeni në gjendje të hiqni heshtimin për ta, por ata munden kurdo ta heqin për veten.",
"muteParticipantButton": "Heshtoje",
"muteParticipantsVideoBody": "S’do të jeni në gjendje të riaktivizoni kamerën e tyre, por ata munden kurdo ta riaktvizojnë për veten.",
"muteParticipantsVideoBody": "S’do të jeni në gjendje të riaktivizoni kamerën e tyre, por ata munden kurdo ta riaktivizojnë për veten.",
"muteParticipantsVideoBodyModerationOn": "S’do të jeni në gjendje të riaktivizoni kamerën e tyre dhe as ata s’do të munden.",
"muteParticipantsVideoButton": "Ndale videon",
"muteParticipantsVideoDialog": "Jeni i sigurt se doni të çaktivizoni kamerën e këtij pjesëmarrësi? S’do të jeni në gjendje të riaktivizoni kamerën, por ai mund ta riaktivizojë kurdo.",
@@ -349,8 +383,6 @@
"permissionCameraRequiredError": "Leja mbi kamerën është e domosdoshme për të marrë pjesë në konferenca me video. Ju lutemi, akordojeni që nga Rregullimet",
"permissionErrorTitle": "Leje e domosdoshme",
"permissionMicRequiredError": "Leja mbi mikrofonin është e domosdoshme për të marrë pjesë në konferenca me audio. Ju lutemi, akordojeni që nga Rregullimet",
"popupError": "Shfletuesi juaj i bllokon dritaret flluskë prej këtij sajti. Ju lutemi, aktivizoni flluskat te rregullimet e sigurisë të shfletuesit tuaj dhe riprovoni.",
"popupErrorTitle": "Flluska u bllokua",
"readMore": "më tepër",
"recentlyUsedObjects": "Së fundi përdorët objekte",
"recording": "Regjistrim",
@@ -367,6 +399,8 @@
"removePassword": "Hiqe $t(lockRoomPassword)",
"removeSharedVideoMsg": "Jeni i sigurt se doni të hiqni videon që keni ndarë me të tjerë?",
"removeSharedVideoTitle": "Hiqe videon e ndarë me të tjerë",
"renameBreakoutRoomLabel": "Emër dhome",
"renameBreakoutRoomTitle": "Riemërtoni aneks konsultimesh",
"reservationError": "Gabim sistemi rezervimi",
"reservationErrorMsg": "Kod gabimi: {{code}}, mesazh: {{msg}}",
"retry": "Riprovoni",
@@ -386,8 +420,10 @@
"sendPrivateMessageTitle": "Të dërgohet privatisht?",
"serviceUnavailable": "Shërbim jashtë funksionimi",
"sessTerminated": "Thirrja përfundoi",
"sessTerminatedReason": "Takimi u përfundua",
"sessionRestarted": "Thirrja rinisi për shkak të një problemi lidhjeje.",
"shareAudio": "Vazhdo",
"shareAudio": "Vazhdoni",
"shareAudioAltText": "që të ndani me të tjerë lëndën e dëshiruar, kaloni te \"Skedë Shfletuesi\", përzgjidhni lëndën, aktivizoni shenjën për \"ndani audio me të tjerë\" dhe mandej klikoni butonin \"ndaje me të tjerë\"",
"shareAudioTitle": "Si të ndahet audio me të tjerë",
"shareAudioWarningD1": "lypset të ndalni tregim ekrani, para se të ndani audion tuaj me të tjerë.",
"shareAudioWarningD2": "lypset të rinisni tregimin e ekranit tuaj dhe t’i vini shenjë mundësisë “ndani audio me të tjerë”.",
@@ -417,7 +453,25 @@
"thankYou": "Faleminderit që përdorni {{appName}}!",
"token": "token",
"tokenAuthFailed": "Na ndjeni, s’keni leje të merrni pjesë në këtë thirrje.",
"tokenAuthFailedReason": {
"audInvalid": "Vlerë `aud` e pavlefshme. Duhet të jetë `jitsi`.",
"contextNotFound": "Prej ngarkesës mungon objekti `context`.",
"expInvalid": "Vlerë `exp` e pavlefshme.",
"featureInvalid": "Veçori e pavlefshme: {{feature}}, gjasat janë të mos jetë sendërtuar ende.",
"featureValueInvalid": "Vlerë e pavlefshme për veçorinë: {{feature}}.",
"featuresNotFound": "Prej ngarkesës mungon objekti `features`.",
"headerNotFound": "Mungon krye.",
"issInvalid": "Vlerë `iss` e pavlefshme. Duhet të jetë `chat`.",
"kidMismatch": "ID kyçi (kid) nuk përputhet me sub.",
"kidNotFound": "Mungon ID Kyçi (kid).",
"nbfFuture": "Vlera `nbf` gjendet në të ardhmen.",
"nbfInvalid": "Vlerë `nbf` e pavlefshme.",
"payloadNotFound": "Mungon ngarkesë.",
"tokenExpired": "Token-i ka skaduar."
},
"tokenAuthFailedTitle": "Mirëfilltësimi dështoi",
"tokenAuthFailedWithReasons": "Na ndjeni, nuk keni leje të merrni pjesë në këtë thirrje. Arsye e mundshme: {{reason}}",
"tokenAuthUnsupported": "Nuk mbulohet URL token-i.",
"transcribing": "Transkriptim",
"unlockRoom": "Hiq $t(lockRoomPassword) takimi",
"user": "Përdorues",
@@ -429,8 +483,12 @@
"verifyParticipantTitle": "Verifikim përdoruesi",
"videoLink": "Lidhje videoje",
"viewUpgradeOptions": "Shihni mundësi përmirësimi",
"viewUpgradeOptionsContent": "Që të përfitoni përdorim të pakufizuar veçorish me pagesë, të tilla si regjistrimi, transcriptime, RTMP Streaming & etj, duhet të përmirësoni planin tuaj.",
"viewUpgradeOptionsContent": "Që të përfitoni përdorim të pakufizuar veçorish me pagesë, të tilla si regjistrimi, transkriptime, RTMP Streaming & etj, duhet të përmirësoni planin tuaj.",
"viewUpgradeOptionsTitle": "Zbuluat një veçori me pagesë!",
"whiteboardLimitContent": "Na ndjeni, është mbërritur te kufiri i tabelave të njëkohshme.",
"whiteboardLimitReference": "Për më tepër hollësi, ju lutemi, vizitoni",
"whiteboardLimitReferenceUrl": "sajtin tonë",
"whiteboardLimitTitle": "Kufizim përdorimi tabele",
"yourEntireScreen": "Krejt ekranin tuaj"
},
"documentSharing": {
@@ -443,6 +501,9 @@
"title": "Trupëzojeni këtë takim"
},
"feedback": {
"accessibilityLabel": {
"yourChoice": "Zgjedhja juaj: {{rating}}"
},
"average": "Çka",
"bad": "I dobët",
"detailsLabel": "Na thoni më tepër rreth tij.",
@@ -499,13 +560,16 @@
"noNumbers": "S’ka numra për përdorim.",
"noPassword": "Asnjë",
"noRoom": "S’u dha dhomë për të cilën të formësohet numri.",
"noWhiteboard": "S’u ngarkua dot tabela.",
"numbers": "Numra Për T’u Përdorur",
"password": "$t(lockRoomPasswordUppercase): ",
"reachedLimit": "Keni mbërritur në kufijtë e planit tuaj.",
"sip": "Adresë SIP",
"sipAudioOnly": "Adresë SIP vetëm për audio",
"title": "Ndani me të tjerë",
"tooltip": "Ndani me të tjerë lidhje dhe hollësi numrash për këtë takim",
"upgradeOptions": "Ju lutemi, shihni mundësitë e përmirësimit, te"
"upgradeOptions": "Ju lutemi, shihni mundësitë e përmirësimit, te",
"whiteboardError": "Gabim në ngarkimin e tabelës. Ju lutemi, riprovoni më vonë."
},
"inlineDialogFailure": {
"msg": "Ngecëm pak.",
@@ -607,13 +671,13 @@
"knockingParticipantList": "Listë pjesëmarrësish që duan të hyjnë",
"lobbyChatStartedNotification": "{{moderator}} filloi një fjalosje në holl me {{attendee}}",
"lobbyChatStartedTitle": "{{moderator}} ka filluar një fjalosje në holl me ju.",
"lobbyClosed": "Dhoma holl u mbyll.",
"nameField": "Jepni emrin tuaj",
"notificationLobbyAccessDenied": "Hyrja e {{targetParticipantName}} është hedhur poshtë nga {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} është lejuar të hyjë nga {{originParticipantName}}",
"notificationLobbyDisabled": "Holli është çaktivizuar nga {{originParticipantName}}",
"notificationLobbyEnabled": "Holli është aktivizuar nga {{originParticipantName}}",
"notificationTitle": "Holl",
"passwordField": "Jepni fjalëkalim takimi",
"passwordJoinButton": "Hyni",
"title": "Holl",
"toggleLabel": "Aktivizoni hollin"
@@ -646,6 +710,8 @@
"sessionToken": "Token Sesioni",
"start": "Nis Regjistrim",
"stop": "Ndale Regjistrimin",
"stopping": "Po ndalet Regjistrimi",
"wait": "Ju lutemi, prisni, teksa ruajmë regjistrimin tuaj",
"yes": "Po"
},
"lockRoomPassword": "fjalëkalim",
@@ -667,8 +733,11 @@
"connectedTwoMembers": "{{first}} dhe {{second}} tjetër hynë në takim",
"dataChannelClosed": "Rënie cilësie videoje",
"dataChannelClosedDescription": "Kanali urë u shkëput, kështu që cilësia e videos është kufizuar te vlera më e ulët.",
"disabledIframe": "Trupëzimi është menduar vetëm për qëllime demonstrimi, ndaj kjo thirrje do të ndërpritet pas {{timeout}} minutash.",
"disabledIframeSecondary": "Trupëzimi i {{domain}} është menduar vetëm për qëllime demonstrimi, ndaj kjo thirrje do të ndërpritet pas {{timeout}} minutash. Ju lutemi, për trupëzime të njëmendta përdorni <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a>!",
"disconnected": "u shkëput",
"displayNotifications": "Shfaq njoftime për",
"dontRemindMe": "Mos ma kujto",
"focus": "Fokusi te konferenca",
"focusFail": "{{component}} jo i passhëm - riprovoni pas {{ms}} sekondash",
"gifsMenu": "GIPHY",
@@ -677,6 +746,7 @@
"invitedOneMember": "{{name}} u ftua",
"invitedThreePlusMembers": "{{name}} dhe {{count}} të tjerë u ftuan",
"invitedTwoMembers": "{{first}} dhe {{second}} u ftuan",
"joinMeeting": "Hyni",
"kickParticipant": "{{kicked}} u përzu nga {{kicker}}",
"leftOneMember": "{{name}} doli nga takimi",
"leftThreePlusMembers": "{{name}} dhe mjaft të tjerë dolën nga takimi",
@@ -711,7 +781,6 @@
"newDeviceCameraTitle": "U pikas kamerë e re",
"noiseSuppressionDesktopAudioDescription": "Mbytja e zhurmave s’mund të aktivizohet teksa ndahet me të tjerët audioja e desktopit, ju lutemi, çaktivizojeni dhe riprovoni.",
"noiseSuppressionFailedTitle": "S’u arrit të nisej mbytja e zhurmave",
"noiseSuppressionNoTrackDescription": "Ju lutemi, së pari, çheshtoni mikrofonin tuaj.",
"noiseSuppressionStereoDescription": "Aktualisht nuk mbulohet mbytje zhurmash audioje stereo.",
"oldElectronClientDescription1": "Duket se përdorni një version të vjetër të klientit Jitsi Meet, i cili ka cenueshmëri të ditura sigurie. Ju lutemi, siguroni përditësimin me ",
"oldElectronClientDescription2": "montimin tonë më të ri",
@@ -735,13 +804,18 @@
"startSilentTitle": "Hytë pa zë në dalje!",
"suboptimalBrowserWarning": "Kemi frikë se funksionimi i takimit për ju s’do të jetë kushedi këtu. Po kërkojmë rrugë për ta përmirësuar këtë punë, por deri atëherë, ju lutemi, provoni të përdorni një nga <a href='{{recommendedBrowserPageLink}}' target='_blank'>shfletuesit e mbuluar plotësisht</a>.",
"suboptimalExperienceTitle": "Sinjalizim Mbi Shfletuesin",
"suggestRecordingAction": "Niseni",
"suggestRecordingDescription": "Do të donit të nisej një regjistrim?",
"suggestRecordingTitle": "Regjistroje këtë takim",
"unmute": "Çheshtoje",
"videoMutedRemotelyDescription": "Mundeni përherë ta rihapni.",
"videoMutedRemotelyTitle": "Videoja juaj u mbyll nga {{moderator}}",
"videoUnmuteBlockedDescription": "Heqja e heshtimit të kamerës dhe veprimi i tregimit të desktopit janë bllokuar përkohësisht për shkak kufizimesh të sistemit.",
"videoUnmuteBlockedTitle": "Heqja e heshtimit të kamerës dhe tregimi i desktopit janë bllokuar!",
"viewLobby": "Shihni hollin",
"waitingParticipants": "{{waitingParticipants}} vetë"
"waitingParticipants": "{{waitingParticipants}} vetë",
"whiteboardLimitDescription": "Ju lutemi, ruani ç’keni bërë, ngaqë së shpejti do të mbërrihet në kufi përdoruesi dhe tabela do të mbyllet.",
"whiteboardLimitTitle": "Përdorim tabele"
},
"participantsPane": {
"actions": {
@@ -752,6 +826,7 @@
"askUnmute": "Kërkoni heqje heshtimi",
"audioModeration": "Heqin heshtim të vetes",
"blockEveryoneMicCamera": "Bllokoni mikrofonin dhe kamerën e gjithkujt",
"breakoutRooms": "Dhoma anekse konsultimesh",
"invite": "Ftoni Dikë",
"moreModerationActions": "Më tepër mundësi moderimi",
"moreModerationControls": "Më tepër kontrolle moderimi",
@@ -769,7 +844,8 @@
"headings": {
"lobby": "Holli ({{count}})",
"participantsList": "Pjesëmarrës në takim ({{count}})",
"visitors": "Vizitorë ({{count}})",
"visitorRequests": " (requests {{count}})",
"visitors": "Vizitorë {{count}}",
"waitingLobby": "Duke pritur në holl ({{count}})"
},
"search": "Kërkoni te pjesëmarrësit",
@@ -859,12 +935,15 @@
"joinWithoutAudio": "Merrni pjesë pa audio",
"keyboardShortcuts": "Aktivizo shkurtore tastiere",
"linkCopied": "Lidhja u kopjua në të papastër",
"lookGood": "Mikrofoni juaj po punon si duhet",
"lookGood": "Gjithçka po punon si duhet",
"or": "ose",
"premeeting": "Para takimit",
"proceedAnyway": "Vazhdo, sido qoftë",
"recordingWarning": "Këtë thirrje pjesëmarrës të tjerë mund ta regjistrojnë",
"screenSharingError": "Gabim ndarjeje ekrani me të tjerë:",
"showScreen": "Aktivizoni skenë para takimit",
"startWithPhone": "Nise me audio telefoni",
"unsafeRoomConsent": "I kuptoj rreziqet, dëshiroj të marr pjesë te takimi",
"videoOnlyError": "Gabim video:",
"videoTrackError": "S’u krijua dot pistë video.",
"viewAllNumbers": "shihni krejt numrat"
@@ -885,9 +964,9 @@
},
"profile": {
"avatar": "avatar",
"setDisplayNameLabel": "Caktoni emrin tuaj për në ekran",
"setDisplayNameLabel": "Emër",
"setEmailInput": "Jepni email",
"setEmailLabel": "Caktoni email të gravatarit tuaj",
"setEmailLabel": "Jepni email të gravatarit tuaj",
"title": "Profil"
},
"raisedHand": "Do të donte të fliste",
@@ -919,17 +998,16 @@
"failedToStart": "S’u arrit të niset regjistrimi",
"fileSharingdescription": "Ndajeni regjistrimin me pjesëmarrësit në takim",
"highlight": "Nxjerrje në pah",
"highlightMoment": "Nxirni në pah një çast",
"highlightMoment": "Nxirrni në pah një çast",
"highlightMomentDisabled": "Mund të nxirrni në pah çaste kur fillon regjistrimi",
"highlightMomentSuccess": "Çasti u nxorr në pah",
"highlightMomentSuccess": "Çasti u nxor në pah",
"highlightMomentSucessDescription": "Çasti i nxjerrë në pah nga ju do të shtohet te përmbledhja e takimit.",
"inProgress": "Regjistrim ose transmetim drejtpërsëdrejti në ecuri e sipër",
"limitNotificationDescriptionNative": "Për shkak kërkesash të shumta, regjistrimi juaj do të kufizohet në {{limit}} min. Për regjistrime të pakufizuara provoni <3>{{app}}</3>.",
"limitNotificationDescriptionWeb": "Për shkak kërkesash të shumta, regjistrimi juaj do të kufizohet në {{limit}} min. Për regjistrime të pakufizuara provoni <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"linkGenerated": "Kemi prodhuar një lidhje për te regjistrimi juaj.",
"live": "DREJTPËRSËDREJTI",
"localRecordingNoNotificationWarning": "Regjistrimi s’do t’u njoftohet pjesëmarrësve të tjerë. Do t’ju duhet t’i vini në dijeni se takimi po regjistrohet.",
"localRecordingNoVideo": "Videoja nuk po regjistrohet",
"localRecordingNoVideo": "Videoja s’po regjistrohet",
"localRecordingStartWarning": "Ju lutemi, sigurohuni se e ndalni regjistrimin para se dilni nga takimi, që të mund ta ruani.",
"localRecordingStartWarningTitle": "Ndaleni regjistrimin që ta ruani",
"localRecordingVideoStop": "Ndalja e videos tuaj do të ndalë gjithashtu edhe regjistrimin vendor. Jeni i sigurt se doni të vazhdohet?",
@@ -944,13 +1022,15 @@
"onBy": "{{name}} nisi regjistrimin",
"onlyRecordSelf": "Regjistro vetëm rrjedhat e mia audio dhe video",
"pending": "Po përgatitet të regjistrohet takimi…",
"rec": "REC",
"recordAudioAndVideo": "Regjistro audio dhe video",
"recordTranscription": "Regjistro transkriptimin",
"saveLocalRecording": "Ruajeni lokalisht kartelën e regjistrimit (Beta)",
"serviceDescription": "Regjistrimi juaj do të ruhet nga shërbimi i regjistrimit",
"serviceDescriptionCloud": "Regjistrim në re",
"serviceDescriptionCloudInfo": "Takimet e regjistruara spastrohen automatikisht 24h pas kohës së regjistrimit të tyre.",
"serviceName": "Shërbim regjistrimi",
"sessionAlreadyActive": "Ky sesion po regjistrohet ose transmetohet drejtpërsëdrejti tashmë.",
"showAdvancedOptions": "Mundësi të mëtejshme",
"signIn": "Hyni",
"signOut": "Dilni",
"surfaceError": "Ju lutemi, përdorni skedën e tanishme.",
@@ -966,10 +1046,17 @@
"security": {
"about": "Takimit tuaj mund t’i shtoni një $t(lockRoomPassword). Pjesëmarrësve do t’u duhet të japin $t(lockRoomPassword) përpara se të lejohen të marrin pjesë në takim.",
"aboutReadOnly": "Pjesëmarrësit moderatorë mund t’i shtojnë takimit një $t(lockRoomPassword). Pjesëmarrësve do t’u duhet të japin $t(lockRoomPassword) përpara se të lejohen të marrin pjesë në takim.",
"insecureRoomNameWarning": "Emri i dhomës s’është pa rrezik. Pjesëmarrës të padëshiruar munden të marrin pjesë në konferencën tuaj. Shihni mundësinë e bërjes të sigurt të takimit tuaj, duke përdorur butonin e sigurisë.",
"title": "Mundësi Sigurie"
"insecureRoomNameWarningNative": "Emri i dhomës s’është pa rrezik. Pjesëmarrës të padëshiruar munden të hyjnë në takimin tuaj. {{recommendAction}} Mësoni më tepër rreth sigurimit të takimit tuaj ",
"insecureRoomNameWarningWeb": "Emri i dhomës s’është pa rrezik. Pjesëmarrës të padëshiruar munden të hyjnë në takimin tuaj. {{recommendAction}} Mësoni më tepër rreth sigurimit të takimit tuaj <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">këtu</a>.",
"title": "Mundësi Sigurie",
"unsafeRoomActions": {
"meeting": "Shihni mundësinë e sigurimit të takimit tuaj, duke përdorur butonin e sigurisë.",
"prejoin": "Shihni mundësinë e përdorimit për takimin të një emri më të veçantë.",
"welcome": "Shihni mundësinë e përdorimit për takimin të një emri më të veçantë, ose zgjidhni një nga sugjerimet."
}
},
"settings": {
"audio": "Audio",
"buttonLabel": "Rregullime",
"calendar": {
"about": "Integrimi me kalendar {{appName}} përdoret për të hyrë me sukses te kalendari juaj, që kështu të mund të lexohen veprimtari të ardhshme.",
@@ -990,9 +1077,11 @@
"maxStageParticipants": "Numër maksimum pjesëmarrësish që mund të fiksohen te skena kryesore (EKSPERIMENTALe)",
"microphones": "Mikrofona",
"moderator": "Moderator",
"more": "Më tepër",
"moderatorOptions": "Mundësi moderatori",
"more": "Të përgjitshme",
"name": "Emër",
"noDevice": "Asnjë",
"notifications": "Njoftime",
"participantJoined": "Hyri Pjesëmarrës",
"participantKnocking": "Pjesëmarrës hyri në holl",
"participantLeft": "Doli Pjesëmarrës",
@@ -1003,13 +1092,14 @@
"selectCamera": "Kamerë",
"selectMic": "Mikrofon",
"selfView": "Parje e vetes",
"sounds": "Tinguj",
"shortcuts": "Shkurtore",
"speakers": "Altoparlantë",
"startAudioMuted": "Gjithkush fillon i heshtuar",
"startReactionsMuted": "Heshto tinguj reagimesh për këdo",
"startVideoMuted": "Gjithkush fillon i fshehur",
"talkWhileMuted": "Flisni, teksa jeni i heshtuar",
"title": "Rregullime"
"title": "Rregullime",
"video": "Video"
},
"settingsView": {
"advanced": "Të mëtejshme",
@@ -1017,6 +1107,7 @@
"alertOk": "OK",
"alertTitle": "Kujdes",
"alertURLText": "URL-ja e dhënë për shërbyesin është e pavlefshme",
"apply": "Aplikoji",
"buildInfoSection": "Hollësi Montimi",
"conferenceSection": "Konferencë",
"disableCallIntegration": "Çaktivizo integrim thirrje me sistemin",
@@ -1027,19 +1118,21 @@
"displayNamePlaceholderText": "P.sh.: Zamir Gjoli",
"email": "Email",
"emailPlaceholderText": "email@example.com",
"gavatarMessage": "Nëse emal-i juaj është i përshoqëruar me një llogari Gravatar, do ta përdorim për të shfaqur foton e profilit tuaj.",
"goTo": "Kalo te",
"header": "Rregullime",
"help": "Ndihmë",
"links": "Lidhje",
"privacy": "Privatësi",
"profileSection": "Profil",
"sdkVersion": "Version SDK",
"serverURL": "URL Shërbyesi",
"showAdvanced": "Shfaq rregullime të mëtejshme",
"startCarModeInLowBandwidthMode": "Nën mënyrën “gjerësi e ulët bande” nis mënyrën automjet",
"startWithAudioMuted": "Fillo me audio të mbyllur",
"startWithVideoMuted": "Fillo me video të mbyllur",
"terms": "Terma",
"version": "Version"
"version": "Version aplikacioni"
},
"share": {
"dialInfoText": "\n\n=====\n\nThjesht doni t’i bini numrit në telefonin tuaj?\n\n{{defaultDialInNumber}}Klikoni mbi këtë lidhje, që të shihni numra telefoni për këtë takim\n{{dialInfoPageUrl}}",
@@ -1050,7 +1143,7 @@
"angry": "I zemëruar",
"disgusted": "I pështirosur",
"displayEmotions": "Shfaq emocione",
"fearful": "I firkësuar",
"fearful": "I frikësuar",
"happy": "I gëzuar",
"hours": "{{count}}h",
"minutes": "{{count}}m",
@@ -1058,7 +1151,7 @@
"neutral": "Asnjanës",
"sad": "I trishtuar",
"search": "Kërko",
"searchHint": "Kërkoni pjesëmarrësit",
"searchHint": "Kërkoni për pjesëmarrës",
"seconds": "{{count}}s",
"speakerStats": "Statistika Folësi",
"speakerTime": "Kohë Folësi",
@@ -1081,7 +1174,7 @@
"toolbar": {
"Settings": "Rregullime",
"accessibilityLabel": {
"Settings": "Shfaq/Fshih rregullimet",
"Settings": "Hap rregullimet",
"audioOnly": "Hap/Mbyll vetëm audion",
"audioRoute": "Përzgjidhni pajisje zëri",
"boo": "Ya",
@@ -1091,11 +1184,20 @@
"cc": "Shfaq/Fshih titra",
"chat": "Hapni / Mbyllni fjalosje",
"clap": "Duartrokitje",
"closeChat": "Mbylle fjalosjen",
"closeMoreActions": "Mbyll menunë Më Tepër Veprime",
"closeParticipantsPane": "Mbyll kuadratin pjesëmarrës",
"collapse": "Tkurre",
"document": "Shfaq/Fshih dokument të ndarë",
"documentClose": "Mbyll dokument të ndarë",
"documentOpen": "Hap dokument të ndarë",
"download": "Shkarkoni aplikacionet tona",
"embedMeeting": "Trupëzoni takimin",
"endConference": "Përfundoje takimin për të tërë",
"enterFullScreen": "Shiheni sa krejt ekrani",
"enterTileView": "Kaloni nën mënyrën me kuadrate",
"exitFullScreen": "Dil nga mënyra “Sa krejt ekrani”",
"exitTileView": "Dil nga mënyra me kuadrate",
"expand": "Zgjeroje",
"feedback": "Lini përshtypje",
"fullScreen": "Kalo nën/Dil nga mënyra “Sa krejt ekrani”",
@@ -1104,6 +1206,7 @@
"hangup": "Braktiseni takimin",
"heading": "Panel",
"help": "Ndihmë",
"hideWhiteboard": "Fshihe tabelën",
"invite": "Ftoni njerëz",
"kick": "Përzëre pjesëmarrësin",
"laugh": "E qeshur",
@@ -1113,21 +1216,25 @@
"lobbyButton": "Aktivizo/Çaktivizoni mënyrën holl",
"localRecording": "Shfaq/Fshih kontrolle regjistrimi vendor",
"lockRoom": "Aktivizo/Çaktivizo fjalëkalim takimi",
"lowerHand": "Ulni dorën",
"moreActions": "Më tepër veprime",
"moreActionsMenu": "Menu “Më tepër veprime”",
"moreOptions": "Shfaq më tepër mundësi",
"mute": "Heshtoje / Çheshtoje",
"mute": "Heshto mikrofonin",
"muteEveryone": "Heshto gjithkënd",
"muteEveryoneElse": "Heshto gjithkënd tjetër",
"muteEveryoneElsesVideoStream": "Ndal videon e gjithkujt tjetër",
"muteEveryonesVideoStream": "Ndal videon e gjithkujt",
"muteGUMPending": "Po lidhet mikrofoni juaj",
"noiseSuppression": "Mbytje zhurmash",
"participants": "Pjesëmarrës",
"openChat": "Hapni fjalosje",
"participants": "Hapni kuadrat pjesëmarrësish",
"pip": "Aktivizo/Çaktivizo mënyrën “Picture-in-Picture”",
"privateMessage": "Dërgoni mesazh privat",
"profile": "Përpunoni profilin tuaj",
"raiseHand": "Ngrini / Ulni dorën tuaj",
"reactionsMenu": "Hap / Mbyll menu reagimesh",
"raiseHand": "Ngrini dorën",
"reactions": "Reagime",
"reactionsMenu": "Menu reagimesh",
"recording": "Nis/Ndal regjistrimin",
"remoteMute": "Heshto pjesëmarrësin",
"remoteVideoMute": "Çaktivizo kamerën e pjesëmarrësit",
@@ -1135,20 +1242,25 @@
"selectBackground": "Përzgjidhni Sfond",
"selfView": "Shfaq/Fshih pamje të vetes",
"shareRoom": "Ftoni dikë",
"shareYourScreen": "Nisni / Ndalni tregimin e ekranit tuaj",
"shareYourScreen": "Nisni tregimin e ekranit tuaj",
"shareaudio": "Ndani audio me të tjerë",
"sharedvideo": "Aktivizo/Çaktivizo ndarje videoje me të tjerë",
"sharedvideo": "Ndani video me të tjerë",
"shortcuts": "Shfaq/Fshih shkurtore",
"show": "Shfaqe në skenë",
"showWhiteboard": "Shfaq tabelë",
"silence": "Heshtje",
"speakerStats": "Shfaq/Fshih statistika folësi",
"stopScreenSharing": "Ndalni tregimin e ekranit tuaj",
"stopSharedVideo": "Ndalni videon",
"surprised": "I befasuar",
"tileView": "Kalo në/Dil nga mënyra mozaik",
"toggleCamera": "Hap/Mbyll kamerën",
"toggleFilmstrip": "Shfaq/Fshih shirit filmi",
"unmute": "Hiqni heshtim mikrofoni",
"videoblur": "Aktivizo/Çaktivizo turbullim videoje",
"videomute": "Nis / Ndal kamerën",
"whiteboard": "Shfaq / Fshih tabelën"
"videomute": "Ndal kamerën",
"videomuteGUMPending": "Po lidhet kamera juaj",
"videounmute": "Nis kamerën"
},
"addPeople": "Shtoni persona te thirrja juaj",
"audioOnlyOff": "Çaktivizo mënyrën “Sasi e ulët të dhënash trafiku”",
@@ -1161,6 +1273,7 @@
"chat": "Hap / Mbyll fjalosje",
"clap": "Duartrokitje",
"closeChat": "Mbyll fjalosjen",
"closeParticipantsPane": "Mbylle kuadratin e pjesëmarrësve",
"closeReactionsMenu": "Mbyll menu reagimesh",
"disableNoiseSuppression": "Çaktivizo mbytje zhurmash",
"disableReactionSounds": "Mund të çaktivizoni tinguj reagimesh për këtë takim",
@@ -1169,6 +1282,7 @@
"download": "Shkarkoni aplikacione tonat",
"e2ee": "Fshehtëzim Skaj-Më-Skaj",
"embedMeeting": "Trupëzoni takim",
"enableNoiseSuppression": "Aktivizoni mbytje zhurmash",
"endConference": "Përfundoje takimin për të tërë",
"enterFullScreen": "Shiheni sa krejt ekrani",
"enterTileView": "Kalo te pamja me kuadrate",
@@ -1193,9 +1307,10 @@
"lowerYourHand": "Ulni dorën",
"moreActions": "Më tepër veprime",
"moreOptions": "Më tepër veprime",
"mute": "Heshtoje / Hiqi heshtimin",
"mute": "Heshto mikrofonin",
"muteEveryone": "Heshto gjithkënd",
"muteEveryonesVideo": "Çaktivizo videon e gjithkujt",
"muteGUMPending": "Po lidhet mikrofoni juaj",
"noAudioSignalDesc": "Nëse s’e keni heshtuar që nga rregullimet e sistemit, ose nga hardware-i, shihni mundësinë e ndërrimit të pajisjes.",
"noAudioSignalDescSuggestion": "Nëse s’e keni heshtuar që nga rregullimet e sistemit, ose nga hardware-i, shihni mundësinë e kalimit te pajisja e sugjeruar.",
"noAudioSignalDialInDesc": "Mund t’i bini numrit edhe duke përdorur:",
@@ -1210,7 +1325,7 @@
"pip": "Kalo nën mënyrën “Picture-in-Picture”",
"privateMessage": "Dërgo mesazh privat",
"profile": "Përpunoni profilin tuaj",
"raiseHand": "Ngrini / Ulni dorën",
"raiseHand": "Ngrini dorën",
"raiseYourHand": "Ngrini dorën",
"reactionBoo": "Dërgoni reagim me ya",
"reactionClap": "Dërgoni reagim me duartrokitje",
@@ -1218,6 +1333,7 @@
"reactionLike": "Dërgoni reagim me “thumbs up”",
"reactionSilence": "Dërgoni reagim me heshtje",
"reactionSurprised": "Dërgoni reagim të befasuari",
"reactions": "Reagime",
"security": "Mundësi sigurie",
"selectBackground": "Përzgjidhni sfond",
"shareRoom": "Ftoni dikë",
@@ -1237,17 +1353,17 @@
"talkWhileMutedPopup": "Po provoni të flisni? Jeni i heshtuar.",
"tileViewToggle": "Kalo në/Dil nga mënyra mozaik",
"toggleCamera": "Hapni/Mbyllni kamerën",
"videoSetting": "Rregullime videoje",
"videomute": "Nisni / Ndalni kamerën"
"unmute": "Hiqni heshtimin e mikrofonit",
"videoSettings": "Rregullime videoje",
"videomute": "Ndalni kamerën",
"videomuteGUMPending": "Po lidhet kamera juaj",
"videounmute": "Nisni kamerën"
},
"transcribing": {
"ccButtonTooltip": "Ndali / Nisi titrat",
"error": "Transkriptimi dështoi. Ju lutemi, riprovoni.",
"expandedLabel": "Transkriptimi aktualisht është aktiv",
"failedToStart": "S’u arrit të nisej transkriptim",
"labelToolTip": "Takimit po i bëhet transkriptim",
"off": "Transkriptimi u ndal",
"pending": "Po përgatitet të transkriptohet takimi…",
"sourceLanguageDesc": "Aktualisht si gjuhë takimi është caktuar <b>{{sourceLanguage}}</b>. <br/> Mund ta ndryshoni që nga ",
"sourceLanguageHere": "këtu",
"start": "Fillo shfaqje titrash",
@@ -1284,7 +1400,7 @@
"audioOnly": "AUD",
"audioOnlyExpanded": "Gjendeni nën mënyrën “gjerësi e ulët bande”. Nën këtë mënyrë, do të merrni vetëm audio dhe tregim ekrani.",
"bestPerformance": "Punimi më i mirë",
"callQuality": "Cilësi videoje",
"callQuality": "Cilësi Videoje (0 për punimin më të mirë, 3 për cilësinë më të lartë)",
"hd": "CL",
"hdTooltip": "Po shihet video në cilësi të lartë",
"highDefinition": "Cilësi e Lartë",
@@ -1326,6 +1442,10 @@
"videomute": "Pjesëmarrësi ka ndalur kamerën"
},
"virtualBackground": {
"accessibilityLabel": {
"currentBackground": "Sfondi i tanishëm: {{background}}",
"selectBackground": "Përzgjidhni një sfond"
},
"addBackground": "Shtoni sfond",
"apply": "Zbatoje",
"backgroundEffectError": "S’u arrit të zbatohej efekt sfondi.",
@@ -1343,13 +1463,20 @@
"none": "Asnjë",
"pleaseWait": "Ju lutemi, pritni…",
"removeBackground": "Hiqe sfondin",
"slightBlur": "Turbullim Paksa",
"slightBlur": "Gjysmë-Turbullim",
"title": "Sfonde virtualë",
"uploadedImage": "Ngarkoi figurën {{index}}",
"webAssemblyWarning": "Nuk mbulohet WebAssembly",
"webAssemblyWarningDescription": "WebAssembly e çaktivizuar ose e pambuluar nga ky shfletues"
},
"visitorsLabel": "Numër vizitorësh: {{count}}",
"visitors": {
"chatIndicator": "(vizitor)",
"labelTooltip": "Numër vizitorësh: {{count}}",
"notification": {
"description": "Që të merrni pjesë, ngrini dorën",
"title": "Jeni vizitor në takim"
}
},
"volumeSlider": "Rrëshqitës volumi",
"welcomepage": {
"accessibilityLabel": {
@@ -1382,6 +1509,7 @@
"microsoftLogo": "Stemë Microsoft-i",
"policyLogo": "Stemë Rregullash"
},
"meetingsAccessibilityLabel": "Takime",
"mobileDownLoadLinkAndroid": "Shkarkoni aplikacionin për celular me Android",
"mobileDownLoadLinkFDroid": "Shkarkoni aplikacionin për celular me F-Droid",
"mobileDownLoadLinkIos": "Shkarkoni aplikacionin për celular me iOS",
@@ -1405,6 +1533,7 @@
"whiteboard": {
"accessibilityLabel": {
"heading": "Tabelë shënimesh"
}
},
"screenTitle": "Tabelë shënimesh"
}
}

View File

@@ -305,6 +305,8 @@
"contactSupport": "Contact support",
"copied": "Copied",
"copy": "Copy",
"demoteParticipantDialog": "Are you sure you want to move this participant to visitor?",
"demoteParticipantTitle": "Move to visitor",
"dismiss": "Dismiss",
"displayNameRequired": "Hi! What’s your name?",
"done": "Done",
@@ -813,6 +815,7 @@
"videoUnmuteBlockedDescription": "Camera unmute and desktop sharing operation have been temporarily blocked because of system limits.",
"videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!",
"viewLobby": "View lobby",
"viewVisitors": "View visitors",
"waitingParticipants": "{{waitingParticipants}} people",
"whiteboardLimitDescription": "Please save your progress, as the user limit will soon be reached and the whiteboard will close.",
"whiteboardLimitTitle": "Whiteboard usage"
@@ -1419,6 +1422,7 @@
},
"videothumbnail": {
"connectionInfo": "Connection Info",
"demote": "Move to visitor",
"domute": "Mute",
"domuteOthers": "Mute everyone else",
"domuteVideo": "Disable camera",
@@ -1473,6 +1477,7 @@
"chatIndicator": "(visitor)",
"labelTooltip": "Number of visitors: {{count}}",
"notification": {
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
"description": "To participate raise your hand",
"title": "You are a visitor in the meeting"
}

View File

@@ -11,6 +11,7 @@ import {
} from '../../react/features/base/conference/actions';
import { isMobileBrowser } from '../../react/features/base/environment/utils';
import { setColorAlpha } from '../../react/features/base/util/helpers';
import { sanitizeUrl } from '../../react/features/base/util/uri';
import { setDocumentUrl } from '../../react/features/etherpad/actions';
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
import {
@@ -135,14 +136,16 @@ UI.unbindEvents = () => {
* @param {string} name etherpad id
*/
UI.initEtherpad = name => {
if (etherpadManager || !config.etherpad_base || !name) {
const etherpadBaseUrl = sanitizeUrl(config.etherpad_base);
if (etherpadManager || !etherpadBaseUrl || !name) {
return;
}
logger.log('Etherpad is enabled');
etherpadManager = new EtherpadManager(eventEmitter);
const url = new URL(name, config.etherpad_base);
const url = new URL(name, etherpadBaseUrl);
APP.store.dispatch(setDocumentUrl(url.toString()));

867
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,12 +17,13 @@
"readmeFilename": "README.md",
"dependencies": {
"@amplitude/react-native": "2.7.0",
"@braintree/sanitize-url": "7.0.0",
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
"@giphy/js-fetch-api": "4.7.1",
"@giphy/react-components": "6.8.1",
"@giphy/react-native-sdk": "2.3.0",
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.16/jitsi-excalidraw-0.0.16.tgz",
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.17/jitsi-excalidraw-0.0.17.tgz",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rnnoise-wasm": "0.1.0",
@@ -30,7 +31,6 @@
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@microsoft/microsoft-graph-client": "3.0.1",
"@mui/material": "5.12.1",
"@mui/styles": "5.12.0",
"@react-native-async-storage/async-storage": "1.19.4",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/netinfo": "11.1.0",
@@ -67,7 +67,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/v1773.0.0+cc9b0e60/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1789.0.0+a8f8666b/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -106,7 +106,7 @@
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "118.0.0",
"react-native-webrtc": "118.0.2",
"react-native-webview": "13.5.1",
"react-native-youtube-iframe": "2.3.0",
"react-redux": "7.2.9",
@@ -118,7 +118,7 @@
"seamless-scroll-polyfill": "2.1.8",
"semver": "7.5.4",
"text-encoding": "0.7.0",
"tss-react": "4.4.4",
"tss-react": "4.9.4",
"util": "0.12.1",
"uuid": "8.3.2",
"wasm-check": "2.0.1",
@@ -132,7 +132,7 @@
"@babel/plugin-proposal-export-default-from": "7.22.5",
"@babel/preset-env": "7.21.5",
"@babel/preset-react": "7.16.0",
"@jitsi/eslint-config": "4.1.5",
"@jitsi/eslint-config": "4.1.10",
"@types/amplitude-js": "8.16.5",
"@types/audioworklet": "0.0.29",
"@types/dom-screen-wake-lock": "1.0.1",
@@ -164,7 +164,7 @@
"css-loader": "6.8.1",
"eslint": "8.40.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jsdoc": "37.0.3",
"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",

View File

@@ -133,7 +133,7 @@ dependencies {
if (isNewArchitectureEnabled()) {
react {
jsRootDir = file("../src/")
jsRootDir = file("../")
libraryName = "JitsiMeetReactNative"
codegenJavaPackageName = "org.jitsi.meet.sdk"
}

View File

@@ -1,44 +0,0 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
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;
@ReactModule(name = JMOngoingConferenceModule.NAME)
class JMOngoingConferenceModule
extends ReactContextBaseJavaModule {
public static final String NAME = "JMOngoingConference";
public JMOngoingConferenceModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@ReactMethod
public void launch() {
Context context = getReactApplicationContext();
Activity currentActivity = getCurrentActivity();
JitsiMeetOngoingConferenceService.launch(context, currentActivity);
}
@ReactMethod
public void abort() {
Context context = getReactApplicationContext();
JitsiMeetOngoingConferenceService.abort(context);
}
@NonNull
@Override
public String getName() {
return NAME;
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
/**
* 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 JitsiMeetOngoingConferenceService extends Service {
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
public static void launch(Context context, Activity currentActivity) {
RNOngoingNotification.createOngoingConferenceNotificationChannel(currentActivity);
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
ComponentName componentName;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
componentName = context.startForegroundService(intent);
} else {
componentName = context.startService(intent);
}
} 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);
return;
}
if (componentName == null) {
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
}
}
public static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent);
}
@Override
public void onCreate() {
super.onCreate();
Notification notification = RNOngoingNotification.buildOngoingConferenceNotification(this);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
startForeground(RNOngoingNotification.NOTIFICATION_ID, notification);
JitsiMeetLogger.i(TAG + " Service started");
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
}

View File

@@ -1,40 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JitsiMeetReactNativePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules
= new ArrayList<>(Arrays.<NativeModule>asList(
new AndroidSettingsModule(reactContext),
new AppInfoModule(reactContext),
new AudioModeModule(reactContext),
new JMOngoingConferenceModule(reactContext),
new JavaScriptSandboxModule(reactContext),
new LocaleDetector(reactContext),
new LogBridgeModule(reactContext),
new PictureInPictureModule(reactContext),
new ProximityModule(reactContext),
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)
));
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.Random;
/**
* Helper class for creating the ongoing notification which is used with
* {@link JitsiMeetOngoingConferenceService}. 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 int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
static void createOngoingConferenceNotificationChannel(Activity currentActivity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
if (currentActivity == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
return;
}
NotificationManager notificationManager
= (NotificationManager) currentActivity.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel
= notificationManager.getNotificationChannel("JitsiOngoingConferenceChannel");
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}
channel = new NotificationChannel("JitsiOngoingConferenceChannel", currentActivity.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
}
static Notification buildOngoingConferenceNotification(Context context) {
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
return null;
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "JitsiOngoingConferenceChannel");
builder
.setCategory(NotificationCompat.CATEGORY_CALL)
.setContentTitle(context.getString(R.string.ongoing_notification_title))
.setContentText(context.getString(R.string.ongoing_notification_text))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setUsesChronometer(true)
.setAutoCancel(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
return builder.build();
}
}

View File

@@ -10,7 +10,7 @@
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@jitsi/js-utils": "2.1.3",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rtcstats": "9.5.1",
"@react-navigation/bottom-tabs": "6.5.8",
@@ -19,7 +19,7 @@
"@react-navigation/native": "6.1.7",
"@react-navigation/stack": "6.3.17",
"@xmldom/xmldom": "0.8.7",
"base64-js": "1.3.1",
"base64-js": "1.5.1",
"grapheme-splitter": "1.0.4",
"i18n-iso-countries": "6.8.0",
"i18next": "17.0.6",
@@ -27,7 +27,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/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1789.0.0+a8f8666b/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -37,7 +37,6 @@
"react-emoji-render": "1.2.4",
"react-i18next": "10.11.4",
"react-linkify": "1.0.0-alpha",
"react-native-callstats": "3.73.22",
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"react-native-svg-transformer": "1.1.0",
"react-native-tab-view": "3.5.2",
@@ -53,12 +52,13 @@
},
"peerDependencies": {
"@amplitude/react-native": "2.7.0",
"@braintree/sanitize-url": "7.0.0",
"@giphy/react-native-sdk": "2.3.0",
"@react-native-async-storage/async-storage": "1.19.3",
"@react-native-async-storage/async-storage": "1.19.4",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/netinfo": "9.4.1",
"@react-native-community/netinfo": "11.1.0",
"@react-native-community/slider": "4.4.3",
"@react-native-google-signin/google-signin": "10.0.1",
"@react-native-google-signin/google-signin": "10.1.0",
"react": "*",
"react-native": "*",
"react-native-background-timer": "2.4.1",
@@ -69,7 +69,7 @@
"react-native-get-random-values": "1.9.0",
"react-native-immersive-mode": "2.0.1",
"react-native-keep-awake": "4.0.0",
"react-native-orientation-locker": "1.5.0",
"react-native-orientation-locker": "1.6.0",
"react-native-pager-view": "6.2.0",
"react-native-paper": "5.10.3",
"react-native-performance": "5.0.0",
@@ -78,10 +78,11 @@
"react-native-sound": "0.11.2",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "13.13.0",
"react-native-video": "6.0.0-alpha.7",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "111.0.3",
"react-native-webview": "13.5.1"
"react-native-webrtc": "118.0.2",
"react-native-webview": "13.5.1",
"text-encoding": "0.7.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -396,13 +397,13 @@
"integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="
},
"node_modules/@jitsi/js-utils": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.1.3.tgz",
"integrity": "sha512-fXoNLu2JHQGPgzjCDG5qWPPStiXI+AxSzu1JIVY4dMULIFsw4a+doMgcjTIlSfB393J2xccKQYwZSejZUn9MEw==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.2.1.tgz",
"integrity": "sha512-4Ia4hWO7aTMGbYftzeBr+IHIu5YxiWwTlhsSK34z6925oNAUNI863WgYYGTcXkW/1yuM6LBZrnuZBySDqosISA==",
"dependencies": {
"@hapi/bourne": "^3.0.0",
"bowser": "2.7.0",
"js-md5": "0.7.3"
"js-md5": "0.7.3",
"ua-parser-js": "1.0.35"
}
},
"node_modules/@jitsi/js-utils/node_modules/js-md5": {
@@ -443,14 +444,6 @@
"sdp-verify": "checker.js"
}
},
"node_modules/@jitsi/sdp-simulcast": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@jitsi/sdp-simulcast/-/sdp-simulcast-0.4.0.tgz",
"integrity": "sha512-jXJM3/XM853UwPlxhSJx5Fc13G4158RIeTgjis3lA286Y7lp18GOR0xFlRvx/5UyhtE3qT2NzSmAwR+CpJohFw==",
"dependencies": {
"sdp-transform": "2.3.0"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -987,15 +980,24 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base-64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
},
"node_modules/base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/binary-extensions": {
"version": "2.2.0",
@@ -1010,11 +1012,6 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/bowser": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz",
"integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2443,14 +2440,6 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jssha": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.0.tgz",
"integrity": "sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==",
"engines": {
"node": "*"
}
},
"node_modules/jwt-decode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
@@ -2481,16 +2470,15 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
"integrity": "sha512-ky3q5gLeLDHFtenXe7+QlxAh33opz0a3bqEp8svA9urR1AICUi0QthOwmUerTdfSRFU3wfVObF9axlX1fbHZ3A==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1789.0.0+a8f8666b/lib-jitsi-meet.tgz",
"integrity": "sha512-nWHUC16US3x9dHz8/auJk8qAsiyvisWr954e5LcaFIImwR8ArD8ZVgyMMeQcnH+a8tFmzSB2NEZR4o/v0lKV9Q==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@jitsi/js-utils": "2.1.3",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rtcstats": "9.5.1",
"@jitsi/rtcstats": "9.7.0",
"@jitsi/sdp-interop": "git+https://github.com/jitsi/sdp-interop#3d49eb4aa26863a3f8d32d7581cdb4321244266b",
"@jitsi/sdp-simulcast": "0.4.0",
"@testrtc/watchrtc-sdk": "1.36.3",
"async-es": "3.2.4",
"base64-js": "1.3.1",
@@ -2509,6 +2497,30 @@
"webrtc-adapter": "8.1.1"
}
},
"node_modules/lib-jitsi-meet/node_modules/@jitsi/rtcstats": {
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.7.0.tgz",
"integrity": "sha512-OPURb3GO7zciPYoF/5IWLl2VVd90GD9KFlOnLQPalI2gUQDfxYNqn7JqeKAug1peTgml6I2o55lWYINuzPETWQ==",
"dependencies": {
"@jitsi/js-utils": "^2.2.0",
"@jitsi/logger": "2.0.2",
"sdp": "^3.0.3",
"uuid": "^8.3.2"
}
},
"node_modules/lib-jitsi-meet/node_modules/@jitsi/rtcstats/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/lib-jitsi-meet/node_modules/base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"node_modules/lib-jitsi-meet/node_modules/uuid": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
@@ -3054,15 +3066,6 @@
"tlds": "^1.199.0"
}
},
"node_modules/react-native-callstats": {
"version": "3.73.22",
"resolved": "https://registry.npmjs.org/react-native-callstats/-/react-native-callstats-3.73.22.tgz",
"integrity": "sha512-1l1n9XIca8VT7Vp/xaQY8grPDmgVp++nx0cYhelOWyn4TwBrWxIOPU1Ok4ODrNiYtnsh+/Dyqq+lSSwZksr7LA==",
"dependencies": {
"base-64": "0.1.0",
"jssha": "^3.2.0"
}
},
"node_modules/react-native-dialog": {
"version": "9.2.2",
"resolved": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
@@ -3570,6 +3573,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ua-parser-js": {
"version": "1.0.35",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz",
"integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
}
],
"engines": {
"node": "*"
}
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
@@ -4044,13 +4065,13 @@
"integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="
},
"@jitsi/js-utils": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.1.3.tgz",
"integrity": "sha512-fXoNLu2JHQGPgzjCDG5qWPPStiXI+AxSzu1JIVY4dMULIFsw4a+doMgcjTIlSfB393J2xccKQYwZSejZUn9MEw==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.2.1.tgz",
"integrity": "sha512-4Ia4hWO7aTMGbYftzeBr+IHIu5YxiWwTlhsSK34z6925oNAUNI863WgYYGTcXkW/1yuM6LBZrnuZBySDqosISA==",
"requires": {
"@hapi/bourne": "^3.0.0",
"bowser": "2.7.0",
"js-md5": "0.7.3"
"js-md5": "0.7.3",
"ua-parser-js": "1.0.35"
},
"dependencies": {
"js-md5": {
@@ -4091,14 +4112,6 @@
}
}
},
"@jitsi/sdp-simulcast": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@jitsi/sdp-simulcast/-/sdp-simulcast-0.4.0.tgz",
"integrity": "sha512-jXJM3/XM853UwPlxhSJx5Fc13G4158RIeTgjis3lA286Y7lp18GOR0xFlRvx/5UyhtE3qT2NzSmAwR+CpJohFw==",
"requires": {
"sdp-transform": "2.3.0"
}
},
"@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -4439,15 +4452,10 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base-64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"binary-extensions": {
"version": "2.2.0",
@@ -4459,11 +4467,6 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"bowser": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz",
"integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -5448,11 +5451,6 @@
"universalify": "^2.0.0"
}
},
"jssha": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.0.tgz",
"integrity": "sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w=="
},
"jwt-decode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
@@ -5476,14 +5474,13 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
"integrity": "sha512-ky3q5gLeLDHFtenXe7+QlxAh33opz0a3bqEp8svA9urR1AICUi0QthOwmUerTdfSRFU3wfVObF9axlX1fbHZ3A==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1789.0.0+a8f8666b/lib-jitsi-meet.tgz",
"integrity": "sha512-nWHUC16US3x9dHz8/auJk8qAsiyvisWr954e5LcaFIImwR8ArD8ZVgyMMeQcnH+a8tFmzSB2NEZR4o/v0lKV9Q==",
"requires": {
"@jitsi/js-utils": "2.1.3",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rtcstats": "9.5.1",
"@jitsi/rtcstats": "9.7.0",
"@jitsi/sdp-interop": "git+https://github.com/jitsi/sdp-interop#3d49eb4aa26863a3f8d32d7581cdb4321244266b",
"@jitsi/sdp-simulcast": "0.4.0",
"@testrtc/watchrtc-sdk": "1.36.3",
"async-es": "3.2.4",
"base64-js": "1.3.1",
@@ -5502,6 +5499,29 @@
"webrtc-adapter": "8.1.1"
},
"dependencies": {
"@jitsi/rtcstats": {
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.7.0.tgz",
"integrity": "sha512-OPURb3GO7zciPYoF/5IWLl2VVd90GD9KFlOnLQPalI2gUQDfxYNqn7JqeKAug1peTgml6I2o55lWYINuzPETWQ==",
"requires": {
"@jitsi/js-utils": "^2.2.0",
"@jitsi/logger": "2.0.2",
"sdp": "^3.0.3",
"uuid": "^8.3.2"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
}
}
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"uuid": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
@@ -5902,15 +5922,6 @@
"tlds": "^1.199.0"
}
},
"react-native-callstats": {
"version": "3.73.22",
"resolved": "https://registry.npmjs.org/react-native-callstats/-/react-native-callstats-3.73.22.tgz",
"integrity": "sha512-1l1n9XIca8VT7Vp/xaQY8grPDmgVp++nx0cYhelOWyn4TwBrWxIOPU1Ok4ODrNiYtnsh+/Dyqq+lSSwZksr7LA==",
"requires": {
"base-64": "0.1.0",
"jssha": "^3.2.0"
}
},
"react-native-dialog": {
"version": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"integrity": "sha512-MKbuBbovO8eGiAM9i6o0nrdBXivhRpzPQ+aVBXGJEPMH7RrCSNUKaCoEpkjfGHlTxjZimi6WjDCjjzCRSHlV1A=="
@@ -6263,6 +6274,11 @@
"is-typed-array": "^1.1.9"
}
},
"ua-parser-js": {
"version": "1.0.35",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz",
"integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA=="
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",

View File

@@ -11,7 +11,7 @@
"url": "git+https://github.com/jitsi/jitsi-meet.git"
},
"dependencies": {
"@jitsi/js-utils": "2.1.3",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rtcstats": "9.5.1",
"@react-navigation/bottom-tabs": "6.5.8",
@@ -20,7 +20,7 @@
"@react-navigation/native": "6.1.7",
"@react-navigation/stack": "6.3.17",
"@xmldom/xmldom": "0.8.7",
"base64-js": "1.3.1",
"base64-js": "1.5.1",
"grapheme-splitter": "1.0.4",
"i18n-iso-countries": "6.8.0",
"i18next": "17.0.6",
@@ -28,7 +28,7 @@
"i18next-http-backend": "^2.2.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1789.0.0+a8f8666b/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -53,12 +53,13 @@
},
"peerDependencies": {
"@amplitude/react-native": "2.7.0",
"@braintree/sanitize-url": "7.0.0",
"@giphy/react-native-sdk": "2.3.0",
"@react-native-async-storage/async-storage": "1.19.3",
"@react-native-async-storage/async-storage": "1.19.4",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/netinfo": "9.4.1",
"@react-native-community/netinfo": "11.1.0",
"@react-native-community/slider": "4.4.3",
"@react-native-google-signin/google-signin": "10.0.1",
"@react-native-google-signin/google-signin": "10.1.0",
"react-native": "*",
"react": "*",
"react-native-background-timer": "2.4.1",
@@ -72,16 +73,17 @@
"react-native-pager-view": "6.2.0",
"react-native-paper": "5.10.3",
"react-native-performance": "5.0.0",
"react-native-orientation-locker": "1.5.0",
"react-native-orientation-locker": "1.6.0",
"react-native-safe-area-context": "4.7.1",
"react-native-screens": "3.24.0",
"react-native-sound": "0.11.2",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "13.13.0",
"react-native-video": "6.0.0-alpha.7",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "111.0.3",
"react-native-webview": "13.5.1"
"react-native-webrtc": "118.0.2",
"react-native-webview": "13.5.1",
"text-encoding": "0.7.0"
},
"overrides": {
"@xmldom/xmldom": "0.8.7"
@@ -96,4 +98,4 @@
"keywords": [
"react-native"
]
}
}

View File

@@ -14,7 +14,7 @@ export default class AmplitudeHandler extends AbstractHandler {
/**
* Creates new instance of the Amplitude analytics handler.
*
* @param {Object} options -
* @param {Object} options - The amplitude options.
* @param {string} options.amplitudeAPPKey - The Amplitude app key required by the Amplitude API.
* @param {boolean} options.amplitudeIncludeUTM - Whether to include UTM parameters
* in the Amplitude events.

View File

@@ -14,7 +14,7 @@ class GoogleAnalyticsHandler extends AbstractHandler {
/**
* Creates new instance of the GA analytics handler.
*
* @param {Object} options -
* @param {Object} options - The Google Analytics options.
* @param {string} options.googleAnalyticsTrackingId - The GA track id
* required by the GA API.
*/
@@ -34,7 +34,7 @@ class GoogleAnalyticsHandler extends AbstractHandler {
/**
* Initializes the ga object.
*
* @param {Object} options -
* @param {Object} options - The Google Analytics options.
* @param {string} options.googleAnalyticsTrackingId - The GA track id
* required by the GA API.
* @returns {void}

View File

@@ -13,7 +13,7 @@ export default class MatomoHandler extends AbstractHandler {
/**
* Creates new instance of the Matomo handler.
*
* @param {Object} options -
* @param {Object} options - The matomo options.
* @param {string} options.matomoEndpoint - The Matomo endpoint.
* @param {string} options.matomoSiteID - The site ID.
*/
@@ -39,7 +39,7 @@ export default class MatomoHandler extends AbstractHandler {
/**
* Initializes the _paq object.
*
* @param {Object} options -
* @param {Object} options - The matomo options.
* @param {string} options.matomoEndpoint - The Matomo endpoint.
* @param {string} options.matomoSiteID - The site ID.
* @returns {void}

View File

@@ -3,6 +3,7 @@ import '../authentication/middleware';
import '../av-moderation/middleware';
import '../base/conference/middleware';
import '../base/config/middleware';
import '../base/i18n/middleware';
import '../base/jwt/middleware';
import '../base/known-domains/middleware';
import '../base/lastn/middleware';

View File

@@ -1,6 +1,5 @@
import '../base/app/middleware';
import '../base/connection/middleware';
import '../base/i18n/middleware';
import '../base/devices/middleware';
import '../base/media/middleware';
import '../dynamic-branding/middleware';

View File

@@ -133,7 +133,7 @@ function _upgradeRoleStarted(thenableWithCancel: Object) {
* Hides an authentication dialog where the local participant
* should authenticate.
*
* @returns {Function}.
* @returns {Function}
*/
export function hideLoginDialog() {
return hideDialog(LoginDialog);

View File

@@ -18,7 +18,7 @@ interface IProps {
* Implements the dialog that warns the user that the login will leave the conference.
*
* @param {Object} props - The props of the component.
* @returns {React$Element}.
* @returns {React$Element}
*/
const LoginQuestionDialog = ({ handler }: IProps) => {
const { t } = useTranslation();

View File

@@ -8,8 +8,15 @@ import { getReplaceParticipant } from '../config/functions';
import { connect, disconnect, hangup } from '../connection/actions';
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
import { JitsiConferenceEvents, JitsiE2ePingEvents } from '../lib-jitsi-meet';
import { setAudioMuted, setAudioUnmutePermissions, setVideoMuted, setVideoUnmutePermissions } from '../media/actions';
import {
gumPending,
setAudioMuted,
setAudioUnmutePermissions,
setVideoMuted,
setVideoUnmutePermissions
} from '../media/actions';
import { MEDIA_TYPE } from '../media/constants';
import { IGUMPendingState } from '../media/types';
import {
dominantSpeakerChanged,
participantKicked,
@@ -1052,6 +1059,11 @@ export function redirect(vnode: string, focusJid: string, username: string) {
.then(() => dispatch(conferenceWillInit()))
.then(() => dispatch(connect()))
.then(() => {
// Clear the gum pending state in case we have set it to pending since we are starting the
// conference without tracks.
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
if (typeof APP !== 'undefined') {
APP.conference.startConference([]);

View File

@@ -400,7 +400,7 @@ function _connectionFailed({ dispatch, getState }: IStore, next: Function, actio
descriptionKey: errors ? 'dialog.tokenAuthFailedWithReasons' : 'dialog.tokenAuthFailed',
descriptionArguments: { reason: errors },
titleKey: 'dialog.tokenAuthFailedTitle'
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
}
}

View File

@@ -1,40 +1,4 @@
export type ToolbarButton = 'camera' |
'chat' |
'closedcaptions' |
'desktop' |
'download' |
'embedmeeting' |
'etherpad' |
'feedback' |
'filmstrip' |
'fullscreen' |
'hangup' |
'help' |
'highlight' |
'invite' |
'linktosalesforce' |
'livestreaming' |
'microphone' |
'mute-everyone' |
'mute-video-everyone' |
'noisesuppression' |
'participants-pane' |
'profile' |
'raisehand' |
'reactions' |
'recording' |
'security' |
'select-background' |
'settings' |
'shareaudio' |
'sharedvideo' |
'shortcuts' |
'stats' |
'tileview' |
'toggle-camera' |
'videoquality' |
'whiteboard' |
'__end';
import { ToolbarButton } from '../../toolbox/types';
type ButtonsWithNotifyClick = 'camera' |
'chat' |
@@ -503,6 +467,7 @@ export interface IConfig {
preventExecution: boolean;
}>;
participantsPane?: {
enabled?: boolean;
hideModeratorSettingsTab?: boolean;
hideMoreActionsButton?: boolean;
hideMuteAllButton?: boolean;
@@ -535,6 +500,7 @@ export interface IConfig {
suggestRecording?: boolean;
};
remoteVideoMenu?: {
disableDemote?: boolean;
disableGrantModerator?: boolean;
disableKick?: boolean;
disablePrivateChat?: boolean;

View File

@@ -1,5 +1,3 @@
import { ToolbarButton } from './configType';
/**
* The prefix of the {@code localStorage} key into which {@link storeConfig}
* stores and from which {@link restoreConfig} restores.
@@ -9,50 +7,6 @@ import { ToolbarButton } from './configType';
*/
export const _CONFIG_STORE_PREFIX = 'config.js';
/**
* The list of all possible UI buttons.
*
* @protected
* @type Array<string>
*/
export const TOOLBAR_BUTTONS: ToolbarButton[] = [
'camera',
'chat',
'closedcaptions',
'desktop',
'download',
'embedmeeting',
'etherpad',
'feedback',
'filmstrip',
'fullscreen',
'hangup',
'help',
'highlight',
'invite',
'linktosalesforce',
'livestreaming',
'microphone',
'mute-everyone',
'mute-video-everyone',
'participants-pane',
'profile',
'raisehand',
'recording',
'security',
'select-background',
'settings',
'shareaudio',
'noisesuppression',
'sharedvideo',
'shortcuts',
'stats',
'tileview',
'toggle-camera',
'videoquality',
'whiteboard'
];
/**
* The toolbar buttons to show on premeeting screens.
*/
@@ -63,11 +17,6 @@ export const PREMEETING_BUTTONS = [ 'microphone', 'camera', 'select-background',
*/
export const THIRD_PARTY_PREJOIN_BUTTONS = [ 'microphone', 'camera', 'select-background' ];
/**
* The toolbar buttons to show when in visitors mode.
*/
export const VISITORS_MODE_BUTTONS = [ 'chat', 'hangup', 'raisehand', 'settings', 'tileview' ];
/**
* The set of feature flags.
*

View File

@@ -65,7 +65,7 @@ export function getMeetingRegion(state: IReduxState) {
* @returns {boolean}
*/
export function getSsrcRewritingFeatureFlag(state: IReduxState) {
return getFeatureFlag(state, FEATURE_FLAGS.SSRC_REWRITING);
return getFeatureFlag(state, FEATURE_FLAGS.SSRC_REWRITING) ?? true;
}
/**

View File

@@ -7,10 +7,8 @@ import {
IDeeplinkingConfig,
IDeeplinkingDesktopConfig,
IDeeplinkingMobileConfig,
NotifyClickButton,
ToolbarButton
NotifyClickButton
} from './configType';
import { TOOLBAR_BUTTONS } from './constants';
export * from './functions.any';
@@ -34,25 +32,6 @@ export function getReplaceParticipant(state: IReduxState): string | undefined {
return state['features/base/config'].replaceParticipant;
}
/**
* Returns the list of enabled toolbar buttons.
*
* @param {Object} state - The redux state.
* @returns {Array<string>} - The list of enabled toolbar buttons.
*/
export function getToolbarButtons(state: IReduxState): Array<string> {
const { toolbarButtons, customToolbarButtons } = state['features/base/config'];
const customButtons = customToolbarButtons?.map(({ id }) => id);
const buttons = Array.isArray(toolbarButtons) ? toolbarButtons : TOOLBAR_BUTTONS;
if (customButtons) {
buttons.push(...customButtons as ToolbarButton[]);
}
return buttons;
}
/**
* Returns the configuration value of web-hid feature.
*
@@ -63,20 +42,6 @@ export function getWebHIDFeatureConfig(state: IReduxState): boolean {
return state['features/base/config'].enableWebHIDFeature || false;
}
/**
* Checks if the specified button is enabled.
*
* @param {string} buttonName - The name of the button.
* {@link interfaceConfig}.
* @param {Object|Array<string>} state - The redux state or the array with the enabled buttons.
* @returns {boolean} - True if the button is enabled and false otherwise.
*/
export function isToolbarButtonEnabled(buttonName: string, state: IReduxState | Array<string>) {
const buttons = Array.isArray(state) ? state : getToolbarButtons(state);
return buttons.includes(buttonName);
}
/**
* Returns whether audio level measurement is enabled or not.
*

View File

@@ -1,6 +1,8 @@
import _ from 'lodash';
import { CONFERENCE_INFO } from '../../conference/components/constants';
import { TOOLBAR_BUTTONS } from '../../toolbox/constants';
import { ToolbarButton } from '../../toolbox/types';
import ReducerRegistry from '../redux/ReducerRegistry';
import { equals } from '../redux/functions';
@@ -16,10 +18,8 @@ import {
IDeeplinkingConfig,
IDeeplinkingDesktopConfig,
IDeeplinkingMobileConfig,
IMobileDynamicLink,
ToolbarButton
IMobileDynamicLink
} from './configType';
import { TOOLBAR_BUTTONS } from './constants';
import { _cleanupConfig, _setDeeplinkingDefaults } from './functions';
/**

View File

@@ -52,6 +52,16 @@ export const CONNECTION_WILL_CONNECT = 'CONNECTION_WILL_CONNECT';
*/
export const SET_LOCATION_URL = 'SET_LOCATION_URL';
/**
* The type of (redux) action which sets the preferVisitor in store.
*
* {
* type: SET_PREFER_VISITOR,
* preferVisitor: ?boolean
* }
*/
export const SET_PREFER_VISITOR = 'SET_PREFER_VISITOR';
/**
* The type of (redux) action which tells whether connection info should be displayed
* on context menu.

View File

@@ -16,7 +16,8 @@ import {
CONNECTION_ESTABLISHED,
CONNECTION_FAILED,
CONNECTION_WILL_CONNECT,
SET_LOCATION_URL
SET_LOCATION_URL,
SET_PREFER_VISITOR
} from './actionTypes';
import { JITSI_CONNECTION_URL_KEY } from './constants';
import logger from './logger';
@@ -180,6 +181,22 @@ export function setLocationURL(locationURL?: URL) {
};
}
/**
* To change prefer visitor in the store. Used later to decide what to request from jicofo on connection.
*
* @param {boolean} preferVisitor - The value to set.
* @returns {{
* type: SET_PREFER_VISITOR,
* preferVisitor: boolean
* }}
*/
export function setPreferVisitor(preferVisitor: boolean) {
return {
type: SET_PREFER_VISITOR,
preferVisitor
};
}
/**
* Opens new connection.
*

View File

@@ -10,6 +10,7 @@ import {
CONNECTION_FAILED,
CONNECTION_WILL_CONNECT,
SET_LOCATION_URL,
SET_PREFER_VISITOR,
SHOW_CONNECTION_INFO
} from './actionTypes';
import { ConnectionFailedError } from './types';
@@ -57,6 +58,11 @@ ReducerRegistry.register<IConnectionState>(
case SET_LOCATION_URL:
return _setLocationURL(state, action);
case SET_PREFER_VISITOR:
return assign(state, {
preferVisitor: action.preferVisitor
});
case SET_ROOM:
return _setRoom(state);

View File

@@ -164,6 +164,12 @@ export const NOTIFICATIONS_ENABLED = 'notifications.enabled';
*/
export const OVERFLOW_MENU_ENABLED = 'overflow-menu.enabled';
/**
* Flag indicating if participants should be enabled.
* Default: enabled (true).
*/
export const PARTICIPANTS_ENABLED = 'participants.enabled';
/**
* Flag indicating if Picture-in-Picture should be enabled.
* Default: auto-detected.

View File

@@ -8,7 +8,7 @@ import { toState } from '../../redux/functions';
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {number}.
* @returns {number}
*/
export function getClientWidth(stateful: IStateful) {
const state = toState(stateful)['features/base/responsive-ui'];
@@ -23,7 +23,7 @@ export function getClientWidth(stateful: IStateful) {
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {number}.
* @returns {number}
*/
export function getClientHeight(stateful: IStateful) {
const state = toState(stateful)['features/base/responsive-ui'];

View File

@@ -7,9 +7,9 @@ import { IReduxState } from '../../../../app/types';
import DeviceStatus from '../../../../prejoin/components/web/preview/DeviceStatus';
import { isRoomNameEnabled } from '../../../../prejoin/functions';
import Toolbox from '../../../../toolbox/components/web/Toolbox';
import { isButtonEnabled } from '../../../../toolbox/functions.web';
import { getConferenceName } from '../../../conference/functions';
import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from '../../../config/constants';
import { getToolbarButtons, isToolbarButtonEnabled } from '../../../config/functions.web';
import { withPixelLineHeight } from '../../../styles/functions.web';
import ConnectionStatus from './ConnectionStatus';
@@ -229,7 +229,7 @@ const PreMeetingScreen = ({
*/
function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
const { hiddenPremeetingButtons } = state['features/base/config'];
const toolbarButtons = getToolbarButtons(state);
const { toolbarButtons } = state['features/toolbox'];
const premeetingButtons = (ownProps.thirdParty
? THIRD_PARTY_PREJOIN_BUTTONS
: PREMEETING_BUTTONS).filter((b: any) => !(hiddenPremeetingButtons || []).includes(b));
@@ -244,7 +244,7 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
// toolbarButtons config overwrite.
_buttons: hiddenPremeetingButtons
? premeetingButtons
: premeetingButtons.filter(b => isToolbarButtonEnabled(b, toolbarButtons)),
: premeetingButtons.filter(b => isButtonEnabled(b, toolbarButtons)),
_premeetingBackground: premeetingBackground,
_roomName: isRoomNameEnabled(state) ? getConferenceName(state) : ''
};

View File

@@ -1,9 +1,9 @@
import { NativeModules, Platform } from 'react-native';
import { IReduxState, IStore } from '../../app/types';
import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
import { showNotification } from '../../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
import { PIP_WHILE_SCREEN_SHARING_ENABLED } from '../flags/constants';
import { getFeatureFlag } from '../flags/functions';
import JitsiMeetJS from '../lib-jitsi-meet';
import {
setScreenshareMuted,
@@ -14,7 +14,6 @@ import { VIDEO_MUTISM_AUTHORITY } from '../media/constants';
import { addLocalTrack, replaceLocalTrack } from './actions.any';
import { getLocalDesktopTrack, getTrackState, isLocalVideoTrackDesktop } from './functions.native';
const { JitsiMeetMediaProjectionModule } = NativeModules;
export * from './actions.any';
@@ -33,10 +32,7 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
if (enabled) {
const isSharing = isLocalVideoTrackDesktop(state);
if (isSharing) {
Platform.OS === 'android' && JitsiMeetMediaProjectionModule.abort();
} else {
Platform.OS === 'android' && JitsiMeetMediaProjectionModule.launch();
if (!isSharing) {
_startScreenSharing(dispatch, state);
}
} else {
@@ -56,7 +52,11 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
* @returns {void}
*/
async function _startScreenSharing(dispatch: IStore['dispatch'], state: IReduxState) {
setPictureInPictureEnabled(false);
const pipWhileScreenSharingEnabled = getFeatureFlag(state, PIP_WHILE_SCREEN_SHARING_ENABLED, false);
if (!pipWhileScreenSharingEnabled) {
setPictureInPictureEnabled(false);
}
try {
const tracks: any[] = await JitsiMeetJS.createLocalTracks({ devices: [ 'desktop' ] });

View File

@@ -145,6 +145,24 @@ const useStyles = makeStyles()(theme => {
pointerEvents: 'none'
},
contextMenuItemIconDisabled: {
'& svg': {
fill: `${theme.palette.text03} !important`
}
},
contextMenuItemLabelDisabled: {
color: theme.palette.text03,
'&:hover': {
background: 'none'
},
'& svg': {
fill: theme.palette.text03
}
},
contextMenuItemDrawer: {
padding: '13px 16px'
},
@@ -233,13 +251,15 @@ const ContextMenuItem = ({
tabIndex = { onClick ? tabIndex : undefined }>
{customIcon ? customIcon
: icon && <Icon
className = { styles.contextMenuItemIcon }
className = { cx(styles.contextMenuItemIcon,
disabled && styles.contextMenuItemIconDisabled) }
size = { 20 }
src = { icon } />}
{text && (
<TextWithOverflow
className = { cx(styles.text,
_overflowDrawer && styles.drawerText,
disabled && styles.contextMenuItemLabelDisabled,
textClassName) }
overflowType = { overflowType } >
{text}

View File

@@ -145,7 +145,7 @@ export function setColorAlpha(color: string, opacity: number) {
/**
* Gets the hexa rgb values for a shorthand css color.
*
* @param {string} color -
* @param {string} color - The shorthand css color.
* @returns {Array<number>} - Array containing parsed r, g, b values of the color.
*/
function parseShorthandColor(color: string) {

View File

@@ -1,3 +1,5 @@
import { sanitizeUrl as _sanitizeUrl } from '@braintree/sanitize-url';
import { parseURLParams } from './parseURLParams';
import { normalizeNFKC } from './strings';
@@ -657,3 +659,25 @@ export function appendURLHashParam(url: string, name: string, value: string) {
return newUrl.toString();
}
/**
* Sanitizes the given URL so that it's safe to use. If it's unsafe, null is returned.
*
* @param {string|URL} url - The URL that needs to be sanitized.
*
* @returns {URL?} - The sanitized URL, or null otherwise.
*/
export function sanitizeUrl(url?: string | URL): URL | null {
if (!url) {
return null;
}
const urlStr = url.toString();
const result = _sanitizeUrl(urlStr);
if (result === 'about:blank') {
return null;
}
return new URL(result);
}

View File

@@ -295,7 +295,7 @@ export default class MessageContainer extends AbstractMessageContainer<IProps, I
/**
* Check if a message is visible in view.
*
* @param {Element} message -
* @param {Element} message - The message.
*
* @returns {boolean}
*/

View File

@@ -1,11 +1,13 @@
import React, { useCallback } from 'react';
import { TouchableOpacity } from 'react-native';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { openHighlightDialog } from '../../../recording/actions.native';
import HighlightButton from '../../../recording/components/Recording/native/HighlightButton';
import RecordingLabel from '../../../recording/components/native/RecordingLabel';
import { getActiveSession } from '../../../recording/functions';
import VisitorsCountLabel from '../../../visitors/components/native/VisitorsCountLabel';
import RaisedHandsCountLabel from './RaisedHandsCountLabel';
@@ -28,7 +30,10 @@ interface IProps {
const AlwaysOnLabels = ({ createOnPress }: IProps) => {
const dispatch = useDispatch();
const openHighlightDialogCallback = useCallback(() => dispatch(openHighlightDialog()), [ dispatch ]);
const isStreaming = useSelector((state: IReduxState) =>
Boolean(getActiveSession(state, JitsiRecordingConstants.mode.STREAM)));
const openHighlightDialogCallback = useCallback(() =>
dispatch(openHighlightDialog()), [ dispatch ]);
return (<>
<TouchableOpacity
@@ -36,11 +41,14 @@ const AlwaysOnLabels = ({ createOnPress }: IProps) => {
onPress = { createOnPress(LABEL_ID_RECORDING) } >
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
</TouchableOpacity>
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { createOnPress(LABEL_ID_STREAMING) } >
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
</TouchableOpacity>
{
isStreaming
&& <TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { createOnPress(LABEL_ID_STREAMING) } >
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
</TouchableOpacity>
}
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { openHighlightDialogCallback }>

View File

@@ -9,6 +9,7 @@ import { getFeatureFlag } from '../../../base/flags/functions';
import AudioDeviceToggleButton from '../../../mobile/audio-mode/components/AudioDeviceToggleButton';
import PictureInPictureButton from '../../../mobile/picture-in-picture/components/PictureInPictureButton';
import ParticipantsPaneButton from '../../../participants-pane/components/native/ParticipantsPaneButton';
import { isParticipantsPaneEnabled } from '../../../participants-pane/functions';
import { isRoomNameEnabled } from '../../../prejoin/functions';
import ToggleCameraButton from '../../../toolbox/components/native/ToggleCameraButton';
import { isToolboxVisible } from '../../../toolbox/functions.native';
@@ -31,6 +32,11 @@ interface IProps {
*/
_createOnPress: Function;
/**
* Whether participants feature is enabled or not.
*/
_isParticipantsPaneEnabled: boolean;
/**
* Name of the meeting we're currently in.
*/
@@ -55,7 +61,7 @@ interface IProps {
* @returns {JSX.Element}
*/
const TitleBar = (props: IProps) => {
const { _visible } = props;
const { _isParticipantsPaneEnabled, _visible } = props;
if (!_visible) {
return null;
@@ -95,9 +101,13 @@ const TitleBar = (props: IProps) => {
<View style = { styles.titleBarButtonContainer }>
<AudioDeviceToggleButton styles = { styles.titleBarButton } />
</View>
<View style = { styles.titleBarButtonContainer }>
<ParticipantsPaneButton styles = { styles.titleBarButton } />
</View>
{
_isParticipantsPaneEnabled
&& <View style = { styles.titleBarButtonContainer }>
<ParticipantsPaneButton
styles = { styles.titleBarButton } />
</View>
}
</View>
);
};
@@ -115,6 +125,7 @@ function _mapStateToProps(state: IReduxState) {
return {
_conferenceTimerEnabled:
Boolean(getFeatureFlag(state, CONFERENCE_TIMER_ENABLED, true) && !hideConferenceTimer && startTimestamp),
_isParticipantsPaneEnabled: isParticipantsPaneEnabled(state),
_meetingName: getConferenceName(state),
_roomNameEnabled: isRoomNameEnabled(state),
_visible: isToolboxVisible(state)

View File

@@ -8,7 +8,6 @@ import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
* React component for Audio icon.
*
* @returns {JSX.Element} - The Audio icon.
*
*/
const AudioIcon = (): JSX.Element => (<Icon
color = { BaseTheme.palette.ui02 }

View File

@@ -25,7 +25,7 @@ export function shouldDisplayNotifications(stateful: IStateful) {
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {boolean}.
* @returns {boolean}
*/
export function arePollsDisabled(stateful: IStateful) {
const state = toState(stateful);

View File

@@ -1,9 +1,9 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import clsx from 'clsx';
import React from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { IReduxState, IStore } from '../../../app/types';
import { translate } from '../../../base/i18n/functions';
@@ -114,7 +114,7 @@ interface IProps extends AbstractProps, WithTranslation {
/**
* An object containing the CSS classes.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
/**
* The Redux dispatch function.
@@ -225,7 +225,8 @@ class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
* @returns {ReactElement}
*/
render() {
const { enableStatsDisplay, participantId, statsPopoverPosition, classes, t } = this.props;
const { enableStatsDisplay, participantId, statsPopoverPosition, t } = this.props;
const classes = withStyles.getClasses(this.props);
const visibilityClass = this._getVisibilityClass();
if (this.props._popoverDisabled) {
@@ -305,7 +306,8 @@ class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
* @returns {string}
*/
_getVisibilityClass() {
const { _isConnectionStatusInactive, _isConnectionStatusInterrupted, classes } = this.props;
const { _isConnectionStatusInactive, _isConnectionStatusInterrupted } = this.props;
const classes = withStyles.getClasses(this.props);
return this.state.showIndicator
|| this.props.alwaysVisible
@@ -399,4 +401,4 @@ export function _mapStateToProps(state: IReduxState, ownProps: any) {
};
}
export default connect(_mapStateToProps)(translate(withStyles(styles)(ConnectionIndicator)));
export default connect(_mapStateToProps)(translate(withStyles(ConnectionIndicator, styles)));

View File

@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { makeStyles } from 'tss-react/mui';
import { useStyles } from 'tss-react/mui';
import Icon from '../../../base/icons/components/Icon';
import { IconConnection, IconConnectionInactive } from '../../../base/icons/svg';
@@ -13,7 +13,7 @@ interface IProps {
/**
* An object containing the CSS classes.
*/
classes: any;
classes?: Partial<Record<'icon' | 'inactiveIcon', string>>;
/**
* A CSS class that interprets the current connection status as a color.
@@ -41,10 +41,6 @@ interface IProps {
track?: ITrack;
}
const useStyles = makeStyles()(() => {
return {};
});
export const ConnectionIndicatorIcon = ({
classes,
colorClass,
@@ -90,7 +86,7 @@ export const ConnectionIndicatorIcon = ({
return (
<span className = 'connection_ninja'>
<Icon
className = { cx(classes.icon, classes.inactiveIcon, colorClass) }
className = { cx(classes?.icon, classes?.inactiveIcon, colorClass) }
size = { 24 }
src = { IconConnectionInactive } />
</span>
@@ -107,7 +103,7 @@ export const ConnectionIndicatorIcon = ({
return (
<span className = { emptyIconWrapperClassName }>
<Icon
className = { cx(classes.icon, colorClass) }
className = { cx(classes?.icon, colorClass) }
size = { 16 }
src = { IconConnection } />
</span>

View File

@@ -8,9 +8,11 @@ import {
/**
* Contains all the callbacks to be notified when stats are updated.
*
* ```
* {
* userId: Function[]
* }.
* }
* ```
*/
const subscribers: any = {};

View File

@@ -1,8 +1,8 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import React from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { IReduxState, IStore } from '../../app/types';
import { getAvailableDevices } from '../../base/devices/actions.web';
@@ -37,7 +37,7 @@ interface IProps extends AbstractDialogTabProps, WithTranslation {
/**
* CSS classes object.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
/**
* Whether or not the audio selector can be interacted with. If true,
@@ -234,7 +234,6 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
*/
render() {
const {
classes,
hasAudioPermission,
hideAudioInputPreview,
hideAudioOutputPreview,
@@ -247,6 +246,8 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
} = this.props;
const { audioInput, audioOutput } = this._getSelectors();
const classes = withStyles.getClasses(this.props);
return (
<div className = { classes.container }>
{!iAmVisitor && <div
@@ -392,4 +393,4 @@ const mapStateToProps = (state: IReduxState) => {
};
};
export default connect(mapStateToProps)(withStyles(styles)(translate(AudioDevicesSelection)));
export default connect(mapStateToProps)(withStyles(translate(AudioDevicesSelection), styles));

View File

@@ -1,8 +1,8 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import React from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { IReduxState, IStore } from '../../app/types';
import { getAvailableDevices } from '../../base/devices/actions.web';
@@ -33,7 +33,7 @@ export interface IProps extends AbstractDialogTabProps, WithTranslation {
/**
* CSS classes object.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
/**
* The currently selected desktop share frame rate in the frame rate select dropdown.
@@ -203,13 +203,14 @@ class VideoDeviceSelection extends AbstractDialogTab<IProps, IState> {
*/
render() {
const {
classes,
hideAdditionalSettings,
hideVideoInputPreview,
localFlipX,
t
} = this.props;
const classes = withStyles.getClasses(this.props);
return (
<div className = { classes.container }>
{ !hideVideoInputPreview
@@ -366,4 +367,4 @@ const mapStateToProps = (state: IReduxState) => {
};
};
export default connect(mapStateToProps)(withStyles(styles)(translate(VideoDeviceSelection)));
export default connect(mapStateToProps)(withStyles(translate(VideoDeviceSelection), styles));

View File

@@ -23,7 +23,8 @@ MiddlewareRegistry.register(store => next => action => {
backgroundImageUrl,
brandedIcons,
didPageUrl,
inviteDomain
inviteDomain,
labels
} = action.value;
action.value = {
@@ -32,7 +33,8 @@ MiddlewareRegistry.register(store => next => action => {
backgroundImageUrl,
brandedIcons,
didPageUrl,
inviteDomain
inviteDomain,
labels
};
// The backend may send an empty string, make sure we skip that.

View File

@@ -230,7 +230,7 @@ ReducerRegistry.register<IDynamicBrandingState>(STORE_NAME, (state = DEFAULT_STA
* Transforms the branding images into an array of Images objects ready
* to be used as virtual backgrounds.
*
* @param {Array<string>} images -
* @param {Array<string>} images - The branding images.
* @private
* @returns {{Props}}
*/

View File

@@ -50,7 +50,7 @@ export function doesEveryoneSupportE2EE(stateful: IStateful) {
*
* @param {Function|Object} stateful - The redux store or {@code getState}
* function.
* @returns {boolean}.
* @returns {boolean}
*/
export function isMaxModeReached(stateful: IStateful) {
const participantCount = getParticipantCount(toState(stateful));
@@ -63,7 +63,7 @@ export function isMaxModeReached(stateful: IStateful) {
*
* @param {Function|Object} stateful - The redux store or {@code getState}
* function.
* @returns {boolean}.
* @returns {boolean}
*/
export function isMaxModeThresholdReached(stateful: IStateful) {
const participantCount = getParticipantCount(toState(stateful));

View File

@@ -4,6 +4,7 @@ import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
import { sanitizeUrl } from '../base/util/uri';
import { TOGGLE_DOCUMENT_EDITING } from './actionTypes';
import { setDocumentUrl } from './actions';
@@ -27,9 +28,10 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
({ value }: { value: string; }) => {
let url;
const { etherpad_base: etherpadBase } = getState()['features/base/config'];
const etherpadBaseUrl = sanitizeUrl(etherpadBase);
if (etherpadBase) {
url = new URL(value, etherpadBase).toString();
if (etherpadBaseUrl) {
url = new URL(value, etherpadBaseUrl.toString()).toString();
}
dispatch(setDocumentUrl(url));

View File

@@ -81,7 +81,7 @@ interface IProps {
class Filmstrip extends PureComponent<IProps> {
/**
* Whether the local participant should be rendered separately from the
* remote participants ie outside of their {@link ScrollView}.
* remote participants i.e. outside of their {@link ScrollView}.
*/
_separateLocalThumbnail: boolean;

View File

@@ -1,15 +1,14 @@
import { withStyles } from '@mui/styles';
import clsx from 'clsx';
import _ from 'lodash';
import React, { PureComponent } from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { FixedSizeGrid, FixedSizeList } from 'react-window';
import { withStyles } from 'tss-react/mui';
import { ACTION_SHORTCUT_TRIGGERED, createShortcutEvent, createToolbarEvent } from '../../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../analytics/functions';
import { IReduxState, IStore } from '../../../app/types';
import { getToolbarButtons } from '../../../base/config/functions.web';
import { isMobileBrowser } from '../../../base/environment/utils';
import { translate } from '../../../base/i18n/functions';
import Icon from '../../../base/icons/components/Icon';
@@ -213,7 +212,7 @@ interface IProps extends WithTranslation {
/**
* An object containing the CSS classes.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
/**
* The redux {@code dispatch} function.
@@ -349,10 +348,10 @@ class Filmstrip extends PureComponent <IProps, IState> {
_verticalViewBackground,
_verticalViewGrid,
_verticalViewMaxWidth,
classes,
filmstripType,
t
} = this.props;
const classes = withStyles.getClasses(this.props);
const { isMouseDown } = this.state;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
@@ -838,12 +837,12 @@ class Filmstrip extends PureComponent <IProps, IState> {
_renderToggleButton() {
const {
t,
classes,
_isVerticalFilmstrip,
_mainFilmstripVisible,
_topPanelFilmstrip,
_topPanelVisible
} = this.props;
const classes = withStyles.getClasses(this.props);
const icon = (_topPanelFilmstrip ? _topPanelVisible : _mainFilmstripVisible) ? IconArrowDown : IconArrowUp;
const actions = isMobileBrowser()
? { onTouchStart: this._onToggleButtonTouch }
@@ -884,11 +883,11 @@ class Filmstrip extends PureComponent <IProps, IState> {
*/
function _mapStateToProps(state: IReduxState, ownProps: any) {
const { _hasScroll = false, filmstripType, _topPanelFilmstrip, _remoteParticipants } = ownProps;
const toolbarButtons = getToolbarButtons(state);
const { toolbarButtons } = state['features/toolbox'];
const { iAmRecorder } = state['features/base/config'];
const { topPanelHeight, topPanelVisible, visible, width: verticalFilmstripWidth } = state['features/filmstrip'];
const { localScreenShare } = state['features/base/participants'];
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
const reduceHeight = state['features/toolbox'].visible && toolbarButtons?.length;
const remoteVideosVisible = shouldRemoteVideosBeVisible(state);
const { isOpen: shiftRight } = state['features/chat'];
const disableSelfView = getHideSelfView(state);
@@ -942,4 +941,4 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
};
}
export default withStyles(styles)(translate(connect(_mapStateToProps)(Filmstrip)));
export default withStyles(translate(connect(_mapStateToProps)(Filmstrip)), styles);

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { getToolbarButtons } from '../../../base/config/functions.web';
import { isMobileBrowser } from '../../../base/environment/utils';
import { LAYOUTS } from '../../../video-layout/constants';
import { getCurrentLayout } from '../../../video-layout/functions.web';
@@ -107,9 +106,9 @@ const MainFilmstrip = (props: IProps) => (
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState, _ownProps: any) {
const toolbarButtons = getToolbarButtons(state);
const { toolbarButtons } = state['features/toolbox'];
const { remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip'];
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
const reduceHeight = state['features/toolbox'].visible && toolbarButtons?.length;
const {
gridDimensions: dimensions = { columns: undefined,
rows: undefined },

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { getToolbarButtons } from '../../../base/config/functions.web';
import { isMobileBrowser } from '../../../base/environment/utils';
import { LAYOUTS, LAYOUT_CLASSNAMES } from '../../../video-layout/constants';
import { getCurrentLayout } from '../../../video-layout/functions.web';
@@ -108,9 +107,9 @@ const StageFilmstrip = (props: IProps) =>
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState, _ownProps: any) {
const toolbarButtons = getToolbarButtons(state);
const { toolbarButtons } = state['features/toolbox'];
const activeParticipants = getActiveParticipantsIds(state);
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
const reduceHeight = state['features/toolbox'].visible && toolbarButtons?.length;
const {
gridDimensions: dimensions = { columns: undefined,
rows: undefined },

View File

@@ -1,10 +1,10 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import clsx from 'clsx';
import debounce from 'lodash/debounce';
import React, { Component, KeyboardEvent, RefObject, createRef } from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { createScreenSharingIssueEvent } from '../../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../analytics/functions';
@@ -227,7 +227,7 @@ export interface IProps extends WithTranslation {
/**
* An object containing CSS classes.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof defaultStyles>, string>>;
/**
* The redux dispatch function.
@@ -351,8 +351,8 @@ const defaultStyles = (theme: Theme) => {
'& img': {
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
flexGrow: '1'
objectFit: 'contain' as const,
flexGrow: 1
}
},
@@ -946,9 +946,9 @@ class Thumbnail extends Component<IProps, IState> {
_isDominantSpeakerDisabled,
_participant,
_raisedHand,
_thumbnailType,
classes
_thumbnailType
} = this.props;
const classes = withStyles.getClasses(this.props);
className += ` ${DISPLAY_MODE_TO_CLASS_NAME[displayMode]}`;
@@ -994,7 +994,8 @@ class Thumbnail extends Component<IProps, IState> {
* @returns {Component}
*/
_renderGif() {
const { _gifSrc, classes } = this.props;
const { _gifSrc } = this.props;
const classes = withStyles.getClasses(this.props);
return _gifSrc && (
<div className = { classes.gif }>
@@ -1033,10 +1034,10 @@ class Thumbnail extends Component<IProps, IState> {
_shouldDisplayTintBackground,
_thumbnailType,
_videoTrack,
classes,
filmstripType,
t
} = this.props;
const classes = withStyles.getClasses(this.props);
const { id, name, pinned } = _participant || {};
const { isHovered, popoverVisible } = this.state;
const styles = this._getStyles();
@@ -1197,7 +1198,8 @@ class Thumbnail extends Component<IProps, IState> {
if (_isVirtualScreenshareParticipant) {
const { isHovered } = this.state;
const { _videoTrack, _isMobile, classes, _thumbnailType } = this.props;
const { _videoTrack, _isMobile, _thumbnailType } = this.props;
const classes = withStyles.getClasses(this.props);
return (
<VirtualScreenshareParticipant
@@ -1399,4 +1401,4 @@ function _mapStateToProps(state: IReduxState, ownProps: any): Object {
};
}
export default connect(_mapStateToProps)(withStyles(defaultStyles)(translate(Thumbnail)));
export default connect(_mapStateToProps)(withStyles(translate(Thumbnail), defaultStyles));

View File

@@ -17,7 +17,7 @@ interface IProps {
/**
* Class name for indicators container.
*/
className: string;
className?: string;
/**
* Whether or not the indicators are for the local participant.

View File

@@ -1,6 +1,6 @@
import clsx from 'clsx';
import React, { TouchEventHandler } from 'react';
import { useSelector } from 'react-redux';
import { useStyles } from 'tss-react/mui';
import VideoTrack from '../../../base/media/components/web/VideoTrack';
import { ITrack } from '../../../base/tracks/types';
@@ -15,7 +15,15 @@ interface IProps {
/**
* An object containing the CSS classes.
*/
classes: any;
classes?: Partial<Record<
'containerBackground' |
'indicatorsContainer' |
'indicatorsTopContainer' |
'tintBackground' |
'indicatorsBottomContainer' |
'indicatorsBackground',
string
>>;
/**
* The class name that will be used for the container.
@@ -125,6 +133,8 @@ const VirtualScreenshareParticipant = ({
style = { styles.video }
videoTrack = { videoTrack } />;
const { cx } = useStyles();
return (
<span
className = { containerClassName }
@@ -144,10 +154,10 @@ const VirtualScreenshareParticipant = ({
) }
style = { styles.thumbnail }>
{video}
<div className = { classes.containerBackground } />
<div className = { classes?.containerBackground } />
<div
className = { clsx(classes.indicatorsContainer,
classes.indicatorsTopContainer,
className = { cx(classes?.indicatorsContainer,
classes?.indicatorsTopContainer,
currentLayout === LAYOUTS.TILE_VIEW && 'tile-view-mode'
) }>
<ThumbnailTopIndicators
@@ -155,14 +165,14 @@ const VirtualScreenshareParticipant = ({
participantId = { participantId }
thumbnailType = { thumbnailType } />
</div>
{shouldDisplayTintBackground && <div className = { classes.tintBackground } />}
{shouldDisplayTintBackground && <div className = { classes?.tintBackground } />}
<div
className = { clsx(classes.indicatorsContainer,
classes.indicatorsBottomContainer,
className = { cx(classes?.indicatorsContainer,
classes?.indicatorsBottomContainer,
currentLayout === LAYOUTS.TILE_VIEW && 'tile-view-mode'
) }>
<ThumbnailBottomIndicators
className = { classes.indicatorsBackground }
className = { classes?.indicatorsBackground }
local = { false }
participantId = { participantId }
showStatusIndicators = { true } />

View File

@@ -1,7 +1,7 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import React from 'react';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { IReduxState, IStore } from '../../../../app/types';
import Avatar from '../../../../base/avatar/components/Avatar';
@@ -53,7 +53,7 @@ interface IProps extends AbstractProps {
/**
* Css classes.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
/**
* The redux {@code dispatch} function.
@@ -159,6 +159,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<IProps, IState> {
_sipInviteEnabled,
t
} = this.props;
const classes = withStyles.getClasses(this.props);
let isMultiSelectDisabled = this.state.addToCallInProgress;
const loadingMessage = 'addPeople.searching';
const noMatches = 'addPeople.noResults';
@@ -182,7 +183,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<IProps, IState> {
return (
<div
className = { this.props.classes.formWrap }
className = { classes.formWrap }
onKeyDown = { this._onKeyDown }>
<MultiSelectAutocomplete
id = 'invite-contacts-input'
@@ -443,7 +444,8 @@ class InviteContactsForm extends AbstractAddPeopleDialog<IProps, IState> {
*/
_renderFormActions() {
const { inviteItems } = this.state;
const { t, classes } = this.props;
const { t } = this.props;
const classes = withStyles.getClasses(this.props);
if (!inviteItems.length) {
return null;
@@ -511,4 +513,4 @@ function _mapStateToProps(state: IReduxState) {
};
}
export default translate(connect(_mapStateToProps)(withStyles(styles)(InviteContactsForm)));
export default translate(connect(_mapStateToProps)(withStyles(InviteContactsForm, styles)));

View File

@@ -1,8 +1,8 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import clsx from 'clsx';
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { withStyles } from 'tss-react/mui';
import { translate } from '../../../../base/i18n/functions';
import { withPixelLineHeight } from '../../../../base/styles/functions.web';
@@ -24,7 +24,7 @@ interface IProps extends WithTranslation {
/**
* An object containing the CSS classes.
*/
classes: any;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
/**
* Whether or not numbers should include links with the telephone protocol.
@@ -172,7 +172,8 @@ class DialInSummary extends Component<IProps, State> {
let contents;
const { conferenceID, error, loading, numbersEnabled } = this.state;
const { classes, hideError, showTitle, room, clickableNumbers, scrollable, t } = this.props;
const { hideError, showTitle, room, clickableNumbers, scrollable, t } = this.props;
const classes = withStyles.getClasses(this.props);
if (loading) {
contents = '';
@@ -309,4 +310,4 @@ class DialInSummary extends Component<IProps, State> {
}
}
export default translate(withStyles(styles)(DialInSummary));
export default translate(withStyles(DialInSummary, styles));

View File

@@ -116,9 +116,7 @@ export function admitMultiple(participants: Array<IKnockingParticipant>) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const conference = getCurrentConference(getState);
participants.forEach(p => {
conference?.lobbyApproveAccess(p.id);
});
conference?.lobbyApproveAccess(participants.map(p => p.id));
};
}

View File

@@ -43,7 +43,11 @@ import {
import { INotificationProps } from '../notifications/types';
import { open as openParticipantsPane } from '../participants-pane/actions';
import { getParticipantsPaneOpen } from '../participants-pane/functions';
import { isPrejoinPageVisible, shouldAutoKnock } from '../prejoin/functions';
import {
isPrejoinEnabledInConfig,
isPrejoinPageVisible,
shouldAutoKnock
} from '../prejoin/functions';
import {
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
@@ -269,7 +273,6 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
const { membersOnly } = state['features/base/conference'];
const nonFirstFailure = Boolean(membersOnly);
const { isDisplayNameRequiredError } = state['features/lobby'];
const { prejoinConfig } = state['features/base/config'];
if (error.name === JitsiConferenceErrors.MEMBERS_ONLY_ERROR) {
if (typeof error.recoverable === 'undefined') {
@@ -284,7 +287,9 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
dispatch(openLobbyScreen());
// if there was an error about display name and pre-join is not enabled
if (shouldAutoKnock(state) || (isDisplayNameRequiredError && !prejoinConfig?.enabled) || lobbyWaitingForHost) {
if (shouldAutoKnock(state)
|| (isDisplayNameRequiredError && !isPrejoinEnabledInConfig(state))
|| lobbyWaitingForHost) {
dispatch(startKnocking());
}

View File

@@ -11,6 +11,7 @@ import { getFeatureFlag } from '../../base/flags/functions';
import { IconCloseLarge } from '../../base/icons/svg';
import { toState } from '../../base/redux/functions';
import { cancelKnocking } from '../../lobby/actions.native';
import { isPrejoinEnabledInConfig } from '../../prejoin/functions';
import HeaderNavigationButton from './components/HeaderNavigationButton';
@@ -50,10 +51,8 @@ export function screenHeaderCloseButton(goBack: (e?: GestureResponderEvent | Rea
*/
export function isPrejoinPageEnabled(stateful: IStateful) {
const state = toState(stateful);
const { prejoinConfig } = state['features/base/config'];
const isPrejoinEnabledInConfig = prejoinConfig?.enabled;
return getFeatureFlag(state, PREJOIN_PAGE_ENABLED, isPrejoinEnabledInConfig ?? true);
return getFeatureFlag(state, PREJOIN_PAGE_ENABLED, isPrejoinEnabledInConfig(state));
}
/**

View File

@@ -1,5 +1,3 @@
import { NativeModules, Platform } from 'react-native';
import { getAppProp } from '../../base/app/functions';
import {
CONFERENCE_BLURRED,
@@ -11,7 +9,6 @@ import {
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../../base/media/actionTypes';
import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../../base/participants/actionTypes';
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../../base/redux/StateListenerRegistry';
import { READY_TO_CLOSE } from '../external-api/actionTypes';
import { participantToParticipantInfo } from '../external-api/functions';
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture/actionTypes';
@@ -19,7 +16,6 @@ import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture/actionTypes';
import { isExternalAPIAvailable } from './functions';
const externalAPIEnabled = isExternalAPIAvailable();
const { JMOngoingConference } = NativeModules;
/**
@@ -78,21 +74,3 @@ const { JMOngoingConference } = NativeModules;
return result;
});
/**
* Before enabling media projection service control on Android,
* we need to check if native modules are being used or not.
*/
Platform.OS === 'android' && !externalAPIEnabled && StateListenerRegistry.register(
state => state['features/base/conference'].conference,
(conference, previousConference) => {
if (!conference) {
JMOngoingConference.abort();
} else if (conference && !previousConference) {
JMOngoingConference.launch();
} else if (conference !== previousConference) {
JMOngoingConference.abort();
JMOngoingConference.launch();
}
}
);

View File

@@ -15,7 +15,7 @@ import {
isParticipantAudioMuted,
isParticipantVideoMuted
} from '../../../base/tracks/functions.native';
import { showConnectionStatus, showContextMenuDetails, showSharedVideoMenu } from '../../actions.native';
import { showContextMenuDetails, showSharedVideoMenu } from '../../actions.native';
import type { MediaState } from '../../constants';
import { getParticipantAudioMediaState, getParticipantVideoMediaState } from '../../functions';
@@ -117,11 +117,7 @@ class MeetingParticipantItem extends PureComponent<IProps> {
if (_fakeParticipant && _localVideoOwner) {
dispatch(showSharedVideoMenu(_participantID));
} else if (!_fakeParticipant) {
if (_local) {
dispatch(showConnectionStatus(_participantID));
} else {
dispatch(showContextMenuDetails(_participantID));
}
dispatch(showContextMenuDetails(_participantID, _local));
} // else no-op
}

View File

@@ -6,7 +6,6 @@ import { makeStyles } from 'tss-react/mui';
import { IReduxState } from '../../../app/types';
import { rejectParticipantAudio, rejectParticipantVideo } from '../../../av-moderation/actions';
import participantsPaneTheme from '../../../base/components/themes/participantsPaneTheme.json';
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
import { MEDIA_TYPE } from '../../../base/media/constants';
import { getParticipantById, isScreenShareParticipant } from '../../../base/participants/functions';
import { withPixelLineHeight } from '../../../base/styles/functions.web';
@@ -14,7 +13,7 @@ import Input from '../../../base/ui/components/web/Input';
import useContextMenu from '../../../base/ui/hooks/useContextMenu.web';
import { normalizeAccents } from '../../../base/util/strings.web';
import { getBreakoutRooms, getCurrentRoomId, isInBreakoutRoom } from '../../../breakout-rooms/functions';
import { showOverflowDrawer } from '../../../toolbox/functions.web';
import { isButtonEnabled, showOverflowDrawer } from '../../../toolbox/functions.web';
import { muteRemote } from '../../../video-menu/actions.web';
import { getSortedParticipantIds, isCurrentRoomRenamable, shouldRenderInviteButton } from '../../functions';
import { useParticipantDrawer } from '../../hooks';
@@ -184,7 +183,7 @@ function _mapStateToProps(state: IReduxState) {
});
const participantsCount = sortedParticipantIds.length;
const showInviteButton = shouldRenderInviteButton(state) && isToolbarButtonEnabled('invite', state);
const showInviteButton = shouldRenderInviteButton(state) && isButtonEnabled('invite', state);
const overflowDrawer = showOverflowDrawer(state);
const currentRoomId = getCurrentRoomId(state);
const currentRoom = getBreakoutRooms(state)[currentRoomId];

View File

@@ -9,9 +9,11 @@ import {
close as closeParticipantsPane,
open as openParticipantsPane
} from '../../../participants-pane/actions.web';
import { isParticipantsPaneEnabled } from '../../functions';
import ParticipantsCounter from './ParticipantsCounter';
/**
* The type of the React {@code Component} props of {@link ParticipantsPaneButton}.
*/
@@ -21,6 +23,11 @@ interface IProps extends AbstractButtonProps {
* Whether or not the participants pane is open.
*/
_isOpen: boolean;
/**
* Whether participants feature is enabled or not.
*/
_isParticipantsPaneEnabled: boolean;
}
/**
@@ -69,10 +76,16 @@ class ParticipantsPaneButton extends AbstractButton<IProps> {
* @returns {React$Node}
*/
render() {
const { _isParticipantsPaneEnabled } = this.props;
if (!_isParticipantsPaneEnabled) {
return null;
}
return (
<div
className = 'toolbar-button-with-badge'>
{super.render()}
{ super.render() }
<ParticipantsCounter />
</div>
);
@@ -89,7 +102,8 @@ function mapStateToProps(state: IReduxState) {
const { isOpen } = state['features/participants-pane'];
return {
_isOpen: isOpen
_isOpen: isOpen,
_isParticipantsPaneEnabled: isParticipantsPaneEnabled(state)
};
}

View File

@@ -7,7 +7,7 @@ import {
} from '../av-moderation/functions';
import { IStateful } from '../base/app/types';
import { getCurrentConference } from '../base/conference/functions';
import { INVITE_ENABLED } from '../base/flags/constants';
import { INVITE_ENABLED, PARTICIPANTS_ENABLED } from '../base/flags/constants';
import { getFeatureFlag } from '../base/flags/functions';
import { MEDIA_TYPE, type MediaType } from '../base/media/constants';
import {
@@ -309,3 +309,17 @@ export function isBreakoutRoomRenameAllowed(state: IReduxState) {
return isLocalModerator && isRenameBreakoutRoomsSupported;
}
/**
* Returns true if participants is enabled and false otherwise.
*
* @param {IStateful} stateful - The redux store, the redux
* {@code getState} function, or the redux state itself.
* @returns {boolean}
*/
export const isParticipantsPaneEnabled = (stateful: IStateful) => {
const state = toState(stateful);
const { enabled = true } = getParticipantsPaneConfig(state);
return Boolean(getFeatureFlag(state, PARTICIPANTS_ENABLED, true) && enabled);
};

View File

@@ -42,6 +42,16 @@ export function isDisplayNameRequired(state: IReduxState): boolean {
|| state['features/base/config']?.requireDisplayName);
}
/**
* Selector for determining if the prejoin page is enabled in config. Defaults to `true`.
*
* @param {IReduxState} state - The state of the app.
* @returns {boolean}
*/
export function isPrejoinEnabledInConfig(state: IReduxState): boolean {
return state['features/base/config'].prejoinConfig?.enabled ?? true;
}
/**
* Selector for determining if the prejoin display name field is visible.
*
@@ -154,7 +164,7 @@ export function isJoinByPhoneDialogVisible(state: IReduxState): boolean {
*/
export function isPrejoinPageVisible(state: IReduxState): boolean {
return Boolean(navigator.product !== 'ReactNative'
&& state['features/base/config'].prejoinConfig?.enabled
&& isPrejoinEnabledInConfig(state)
&& state['features/prejoin']?.showPrejoin
&& !(state['features/base/config'].enableForcedReload && state['features/prejoin'].skipPrejoinOnReload));
}
@@ -166,12 +176,11 @@ export function isPrejoinPageVisible(state: IReduxState): boolean {
* @returns {boolean}
*/
export function shouldAutoKnock(state: IReduxState): boolean {
const { iAmRecorder, iAmSipGateway, prejoinConfig } = state['features/base/config'];
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
const { userSelectedSkipPrejoin } = state['features/base/settings'];
const { autoKnock } = getLobbyConfig(state);
const isPrejoinEnabled = prejoinConfig?.enabled;
return Boolean(((isPrejoinEnabled && !userSelectedSkipPrejoin)
return Boolean(((isPrejoinEnabledInConfig(state) && !userSelectedSkipPrejoin)
|| autoKnock || (iAmRecorder && iAmSipGateway))
&& !state['features/lobby'].knocking);
}

View File

@@ -1,7 +1,6 @@
import { IReduxState } from '../app/types';
import { getToolbarButtons } from '../base/config/functions.web';
import { shouldDisplayReactionsButtons } from './functions.any';
import { isReactionsEnabled } from './functions.any';
export * from './functions.any';
@@ -22,5 +21,7 @@ export function getReactionsMenuVisibility(state: IReduxState): boolean {
* @returns {boolean}
*/
export function isReactionsButtonEnabled(state: IReduxState) {
return Boolean(getToolbarButtons(state).includes('reactions')) && shouldDisplayReactionsButtons(state);
const { toolbarButtons } = state['features/toolbox'];
return Boolean(toolbarButtons?.includes('reactions')) && isReactionsEnabled(state);
}

View File

@@ -19,6 +19,7 @@ import {
import { NOTIFICATION_TIMEOUT_TYPE, NOTIFICATION_TYPE } from '../notifications/constants';
import { INotificationProps } from '../notifications/types';
import { setRequestingSubtitles } from '../subtitles/actions.any';
import { isRecorderTranscriptionsRunning } from '../transcribing/functions';
import {
CLEAR_RECORDING_SESSIONS,
@@ -404,11 +405,11 @@ export function stopLocalVideoRecording() {
*/
export function showStartRecordingNotificationWithCallback(openRecordingDialog: Function) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
let state = getState();
const { recordings } = state['features/base/config'];
const { suggestRecording } = recordings || {};
const recordButtonProps = getRecordButtonProps(state);
const isAlreadyRecording = isRecordingRunning(state);
const isAlreadyRecording = isRecordingRunning(state) || isRecorderTranscriptionsRunning(state);
const wasNotificationShown = state['features/recording'].wasStartRecordingSuggested;
if (!suggestRecording
@@ -427,9 +428,9 @@ export function showStartRecordingNotificationWithCallback(openRecordingDialog:
customActionType: [ BUTTON_TYPES.PRIMARY ],
customActionNameKey: [ 'notify.suggestRecordingAction' ],
customActionHandler: [ () => {
state = getState();
const isModerator = isLocalParticipantModerator(state);
const { recordingService } = state['features/base/config'];
const canBypassDialog = isModerator
&& recordingService?.enabled
&& isJwtFeatureEnabled(state, 'recording', true);

View File

@@ -2,66 +2,57 @@ import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { IReduxState } from '../../app/types';
import { isTranscribing } from '../../transcribing/functions';
import { getSessionStatusToShow, isRecordingRunning } from '../functions';
import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
import { isRecorderTranscriptionsRunning } from '../../transcribing/functions';
import {
getActiveSession,
getSessionStatusToShow,
isRecordingRunning,
isRemoteParticipantRecordingLocally
} from '../functions';
interface IProps extends WithTranslation {
export interface IProps extends WithTranslation {
/**
* Whether this is the Jibri recorder participant.
*/
_iAmRecorder: boolean;
/**
* Whether the recording is currently running.
*/
_isRecordingRunning: boolean;
/**
* Whether this meeting is being transcribed.
*/
_isTranscribing: boolean;
*/
_isTranscribing: boolean;
/**
* Whether the recording/livestreaming/transcriber is currently running.
*/
_isVisible: boolean;
/**
* The status of the higher priority session.
*/
_status?: string;
/**
* An object containing the CSS classes.
*/
classes?: { [ key: string]: string; };
/**
* The recording mode this indicator should display.
*/
mode: string;
}
/**
* State of the component.
*/
interface IState {
/**
* True if the label status is stale, so it needs to be removed.
*/
staleLabel: boolean;
}
/**
* Abstract class for the {@code RecordingLabel} component.
*/
export default class AbstractRecordingLabel extends Component<IProps, IState> {
export default class AbstractRecordingLabel<P extends IProps = IProps> extends Component<P> {
/**
* Implements React {@code Component}'s render.
*
* @inheritdoc
*/
render() {
return this.props._isRecordingRunning && !this.props._iAmRecorder
? this._renderLabel() : null;
const { _iAmRecorder, _isVisible } = this.props;
return _isVisible && !_iAmRecorder ? this._renderLabel() : null;
}
/**
@@ -88,11 +79,18 @@ export default class AbstractRecordingLabel extends Component<IProps, IState> {
*/
export function _mapStateToProps(state: IReduxState, ownProps: any) {
const { mode } = ownProps;
const isLiveStreamingLabel = mode === JitsiRecordingConstants.mode.STREAM;
const _isTranscribing = isRecorderTranscriptionsRunning(state);
const isLivestreamingRunning = Boolean(getActiveSession(state, JitsiRecordingConstants.mode.STREAM));
const _isVisible = isLiveStreamingLabel
? isLivestreamingRunning // this is the livestreaming label
: isRecordingRunning(state) || isRemoteParticipantRecordingLocally(state)
|| _isTranscribing; // this is the recording label
return {
_isRecordingRunning: isRecordingRunning(state),
_isVisible,
_iAmRecorder: Boolean(state['features/base/config'].iAmRecorder),
_isTranscribing: isTranscribing(state),
_isTranscribing,
_status: getSessionStatusToShow(state, mode)
};
}

View File

@@ -7,6 +7,7 @@ import { isLocalParticipantModerator } from '../../../base/participants/function
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
import { isRecorderTranscriptionsRunning } from '../../../transcribing/functions';
import { getActiveSession, isCloudRecordingRunning } from '../../functions';
import { getLiveStreaming } from './functions';
@@ -140,7 +141,7 @@ export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
}
// disable the button if the recording is running.
if (visible && isCloudRecordingRunning(state)) {
if (visible && (isCloudRecordingRunning(state) || isRecorderTranscriptionsRunning(state))) {
_disabled = true;
_tooltip = 'dialog.liveStreamingDisabledBecauseOfActiveRecordingTooltip';
}

View File

@@ -21,11 +21,11 @@ export type LiveStreaming = {
};
export type LiveStreamingProps = {
dataPrivacyURL: string;
dataPrivacyURL?: string;
enabled: boolean;
helpURL: string;
helpURL?: string;
streamLinkRegexp: RegExp;
termsURL: string;
termsURL?: string;
};
/**
@@ -38,8 +38,6 @@ export interface IProps extends WithTranslation {
*/
_liveStreaming: LiveStreamingProps;
classes?: any;
/**
* Callback invoked when the entered stream key has changed.
*/

View File

@@ -1,4 +1,5 @@
import { IReduxState } from '../../../app/types';
import { sanitizeUrl } from '../../../base/util/uri';
import {
FOUR_GROUPS_DASH_SEPARATED,
@@ -15,15 +16,13 @@ import {
*/
export function getLiveStreaming(state: IReduxState) {
const { liveStreaming = {} } = state['features/base/config'];
const regexp = liveStreaming.validatorRegExpString
&& new RegExp(liveStreaming.validatorRegExpString);
const regexp = liveStreaming.validatorRegExpString && new RegExp(liveStreaming.validatorRegExpString);
return {
enabled: Boolean(liveStreaming.enabled),
helpURL: liveStreaming.helpLink || JITSI_LIVE_STREAMING_HELP_LINK,
termsURL: liveStreaming.termsLink || YOUTUBE_TERMS_URL,
dataPrivacyURL: liveStreaming.dataPrivacyLink || GOOGLE_PRIVACY_POLICY,
helpURL: sanitizeUrl(liveStreaming.helpLink || JITSI_LIVE_STREAMING_HELP_LINK)?.toString(),
termsURL: sanitizeUrl(liveStreaming.termsLink || YOUTUBE_TERMS_URL)?.toString(),
dataPrivacyURL: sanitizeUrl(liveStreaming.dataPrivacyLink || GOOGLE_PRIVACY_POLICY)?.toString(),
streamLinkRegexp: regexp || FOUR_GROUPS_DASH_SEPARATED
};
}

View File

@@ -107,7 +107,11 @@ class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
* @returns {void}
*/
_onOpenGooglePrivacyPolicy() {
Linking.openURL(this.props._liveStreaming.dataPrivacyURL);
const url = this.props._liveStreaming.dataPrivacyURL;
if (typeof url === 'string') {
Linking.openURL(url);
}
}
/**
@@ -118,10 +122,10 @@ class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
* @returns {void}
*/
_onOpenHelp() {
const helpURL = this.props._liveStreaming.helpURL;
const url = this.props._liveStreaming.helpURL;
if (typeof helpURL === 'string') {
Linking.openURL(helpURL);
if (typeof url === 'string') {
Linking.openURL(url);
}
}
@@ -132,7 +136,11 @@ class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
* @returns {void}
*/
_onOpenYoutubeTerms() {
Linking.openURL(this.props._liveStreaming.termsURL);
const url = this.props._liveStreaming.termsURL;
if (typeof url === 'string') {
Linking.openURL(url);
}
}
}

View File

@@ -1,7 +1,6 @@
import { connect } from 'react-redux';
import { IReduxState } from '../../../../app/types';
import { getToolbarButtons } from '../../../../base/config/functions.web';
import { openDialog } from '../../../../base/dialog/actions';
import { translate } from '../../../../base/i18n/functions';
import AbstractLiveStreamButton, {
@@ -50,11 +49,11 @@ class LiveStreamButton extends AbstractLiveStreamButton<IProps> {
*/
function _mapStateToProps(state: IReduxState, ownProps: any) {
const abstractProps = _abstractMapStateToProps(state, ownProps);
const toolbarButtons = getToolbarButtons(state);
const { toolbarButtons } = state['features/toolbox'];
let { visible } = ownProps;
if (typeof visible === 'undefined') {
visible = toolbarButtons.includes('livestreaming') && abstractProps.visible;
visible = Boolean(toolbarButtons?.includes('livestreaming') && abstractProps.visible);
}
return {

View File

@@ -1,15 +1,25 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import React from 'react';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { translate } from '../../../../base/i18n/functions';
import { withPixelLineHeight } from '../../../../base/styles/functions.web';
import Input from '../../../../base/ui/components/web/Input';
import AbstractStreamKeyForm, {
IProps, _mapStateToProps
IProps as AbstractProps,
_mapStateToProps
} from '../AbstractStreamKeyForm';
interface IProps extends AbstractProps {
/**
* An object containing the CSS classes.
*/
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
}
const styles = (theme: Theme) => {
return {
helperLink: {
@@ -46,7 +56,8 @@ class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
* @returns {ReactElement}
*/
render() {
const { classes, t, value } = this.props;
const { t, value } = this.props;
const classes = withStyles.getClasses(this.props);
return (
<div className = 'stream-key-form'>
@@ -99,4 +110,4 @@ class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
}
}
export default translate(connect(_mapStateToProps)(withStyles(styles)(StreamKeyForm)));
export default translate(connect(_mapStateToProps)(withStyles(StreamKeyForm, styles)));

View File

@@ -32,6 +32,11 @@ export interface IProps extends WithTranslation {
*/
_conference?: IJitsiConference;
/**
* Whether subtitles should be displayed or not.
*/
_displaySubtitles?: boolean;
/**
* Whether to show file recordings service, even if integrations
* are enabled.
@@ -69,6 +74,11 @@ export interface IProps extends WithTranslation {
*/
_screenshotCaptureEnabled: boolean;
/**
* The selected language for subtitles.
*/
_subtitlesLanguage: string | null;
/**
* The dropbox access token.
*/
@@ -336,8 +346,10 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
const {
_appKey,
_conference,
_displaySubtitles,
_isDropboxEnabled,
_rToken,
_subtitlesLanguage,
_token,
dispatch
} = this.props;
@@ -397,8 +409,9 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
});
}
if (this.state.shouldRecordTranscription) {
dispatch(setRequestingSubtitles(true, false, null));
if (this.state.selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE
&& this.state.shouldRecordTranscription) {
dispatch(setRequestingSubtitles(true, _displaySubtitles, _subtitlesLanguage));
}
_conference?.getMetadataHandler().setMetadata(RECORDING_METADATA_ID, {
@@ -433,17 +446,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
* @param {Object} state - The Redux state.
* @param {any} _ownProps - Component's own props.
* @private
* @returns {{
* _appKey: string,
* _autoTranscribeOnRecord: boolean,
* _conference: JitsiConference,
* _fileRecordingsServiceEnabled: boolean,
* _fileRecordingsServiceSharingEnabled: boolean,
* _isDropboxEnabled: boolean,
* _rToken:string,
* _tokenExpireDate: number,
* _token: string
* }}
* @returns {IProps}
*/
export function mapStateToProps(state: IReduxState, _ownProps: any) {
const {
@@ -451,16 +454,22 @@ export function mapStateToProps(state: IReduxState, _ownProps: any) {
dropbox = { appKey: undefined },
localRecording
} = state['features/base/config'];
const {
_displaySubtitles,
_language: _subtitlesLanguage
} = state['features/subtitles'];
return {
_appKey: dropbox.appKey ?? '',
_autoTranscribeOnRecord: shouldAutoTranscribeOnRecord(state),
_conference: state['features/base/conference'].conference,
_displaySubtitles,
_fileRecordingsServiceEnabled: recordingService?.enabled ?? false,
_fileRecordingsServiceSharingEnabled: isRecordingSharingEnabled(state),
_isDropboxEnabled: isDropboxEnabled(state),
_localRecordingEnabled: !localRecording?.disable,
_rToken: state['features/dropbox'].rToken ?? '',
_subtitlesLanguage,
_tokenExpireDate: state['features/dropbox'].expireDate,
_token: state['features/dropbox'].token ?? ''
};

View File

@@ -9,6 +9,7 @@ import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { setVideoMuted } from '../../../base/media/actions';
import { setRequestingSubtitles } from '../../../subtitles/actions.any';
import { stopLocalVideoRecording } from '../../actions';
import { RECORDING_METADATA_ID } from '../../constants';
import { getActiveSession } from '../../functions';
import { ISessionData } from '../../reducer';
@@ -25,6 +26,11 @@ export interface IProps extends WithTranslation {
*/
_conference?: IJitsiConference;
/**
* Whether subtitles should be displayed or not.
*/
_displaySubtitles?: boolean;
/**
* The redux representation of the recording session to be stopped.
*/
@@ -35,6 +41,11 @@ export interface IProps extends WithTranslation {
*/
_localRecording: boolean;
/**
* The selected language for subtitles.
*/
_subtitlesLanguage: string | null;
/**
* The redux dispatch function.
*/
@@ -76,22 +87,32 @@ export default class AbstractStopRecordingDialog<P extends IProps>
_onSubmit() {
sendAnalytics(createRecordingDialogEvent('stop', 'confirm.button'));
if (this.props._localRecording) {
this.props.dispatch(stopLocalVideoRecording());
if (this.props.localRecordingVideoStop) {
this.props.dispatch(setVideoMuted(true));
}
} else {
const { _fileRecordingSession } = this.props;
const {
_conference,
_displaySubtitles,
_fileRecordingSession,
_localRecording,
_subtitlesLanguage,
dispatch,
localRecordingVideoStop
} = this.props;
if (_fileRecordingSession) {
this.props._conference?.stopRecording(_fileRecordingSession.id);
this._toggleScreenshotCapture();
if (_localRecording) {
dispatch(stopLocalVideoRecording());
if (localRecordingVideoStop) {
dispatch(setVideoMuted(true));
}
} else if (_fileRecordingSession) {
_conference?.stopRecording(_fileRecordingSession.id);
this._toggleScreenshotCapture();
}
// TODO: this should be an action in transcribing. -saghul
this.props.dispatch(setRequestingSubtitles(false, false, null));
this.props.dispatch(setRequestingSubtitles(Boolean(_displaySubtitles), _displaySubtitles, _subtitlesLanguage));
this.props._conference?.getMetadataHandler().setMetadata(RECORDING_METADATA_ID, {
isTranscribingEnabled: false
});
return true;
}
@@ -112,16 +133,20 @@ export default class AbstractStopRecordingDialog<P extends IProps>
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _conference: JitsiConference,
* _fileRecordingSession: Object
* }}
* @returns {IProps}
*/
export function _mapStateToProps(state: IReduxState) {
const {
_displaySubtitles,
_language: _subtitlesLanguage
} = state['features/subtitles'];
return {
_conference: state['features/base/conference'].conference,
_displaySubtitles,
_fileRecordingSession:
getActiveSession(state, JitsiRecordingConstants.mode.FILE),
_localRecording: LocalRecordingManager.isRecordingLocally()
_localRecording: LocalRecordingManager.isRecordingLocally(),
_subtitlesLanguage
};
}

View File

@@ -36,9 +36,6 @@ interface ILocalRecordingManager {
const getMimeType = (): string => {
const possibleTypes = [
'video/mp4;codecs=h264',
'video/webm;codecs=h264',
'video/webm;codecs=vp9',
'video/webm;codecs=vp8'
];
@@ -164,7 +161,11 @@ const LocalRecordingManager: ILocalRecordingManager = {
this.audioContext = undefined;
this.audioDestination = undefined;
this.totalSize = MAX_SIZE;
setTimeout(() => this.saveRecording(this.recordingData, this.getFilename()), 1000);
setTimeout(() => {
if (this.recordingData.length > 0) {
this.saveRecording(this.recordingData, this.getFilename());
}
}, 1000);
}
},

View File

@@ -51,7 +51,9 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
* @returns {React$Component}
*/
_renderAdvancedOptions() {
if (!this._canStartTranscribing()) {
const { selectedRecordingService } = this.props;
if (selectedRecordingService !== RECORDING_TYPES.JITSI_REC_SERVICE || !this._canStartTranscribing()) {
return null;
}

View File

@@ -1,7 +1,7 @@
import { Theme } from '@mui/material';
import { withStyles } from '@mui/styles';
import React from 'react';
import { connect } from 'react-redux';
import { withStyles } from 'tss-react/mui';
import { openDialog } from '../../../../base/dialog/actions';
import { translate } from '../../../../base/i18n/functions';
@@ -30,7 +30,10 @@ interface IProps extends AbstractProps {
*/
_visible: boolean;
classes: any;
/**
* An object containing the CSS classes.
*/
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
}
/**
@@ -69,7 +72,7 @@ const styles = (theme: Theme) => {
boxSizing: 'border-box' as const,
color: theme.palette.uiBackground,
fontSize: '14px',
fontWeight: '400',
fontWeight: 400,
left: '4px',
padding: '16px',
position: 'absolute' as const,
@@ -79,7 +82,7 @@ const styles = (theme: Theme) => {
highlightNotificationButton: {
color: theme.palette.action01,
cursor: 'pointer',
fontWeight: '600',
fontWeight: 600,
marginTop: '8px'
}
};
@@ -181,9 +184,9 @@ export class HighlightButton extends AbstractHighlightButton<IProps, IState> {
const {
_disabled,
_visible,
classes,
t
} = this.props;
const classes = withStyles.getClasses(this.props);
if (!_visible) {
return null;
@@ -219,4 +222,4 @@ export class HighlightButton extends AbstractHighlightButton<IProps, IState> {
}
}
export default withStyles(styles)(translate(connect(_abstractMapStateToProps)(HighlightButton)));
export default withStyles(translate(connect(_abstractMapStateToProps)(HighlightButton)), styles);

View File

@@ -1,7 +1,6 @@
import { connect } from 'react-redux';
import { IReduxState } from '../../../../app/types';
import { getToolbarButtons } from '../../../../base/config/functions.web';
import { openDialog } from '../../../../base/dialog/actions';
import { translate } from '../../../../base/i18n/functions';
import AbstractRecordButton, {
@@ -49,8 +48,8 @@ class RecordingButton extends AbstractRecordButton<IProps> {
*/
export function _mapStateToProps(state: IReduxState) {
const abstractProps = _abstractMapStateToProps(state);
const toolbarButtons = getToolbarButtons(state);
const visible = toolbarButtons.includes('recording') && abstractProps.visible;
const { toolbarButtons } = state['features/toolbox'];
const visible = Boolean(toolbarButtons?.includes('recording') && abstractProps.visible);
return {
...abstractProps,

Some files were not shown because too many files have changed in this diff Show More