Compare commits

..

1 Commits

Author SHA1 Message Date
titus.moldovan
d5916cdb3a e2ee stuff 2022-06-23 15:18:13 +03:00
63 changed files with 570 additions and 973 deletions

View File

@@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;
import com.oney.WebRTCModule.WebRTCModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@@ -125,18 +126,14 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
* page.
*/
public void enterPictureInPicture() {
PictureInPictureModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
PictureInPictureModule.class);
if (pipModule != null
&& pipModule.isPictureInPictureSupported()
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
&& this.url != null) {
try {
pipModule.enterPictureInPicture();
} catch (RuntimeException re) {
JitsiMeetLogger.e(re, "Failed to enter PiP mode");
}
try {
WebRTCModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
WebRTCModule.class);
pipModule.addDecryptors();
}
catch (Exception e) {
int a = 1;
}
}

View File

@@ -28,6 +28,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
import com.oney.WebRTCModule.WebRTCModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@@ -84,34 +85,10 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
*/
@TargetApi(Build.VERSION_CODES.O)
public void enterPictureInPicture() {
if (!isEnabled) {
return;
}
if (!isSupported) {
throw new IllegalStateException("Picture-in-Picture not supported");
}
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
throw new IllegalStateException("No current Activity!");
}
JitsiMeetLogger.i(TAG + " Entering Picture-in-Picture");
PictureInPictureParams.Builder builder
= new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(1, 1));
// https://developer.android.com/reference/android/app/Activity.html#enterPictureInPictureMode(android.app.PictureInPictureParams)
//
// The system may disallow entering picture-in-picture in various cases,
// including when the activity is not visible, if the screen is locked
// or if the user has an activity pinned.
if (!currentActivity.enterPictureInPictureMode(builder.build())) {
throw new RuntimeException("Failed to enter Picture-in-Picture");
}
WebRTCModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
WebRTCModule.class);
pipModule.addDecryptors();
}
/**
@@ -123,12 +100,10 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void enterPictureInPicture(Promise promise) {
try {
enterPictureInPicture();
promise.resolve(null);
} catch (RuntimeException re) {
promise.reject(re);
}
WebRTCModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
WebRTCModule.class);
pipModule.addDecryptors();
}
@ReactMethod

View File

@@ -35,6 +35,8 @@ import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import org.devio.rn.splashscreen.SplashScreenModule;
import org.webrtc.Loggable;
import org.webrtc.Logging;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.audio.AudioDeviceModule;
@@ -57,6 +59,13 @@ class ReactInstanceManagerHolder {
*/
private static ReactInstanceManager reactInstanceManager;
private static Loggable webrtcLogger = new Loggable() {
@Override
public void onLogMessage(String message, Logging.Severity severity, String tag) {
Log.d(tag,message);
}
};
private static List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> nativeModules
= new ArrayList<>(Arrays.<NativeModule>asList(
@@ -88,6 +97,8 @@ class ReactInstanceManagerHolder {
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
options.setInjectableLogger(webrtcLogger);
options.setLoggingSeverity(Logging.Severity.LS_VERBOSE);
nativeModules.add(new WebRTCModule(reactContext, options));

View File

@@ -3094,12 +3094,34 @@ export default {
* @param email {string} the new email
*/
changeLocalEmail(email = '') {
const localParticipant = getLocalParticipant(APP.store.getState());
const formattedEmail = String(email).trim();
if (formattedEmail === localParticipant.email) {
return;
}
const localId = localParticipant.id;
APP.store.dispatch(participantUpdated({
// XXX Only the local participant is allowed to update without
// stating the JitsiConference instance (i.e. participant property
// `conference` for a remote participant) because the local
// participant is uniquely identified by the very fact that there is
// only one local participant.
id: localId,
local: true,
email: formattedEmail
}));
APP.store.dispatch(updateSettings({
email: formattedEmail
}));
APP.API.notifyEmailChanged(localId, {
email: formattedEmail
});
sendData(commands.EMAIL, formattedEmail);
},
@@ -3108,12 +3130,29 @@ export default {
* @param url {string} the new url
*/
changeLocalAvatarUrl(url = '') {
const { avatarURL, id } = getLocalParticipant(APP.store.getState());
const formattedUrl = String(url).trim();
if (formattedUrl === avatarURL) {
return;
}
APP.store.dispatch(participantUpdated({
// XXX Only the local participant is allowed to update without
// stating the JitsiConference instance (i.e. participant property
// `conference` for a remote participant) because the local
// participant is uniquely identified by the very fact that there is
// only one local participant.
id,
local: true,
avatarURL: formattedUrl
}));
APP.store.dispatch(updateSettings({
avatarURL: formattedUrl
}));
sendData(commands.AVATAR_URL, url);
},
@@ -3154,6 +3193,23 @@ export default {
*/
changeLocalDisplayName(nickname = '') {
const formattedNickname = getNormalizedDisplayName(nickname);
const { id, name } = getLocalParticipant(APP.store.getState());
if (formattedNickname === name) {
return;
}
APP.store.dispatch(participantUpdated({
// XXX Only the local participant is allowed to update without
// stating the JitsiConference instance (i.e. participant property
// `conference` for a remote participant) because the local
// participant is uniquely identified by the very fact that there is
// only one local participant.
id,
local: true,
name: formattedNickname
}));
APP.store.dispatch(updateSettings({
displayName: formattedNickname

View File

@@ -271,9 +271,8 @@ var config = {
// Recording
// DEPRECATED. Use recordingService.enabled instead.
// Whether to enable file recording or not.
// fileRecordingsEnabled: false,
// Enable the dropbox integration.
// dropbox: {
// appKey: '<APP_KEY>' // Specify your app key here.
@@ -283,27 +282,14 @@ var config = {
// redirectURI:
// 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
// },
// recordingService: {
// // When integrations like dropbox are enabled only that will be shown,
// // by enabling fileRecordingsServiceEnabled, we show both the integrations
// // and the generic recording service (its configuration and storage type
// // depends on jibri configuration)
// enabled: false,
// // Whether to show the possibility to share file recording with other people
// // (e.g. meeting participants), based on the actual implementation
// // on the backend.
// sharingEnabled: false,
// // Hide the warning that says we only store the recording for 24 hours.
// hideStorageWarning: false
// },
// DEPRECATED. Use recordingService.enabled instead.
// When integrations like dropbox are enabled only that will be shown,
// by enabling fileRecordingsServiceEnabled, we show both the integrations
// and the generic recording service (its configuration and storage type
// depends on jibri configuration)
// fileRecordingsServiceEnabled: false,
// DEPRECATED. Use recordingService.sharingEnabled instead.
// Whether to show the possibility to share file recording with other people
// (e.g. meeting participants), based on the actual implementation
// on the backend.
// fileRecordingsServiceSharingEnabled: false,
// Whether to enable live streaming or not.
@@ -317,43 +303,25 @@ var config = {
// notifyAllParticipants: false
// },
// DEPRECATED. Use transcription.enabled instead.
// Transcription (in interface_config,
// subtitles and buttons can be configured)
// transcribingEnabled: false,
// DEPRECATED. Use transcription.useAppLanguage instead.
// If true transcriber will use the application language.
// The application language is either explicitly set by participants in their settings or automatically
// detected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its
// default language then transcriptions for that participant will be in french.
// Defaults to true.
// transcribeWithAppLanguage: true,
// DEPRECATED. Use transcription.preferredLanguage instead.
// Transcriber language. This settings will only work if "transcribeWithAppLanguage" is explicitly set to false.
// Available languages can be found in
// ./src/react/features/transcribing/transcriber-langs.json.
// preferredTranscribeLanguage: 'en-US',
// DEPRECATED. Use transcription.autoCaptionOnRecord instead.
// Enables automatic turning on captions when recording is started
// autoCaptionOnRecord: false,
// Transcription options.
// transcription: {
// // Whether the feature should be enabled or not.
// enabled: false,
// // If true transcriber will use the application language.
// // The application language is either explicitly set by participants in their settings or automatically
// // detected based on the environment, e.g. if the app is opened in a chrome instance which
// // is using french as its default language then transcriptions for that participant will be in french.
// // Defaults to true.
// useAppLanguage: true,
// // Transcriber language. This settings will only work if "useAppLanguage"
// // is explicitly set to false.
// // Available languages can be found in
// // ./src/react/features/transcribing/transcriber-langs.json.
// preferredLanguage: 'en-US',
// // Disable start transcription for all participants.
// disableStartForAll: false,
// // Enables automatic turning on captions when recording is started
// autoCaptionOnRecord: false
// },
// Misc
// Default value for the channel "last N" attribute. -1 for unlimited.

View File

@@ -19,10 +19,6 @@
font-size: 14px;
margin-left: 16px;
}
&.space-top {
margin-top: 10px;
}
}
.recording-header-line {

View File

@@ -144,7 +144,7 @@ var interfaceConfig = {
RECENT_LIST_ENABLED: true,
REMOTE_THUMBNAIL_RATIO: 1, // 1:1
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds', 'more' ],
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds' ],
/**
* Specify which sharing features should be displayed. If the value is not set

View File

@@ -9,9 +9,9 @@ install! 'cocoapods', :deterministic_uuids => false
target 'JitsiMeet' do
project 'app/app.xcodeproj'
pod 'Firebase/Analytics', '~> 8.0'
pod 'Firebase/Crashlytics', '~> 8.0'
pod 'Firebase/DynamicLinks', '~> 8.0'
pod 'Firebase/Analytics', '~> 6.33.0'
pod 'Firebase/Crashlytics', '~> 6.33.0'
pod 'Firebase/DynamicLinks', '~> 6.33.0'
end
target 'JitsiMeetSDK' do

View File

@@ -21,60 +21,50 @@ PODS:
- React-Core (= 0.68.1)
- React-jsi (= 0.68.1)
- ReactCommon/turbomodule/core (= 0.68.1)
- Firebase/Analytics (8.15.0):
- Firebase/Analytics (6.33.0):
- Firebase/Core
- Firebase/Core (8.15.0):
- Firebase/Core (6.33.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 8.15.0)
- Firebase/CoreOnly (8.15.0):
- FirebaseCore (= 8.15.0)
- Firebase/Crashlytics (8.15.0):
- FirebaseAnalytics (= 6.8.3)
- Firebase/CoreOnly (6.33.0):
- FirebaseCore (= 6.10.3)
- Firebase/Crashlytics (6.33.0):
- Firebase/CoreOnly
- FirebaseCrashlytics (~> 8.15.0)
- Firebase/DynamicLinks (8.15.0):
- FirebaseCrashlytics (~> 4.6.1)
- Firebase/DynamicLinks (6.33.0):
- Firebase/CoreOnly
- FirebaseDynamicLinks (~> 8.15.0)
- FirebaseAnalytics (8.15.0):
- FirebaseAnalytics/AdIdSupport (= 8.15.0)
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
- GoogleUtilities/MethodSwizzler (~> 7.7)
- GoogleUtilities/Network (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- nanopb (~> 2.30908.0)
- FirebaseAnalytics/AdIdSupport (8.15.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleAppMeasurement (= 8.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
- GoogleUtilities/MethodSwizzler (~> 7.7)
- GoogleUtilities/Network (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- nanopb (~> 2.30908.0)
- FirebaseCore (8.15.0):
- FirebaseCoreDiagnostics (~> 8.0)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/Logger (~> 7.7)
- FirebaseCoreDiagnostics (8.15.0):
- GoogleDataTransport (~> 9.1)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/Logger (~> 7.7)
- nanopb (~> 2.30908.0)
- FirebaseCrashlytics (8.15.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleDataTransport (~> 9.1)
- GoogleUtilities/Environment (~> 7.7)
- nanopb (~> 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- FirebaseDynamicLinks (8.15.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (8.15.0):
- FirebaseCore (~> 8.0)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/UserDefaults (~> 7.7)
- PromisesObjC (< 3.0, >= 1.2)
- FirebaseDynamicLinks (~> 4.3.1)
- FirebaseAnalytics (6.8.3):
- FirebaseCore (~> 6.10)
- FirebaseInstallations (~> 1.6)
- GoogleAppMeasurement (= 6.8.3)
- GoogleUtilities/AppDelegateSwizzler (~> 6.7)
- GoogleUtilities/MethodSwizzler (~> 6.7)
- GoogleUtilities/Network (~> 6.7)
- "GoogleUtilities/NSData+zlib (~> 6.7)"
- nanopb (~> 1.30906.0)
- FirebaseCore (6.10.3):
- FirebaseCoreDiagnostics (~> 1.6)
- GoogleUtilities/Environment (~> 6.7)
- GoogleUtilities/Logger (~> 6.7)
- FirebaseCoreDiagnostics (1.7.0):
- GoogleDataTransport (~> 7.4)
- GoogleUtilities/Environment (~> 6.7)
- GoogleUtilities/Logger (~> 6.7)
- nanopb (~> 1.30906.0)
- FirebaseCrashlytics (4.6.2):
- FirebaseCore (~> 6.10)
- FirebaseInstallations (~> 1.6)
- GoogleDataTransport (~> 7.2)
- nanopb (~> 1.30906.0)
- PromisesObjC (~> 1.2)
- FirebaseDynamicLinks (4.3.1):
- FirebaseCore (~> 6.10)
- FirebaseInstallations (1.7.0):
- FirebaseCore (~> 6.10)
- GoogleUtilities/Environment (~> 6.7)
- GoogleUtilities/UserDefaults (~> 6.7)
- PromisesObjC (~> 1.2)
- fmt (6.2.1)
- Giphy (2.1.20):
- libwebp
@@ -82,52 +72,36 @@ PODS:
- Giphy (= 2.1.20)
- React-Core
- glog (0.3.5)
- GoogleAppMeasurement (8.15.0):
- GoogleAppMeasurement/AdIdSupport (= 8.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
- GoogleUtilities/MethodSwizzler (~> 7.7)
- GoogleUtilities/Network (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- nanopb (~> 2.30908.0)
- GoogleAppMeasurement/AdIdSupport (8.15.0):
- GoogleAppMeasurement/WithoutAdIdSupport (= 8.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
- GoogleUtilities/MethodSwizzler (~> 7.7)
- GoogleUtilities/Network (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- nanopb (~> 2.30908.0)
- GoogleAppMeasurement/WithoutAdIdSupport (8.15.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
- GoogleUtilities/MethodSwizzler (~> 7.7)
- GoogleUtilities/Network (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- nanopb (~> 2.30908.0)
- GoogleDataTransport (9.1.4):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30910.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleAppMeasurement (6.8.3):
- GoogleUtilities/AppDelegateSwizzler (~> 6.7)
- GoogleUtilities/MethodSwizzler (~> 6.7)
- GoogleUtilities/Network (~> 6.7)
- "GoogleUtilities/NSData+zlib (~> 6.7)"
- nanopb (~> 1.30906.0)
- GoogleDataTransport (7.5.1):
- nanopb (~> 1.30906.0)
- GoogleSignIn (6.0.2):
- AppAuth (~> 1.4)
- GTMAppAuth (~> 1.0)
- GTMSessionFetcher/Core (~> 1.1)
- GoogleUtilities/AppDelegateSwizzler (7.7.0):
- GoogleUtilities/AppDelegateSwizzler (6.7.2):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (7.7.0):
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.7.0):
- GoogleUtilities/Environment (6.7.2):
- PromisesObjC (~> 1.2)
- GoogleUtilities/Logger (6.7.2):
- GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (7.7.0):
- GoogleUtilities/MethodSwizzler (6.7.2):
- GoogleUtilities/Logger
- GoogleUtilities/Network (7.7.0):
- GoogleUtilities/Network (6.7.2):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.7.0)"
- GoogleUtilities/Reachability (7.7.0):
- "GoogleUtilities/NSData+zlib (6.7.2)"
- GoogleUtilities/Reachability (6.7.2):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (7.7.0):
- GoogleUtilities/UserDefaults (6.7.2):
- GoogleUtilities/Logger
- GTMAppAuth (1.2.2):
- AppAuth/Core (~> 1.4)
@@ -142,13 +116,13 @@ PODS:
- libwebp/mux (1.2.1):
- libwebp/demux
- libwebp/webp (1.2.1)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- nanopb (1.30906.0):
- nanopb/decode (= 1.30906.0)
- nanopb/encode (= 1.30906.0)
- nanopb/decode (1.30906.0)
- nanopb/encode (1.30906.0)
- ObjectiveDropboxOfficial (6.2.3)
- PromisesObjC (2.1.1)
- PromisesObjC (1.2.12)
- RCT-Folly (2021.06.28.00-v2):
- boost
- DoubleConversion
@@ -467,7 +441,7 @@ PODS:
- React-Core
- RNReanimated (1.13.4):
- React-Core
- RNScreens (3.13.1):
- RNScreens (3.10.1):
- React-Core
- React-RCTImage
- RNSound (0.11.1):
@@ -488,9 +462,9 @@ DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- Firebase/Analytics (~> 8.0)
- Firebase/Crashlytics (~> 8.0)
- Firebase/DynamicLinks (~> 8.0)
- Firebase/Analytics (~> 6.33.0)
- Firebase/Crashlytics (~> 6.33.0)
- Firebase/DynamicLinks (~> 6.33.0)
- "giphy-react-native-sdk (from `../node_modules/@giphy/react-native-sdk`)"
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- ObjectiveDropboxOfficial (= 6.2.3)
@@ -700,27 +674,27 @@ SPEC CHECKSUMS:
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: 2c76493a346ef8cacf1f442926a39f805fffec1f
FBReactNativeSpec: 371350f24afa87b6aba606972ec959dcd4a95c9a
Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
FirebaseAnalytics: 7761cbadb00a717d8d0939363eb46041526474fa
FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1
FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb
FirebaseCrashlytics: feb07e4e9187be3c23c6a846cce4824e5ce2dd0b
FirebaseDynamicLinks: 1dc816ef789c5adac6fede0b46d11478175c70e4
FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd
Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
FirebaseAnalytics: 5dd088bd2e67bb9d13dbf792d1164ceaf3052193
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb
FirebaseDynamicLinks: 6eac37d86910382eafb6315d952cc44c9e176094
FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
Giphy: b6d5087521d251bb8c99cdc0eb07bbdf86d142d5
giphy-react-native-sdk: 7abccf2b52123a0f30ce99da895ab6288023680c
glog: 476ee3e89abb49e07f822b48323c51c57124b572
GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
GoogleAppMeasurement: 966e88df9d19c15715137bb2ddaf52373f111436
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
GoogleSignIn: fd381840dbe7c1137aa6dc30849a5c3e070c034a
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
ObjectiveDropboxOfficial: fe206ce8c0bc49976c249d472db7fdbc53ebbd53
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
RCT-Folly: 4d8508a426467c48885f1151029bc15fa5d7b3b8
RCTRequired: 00581111c53531e39e3c6346ef0d2c0cf52a5a37
RCTTypeSafety: 07e03ee7800e7dd65cba8e52ad0c2edb06c96604
@@ -767,12 +741,12 @@ SPEC CHECKSUMS:
RNGestureHandler: e5c7cab5f214503dcefd6b2b0cefb050e1f51c4a
RNGoogleSignin: c4381751eefd73c552b923ba347a9bfc6f18771c
RNReanimated: c1b56d030d1616239861534d9adb531f8cffab68
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
RNScreens: 522705f2e5c9d27efb17f24aceb2bf8335bc7b8e
RNSound: 27e8268bdb0a1f191f219a33267f7e0445e8d62f
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
RNWatch: 99637948ec9b5c9ec5a41920642594ad5ba07e80
Yoga: 17cd9a50243093b547c1e539c749928dd68152da
PODFILE CHECKSUM: 0e8826a5cb9ee147354a83321ecb3104132f510b
PODFILE CHECKSUM: bef1335067eaa4e8c558b1248f8ab3948de855bc
COCOAPODS: 1.11.3

View File

@@ -895,19 +895,12 @@
"linkGenerated": "We have generated a link to your recording.",
"live": "LIVE",
"localRecordingNoNotificationWarning": "The recording will not be announced to other participants. You will need to let them know that the meeting is recorded.",
"localRecordingNoVideo": "Video is not being recorded",
"localRecordingStartWarning": "Please make sure you stop the recording before exiting the meeting in order to save it.",
"localRecordingStartWarningTitle": "Stop the recording to save it",
"localRecordingVideoStop": "Stopping your video will also stop the local recording. Are you sure you want to continue?",
"localRecordingVideoWarning": "To record your video you must have it on when starting the recording",
"localRecordingWarning": "Make sure you select the current tab in order to use the right video and audio. The recording is currently limited to 1GB, which is around 100 minutes.",
"loggedIn": "Logged in as {{userName}}",
"noStreams": "No audio or video stream detected.",
"off": "Recording stopped",
"offBy": "{{name}} stopped the recording",
"on": "Recording started",
"onBy": "{{name}} started the recording",
"onlyRecordSelf": "Record only my audio and video streams",
"pending": "Preparing to record the meeting...",
"rec": "REC",
"saveLocalRecording": "Save recording file locally",
@@ -957,7 +950,6 @@
"name": "Name",
"noDevice": "None",
"participantJoined": "Participant Joined",
"participantKnocking": "Participant entered lobby",
"participantLeft": "Participant Left",
"playSounds": "Play sound on",
"reactions": "Meeting reactions",

35
package-lock.json generated
View File

@@ -74,7 +74,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/v1461.0.0+96664436/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.2",
"moment-duration-format": "2.2.2",
@@ -104,7 +104,7 @@
"react-native-performance": "2.1.0",
"react-native-reanimated": "https://git@github.com/software-mansion/react-native-reanimated#c4a6b6f687ede090f6081064abe83a2ef9a05784",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "3.13.1",
"react-native-screens": "3.10.1",
"react-native-sound": "0.11.1",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "12.1.0",
@@ -125,7 +125,6 @@
"redux-thunk": "2.2.0",
"resemblejs": "4.0.0",
"rnnoise-wasm": "https://git@github.com/jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",
"util": "0.12.1",
"uuid": "8.3.2",
@@ -12169,8 +12168,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1461.0.0+96664436/lib-jitsi-meet.tgz",
"integrity": "sha512-DbtYpqJ9qsZtugeQIGDEmQOactpGQLSjCHd2obVU+1gdYxp2N6STVTxx7pw7zzlvW7S2pwkIXK/b5B6NFrt2iA==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
"integrity": "sha512-K+dJWt6nlAXtKE/WhR8pkf3vga+52tJSpTWX/fxOTKn8IJKTlj46gSC2CosAfwyG4P6ISzeFnTvVC3E+qbxbUg==",
"license": "Apache-2.0",
"dependencies": {
"@jitsi/js-utils": "2.0.0",
@@ -15535,9 +15534,9 @@
}
},
"node_modules/react-native-screens": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.13.1.tgz",
"integrity": "sha512-xcrnuUs0qUrGpc2gOTDY4VgHHADQwp80mwR1prU/Q0JqbZN5W3koLhuOsT6FkSRKjR5t40l+4LcjhHdpqRB2HA==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.10.1.tgz",
"integrity": "sha512-ZF/XHnRsuinvDY1XiCWLXxoUoSf+NgsAes2SZfX9rFQQcv128zmh/+19SSavGrSf6rQNzqytEMdRGI6yr4Gbjw==",
"dependencies": {
"react-freeze": "^1.0.0",
"warn-once": "^0.1.0"
@@ -16833,11 +16832,6 @@
"sdp-verify": "checker.js"
}
},
"node_modules/seamless-scroll-polyfill": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/seamless-scroll-polyfill/-/seamless-scroll-polyfill-2.1.8.tgz",
"integrity": "sha512-cF92Op90//vEpHphRx25rttJGXIgxcTB1WR5y0ODQhN7O4d0lSEOp5+l3sQDx0aAZ2MfXCqFEb/rG/3ghvVDIQ=="
},
"node_modules/seedrandom": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
@@ -29375,8 +29369,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1461.0.0+96664436/lib-jitsi-meet.tgz",
"integrity": "sha512-DbtYpqJ9qsZtugeQIGDEmQOactpGQLSjCHd2obVU+1gdYxp2N6STVTxx7pw7zzlvW7S2pwkIXK/b5B6NFrt2iA==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
"integrity": "sha512-K+dJWt6nlAXtKE/WhR8pkf3vga+52tJSpTWX/fxOTKn8IJKTlj46gSC2CosAfwyG4P6ISzeFnTvVC3E+qbxbUg==",
"requires": {
"@jitsi/js-utils": "2.0.0",
"@jitsi/logger": "2.0.0",
@@ -32024,9 +32018,9 @@
"integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q=="
},
"react-native-screens": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.13.1.tgz",
"integrity": "sha512-xcrnuUs0qUrGpc2gOTDY4VgHHADQwp80mwR1prU/Q0JqbZN5W3koLhuOsT6FkSRKjR5t40l+4LcjhHdpqRB2HA==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.10.1.tgz",
"integrity": "sha512-ZF/XHnRsuinvDY1XiCWLXxoUoSf+NgsAes2SZfX9rFQQcv128zmh/+19SSavGrSf6rQNzqytEMdRGI6yr4Gbjw==",
"requires": {
"react-freeze": "^1.0.0",
"warn-once": "^0.1.0"
@@ -32888,11 +32882,6 @@
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.3.0.tgz",
"integrity": "sha1-V6lXWUIEHYV3qGnXx01MOgvYiPY="
},
"seamless-scroll-polyfill": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/seamless-scroll-polyfill/-/seamless-scroll-polyfill-2.1.8.tgz",
"integrity": "sha512-cF92Op90//vEpHphRx25rttJGXIgxcTB1WR5y0ODQhN7O4d0lSEOp5+l3sQDx0aAZ2MfXCqFEb/rG/3ghvVDIQ=="
},
"seedrandom": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",

View File

@@ -79,7 +79,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/v1461.0.0+96664436/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.2",
"moment-duration-format": "2.2.2",
@@ -109,7 +109,7 @@
"react-native-performance": "2.1.0",
"react-native-reanimated": "https://git@github.com/software-mansion/react-native-reanimated#c4a6b6f687ede090f6081064abe83a2ef9a05784",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "3.13.1",
"react-native-screens": "3.10.1",
"react-native-sound": "0.11.1",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "12.1.0",
@@ -130,7 +130,6 @@
"redux-thunk": "2.2.0",
"resemblejs": "4.0.0",
"rnnoise-wasm": "https://git@github.com/jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",
"util": "0.12.1",
"uuid": "8.3.2",

View File

@@ -3,7 +3,6 @@
import '../authentication/middleware';
import '../base/i18n/middleware';
import '../base/devices/middleware';
import '../base/media/middleware';
import '../dynamic-branding/middleware';
import '../e2ee/middleware';
import '../external-api/middleware';

View File

@@ -222,7 +222,6 @@ export default [
'toolbarConfig',
'tileView',
'transcribingEnabled',
'transcription',
'useHostPageLocalStorage',
'useTurnUdp',
'videoQuality',

View File

@@ -207,7 +207,11 @@ function _getConferenceInfo(config) {
/**
* Constructs a new config {@code Object}, if necessary, out of a specific
* interface_config {@code Object} which is in the latest format supported by jitsi-meet.
* config {@code Object} which is in the latest format supported by jitsi-meet.
* Such a translation from an old config format to a new/the latest config
* format is necessary because the mobile app bundles jitsi-meet and
* lib-jitsi-meet at build time and does not download them at runtime from the
* deployment on which it will join a conference.
*
* @param {Object} oldValue - The config {@code Object} which may or may not be
* in the latest form supported by jitsi-meet and from which a new config
@@ -215,11 +219,11 @@ function _getConferenceInfo(config) {
* @returns {Object} A config {@code Object} which is in the latest format
* supported by jitsi-meet.
*/
function _translateInterfaceConfig(oldValue: Object) {
function _translateLegacyConfig(oldValue: Object) {
const newValue = oldValue;
if (!Array.isArray(oldValue.toolbarButtons)
&& typeof interfaceConfig === 'object' && Array.isArray(interfaceConfig.TOOLBAR_BUTTONS)) {
&& typeof interfaceConfig === 'object' && Array.isArray(interfaceConfig.TOOLBAR_BUTTONS)) {
newValue.toolbarButtons = interfaceConfig.TOOLBAR_BUTTONS;
}
@@ -245,58 +249,6 @@ function _translateInterfaceConfig(oldValue: Object) {
newValue.toolbarConfig.timeout = interfaceConfig.TOOLBAR_TIMEOUT;
}
if (!oldValue.connectionIndicators
&& typeof interfaceConfig === 'object'
&& (interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_DISABLED')
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_ENABLED')
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT'))) {
newValue.connectionIndicators = {
disabled: interfaceConfig.CONNECTION_INDICATOR_DISABLED,
autoHide: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
autoHideTimeout: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT
};
}
if (oldValue.disableModeratorIndicator === undefined
&& typeof interfaceConfig === 'object'
&& interfaceConfig.hasOwnProperty('DISABLE_FOCUS_INDICATOR')) {
newValue.disableModeratorIndicator = interfaceConfig.DISABLE_FOCUS_INDICATOR;
}
if (oldValue.defaultLocalDisplayName === undefined
&& typeof interfaceConfig === 'object'
&& interfaceConfig.hasOwnProperty('DEFAULT_LOCAL_DISPLAY_NAME')) {
newValue.defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
}
if (oldValue.defaultRemoteDisplayName === undefined
&& typeof interfaceConfig === 'object'
&& interfaceConfig.hasOwnProperty('DEFAULT_REMOTE_DISPLAY_NAME')) {
newValue.defaultRemoteDisplayName = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
}
return newValue;
}
/**
* Constructs a new config {@code Object}, if necessary, out of a specific
* config {@code Object} which is in the latest format supported by jitsi-meet.
* Such a translation from an old config format to a new/the latest config
* format is necessary because the mobile app bundles jitsi-meet and
* lib-jitsi-meet at build time and does not download them at runtime from the
* deployment on which it will join a conference.
*
* @param {Object} oldValue - The config {@code Object} which may or may not be
* in the latest form supported by jitsi-meet and from which a new config
* {@code Object} is to be constructed if necessary.
* @returns {Object} A config {@code Object} which is in the latest format
* supported by jitsi-meet.
*/
function _translateLegacyConfig(oldValue: Object) {
const newValue = _translateInterfaceConfig(oldValue);
// Translate deprecated config values to new config values.
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key]);
if (filteredConferenceInfo.length) {
@@ -318,6 +270,18 @@ function _translateLegacyConfig(oldValue: Object) {
});
}
if (!oldValue.connectionIndicators
&& typeof interfaceConfig === 'object'
&& (interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_DISABLED')
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_ENABLED')
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT'))) {
newValue.connectionIndicators = {
disabled: interfaceConfig.CONNECTION_INDICATOR_DISABLED,
autoHide: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
autoHideTimeout: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT
};
}
newValue.prejoinConfig = oldValue.prejoinConfig || {};
if (oldValue.hasOwnProperty('prejoinPageEnabled')
&& !newValue.prejoinConfig.hasOwnProperty('enabled')
@@ -351,15 +315,33 @@ function _translateLegacyConfig(oldValue: Object) {
};
}
if (oldValue.disableModeratorIndicator === undefined
&& typeof interfaceConfig === 'object'
&& interfaceConfig.hasOwnProperty('DISABLE_FOCUS_INDICATOR')) {
newValue.disableModeratorIndicator = interfaceConfig.DISABLE_FOCUS_INDICATOR;
}
newValue.e2ee = newValue.e2ee || {};
if (oldValue.e2eeLabels) {
newValue.e2ee.e2eeLabels = oldValue.e2eeLabels;
}
if (oldValue.defaultLocalDisplayName === undefined
&& typeof interfaceConfig === 'object'
&& interfaceConfig.hasOwnProperty('DEFAULT_LOCAL_DISPLAY_NAME')) {
newValue.defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
}
newValue.defaultLocalDisplayName
= newValue.defaultLocalDisplayName || 'me';
if (oldValue.defaultRemoteDisplayName === undefined
&& typeof interfaceConfig === 'object'
&& interfaceConfig.hasOwnProperty('DEFAULT_REMOTE_DISPLAY_NAME')) {
newValue.defaultRemoteDisplayName = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
}
if (oldValue.hideAddRoomButton) {
newValue.breakoutRooms = {
/* eslint-disable-next-line no-extra-parens */
@@ -371,46 +353,6 @@ function _translateLegacyConfig(oldValue: Object) {
newValue.defaultRemoteDisplayName
= newValue.defaultRemoteDisplayName || 'Fellow Jitster';
newValue.transcription = newValue.transcription || {};
if (oldValue.transcribingEnabled !== undefined) {
newValue.transcription = {
...newValue.transcription,
enabled: oldValue.transcribingEnabled
};
}
if (oldValue.transcribeWithAppLanguage !== undefined) {
newValue.transcription = {
...newValue.transcription,
useAppLanguage: oldValue.transcribeWithAppLanguage
};
}
if (oldValue.preferredTranscribeLanguage !== undefined) {
newValue.transcription = {
...newValue.transcription,
preferredLanguage: oldValue.preferredTranscribeLanguage
};
}
if (oldValue.autoCaptionOnRecord !== undefined) {
newValue.transcription = {
...newValue.transcription,
autoCaptionOnRecord: oldValue.autoCaptionOnRecord
};
}
newValue.recordingService = newValue.recordingService || {};
if (oldValue.fileRecordingsServiceEnabled !== undefined) {
newValue.recordingService = {
...newValue.recordingService,
enabled: oldValue.fileRecordingsServiceEnabled
};
}
if (oldValue.fileRecordingsServiceSharingEnabled !== undefined) {
newValue.recordingService = {
...newValue.recordingService,
sharingEnabled: oldValue.fileRecordingsServiceSharingEnabled
};
}
return newValue;
}

View File

@@ -1 +0,0 @@
import './middleware.any.js';

View File

@@ -1,40 +0,0 @@
import './middleware.any.js';
// @ts-ignore
import { MiddlewareRegistry } from '../redux';
import { IStore } from '../../app/types';
import { SET_VIDEO_MUTED } from './actionTypes';
import LocalRecordingManager from '../../recording/components/Recording/LocalRecordingManager.web';
// @ts-ignore
import { openDialog } from '../dialog';
// @ts-ignore
import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../../notifications';
// @ts-ignore
import StopRecordingDialog from '../../recording/components/Recording/web/StopRecordingDialog';
/**
* Implements the entry point of the middleware of the feature base/media.
*
* @param {IStore} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: any) => {
const { dispatch } = store;
switch(action.type) {
case SET_VIDEO_MUTED: {
if (LocalRecordingManager.isRecordingLocally() && LocalRecordingManager.selfRecording.on) {
if (action.muted && LocalRecordingManager.selfRecording.withVideo) {
dispatch(openDialog(StopRecordingDialog, { localRecordingVideoStop: true }));
return;
} else if (!action.muted && !LocalRecordingManager.selfRecording.withVideo) {
dispatch(showNotification({
titleKey: 'recording.localRecordingNoVideo',
descriptionKey: 'recording.localRecordingVideoWarning',
uid: 'recording.localRecordingNoVideo'
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
}
}
}
}
return next(action);
});

View File

@@ -236,8 +236,7 @@ export const OVERWRITE_PARTICIPANTS_NAMES = 'OVERWRITE_PARTICIPANTS_NAMES';
* Updates participants local recording status.
* {
* type: SET_LOCAL_PARTICIPANT_RECORDING_STATUS,
* recording: boolean,
* onlySelf: boolean
* recording: boolean
* }
*/
export const SET_LOCAL_PARTICIPANT_RECORDING_STATUS = 'SET_LOCAL_PARTICIPANT_RECORDING_STATUS';

View File

@@ -689,16 +689,14 @@ export function overwriteParticipantsNames(participantList) {
* Local video recording status for the local participant.
*
* @param {boolean} recording - If local recording is ongoing.
* @param {boolean} onlySelf - If recording only local streams.
* @returns {{
* type: SET_LOCAL_PARTICIPANT_RECORDING_STATUS,
* recording: boolean
* }}
*/
export function updateLocalRecordingStatus(recording, onlySelf) {
export function updateLocalRecordingStatus(recording) {
return {
type: SET_LOCAL_PARTICIPANT_RECORDING_STATUS,
recording,
onlySelf
recording
};
}

View File

@@ -179,11 +179,11 @@ MiddlewareRegistry.register(store => next => action => {
case SET_LOCAL_PARTICIPANT_RECORDING_STATUS: {
const state = store.getState();
const { recording, onlySelf } = action;
const { recording } = action;
const localId = getLocalParticipant(state)?.id;
const { localRecording } = state['features/base/config'];
if (localRecording?.notifyAllParticipants && !onlySelf) {
if (localRecording.notifyAllParticipants) {
store.dispatch(participantUpdated({
// XXX Only the local participant is allowed to update without
// stating the JitsiConference instance (i.e. participant property

View File

@@ -3,4 +3,6 @@
/**
* The default server URL to open if no other was specified.
*/
export const DEFAULT_SERVER_URL = 'https://meet.jit.si';
//export const DEFAULT_SERVER_URL = 'https://abora6.jitsi.net#config.replaceParticipant=true';
export const DEFAULT_SERVER_URL = 'https://alpha.jitsi.net';
//export const DEFAULT_SERVER_URL = 'https://meet.jit.si';

View File

@@ -30,7 +30,6 @@ const DEFAULT_STATE = {
hideShareAudioHelper: false,
soundsIncomingMessage: true,
soundsParticipantJoined: true,
soundsParticipantKnocking: true,
soundsParticipantLeft: true,
soundsTalkWhileMuted: true,
soundsReactions: true,

View File

@@ -243,8 +243,8 @@ export const colorMap = {
// Line separators
border03: 'surface04',
border04: 'primary12',
border05: 'surface07',
// Color for error border & message
borderError: 'error06',

View File

@@ -9,6 +9,7 @@ import {
getClientHeight,
getClientWidth
} from '../../../base/modal/components/functions.native';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { screen } from '../../../mobile/navigation/routes';
import { chatTabBarOptions } from '../../../mobile/navigation/screenOptions';
import { PollsPane } from '../../../polls/components';
@@ -26,7 +27,12 @@ const ChatAndPolls = () => {
height: clientHeight,
width: clientWidth
}}
screenOptions = { chatTabBarOptions }>
screenOptions = {{
...chatTabBarOptions,
tabBarStyle: {
backgroundColor: BaseTheme.palette.ui01
}
}}>
<ChatTab.Screen
component = { Chat }
name = { screen.conference.chatandpolls.tab.chat } />

View File

@@ -41,8 +41,7 @@ export default {
alignSelf: 'center',
flex: 1,
padding: BoxModel.padding,
paddingTop: '8%',
maxWidth: '80%'
paddingTop: '8%'
},
/**

View File

@@ -1,7 +1,6 @@
// @flow
import React from 'react';
import { scrollIntoView } from 'seamless-scroll-polyfill';
import { MESSAGE_TYPE_REMOTE } from '../../constants';
import AbstractMessageContainer, { type Props }
@@ -104,7 +103,7 @@ export default class MessageContainer extends AbstractMessageContainer<Props> {
* @returns {void}
*/
scrollToBottom(withAnimation: boolean) {
scrollIntoView(this._messagesListEndRef.current, {
this._messagesListEndRef.current.scrollIntoView({
behavior: withAnimation ? 'smooth' : 'auto',
block: 'nearest'
});

View File

@@ -189,7 +189,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
}
case TOGGLE_E2EE: {
if (conference && conference.isE2EEEnabled() !== action.enabled) {
if (conference) {
logger.debug(`E2EE will be ${action.enabled ? 'enabled' : 'disabled'}`);
conference.toggleE2EE(action.enabled);

View File

@@ -23,7 +23,7 @@ StateListenerRegistry.register(
const localParticipant = getLocalParticipant(store.getState());
const { defaultLocalDisplayName } = store.getState()['features/base/config'];
// Initial setting of the display name happens on app
// Initial setting of the display name occurs happens on app
// initialization, before the local participant is ready. The initial
// settings is not desired to be fired anyways, only changes.
if (localParticipant) {
@@ -39,23 +39,6 @@ StateListenerRegistry.register(
}
});
StateListenerRegistry.register(
/* selector */ state => state['features/base/settings'].email,
/* listener */ (email, store) => {
const localParticipant = getLocalParticipant(store.getState());
// Initial setting of the email happens on app
// initialization, before the local participant is ready. The initial
// settings is not desired to be fired anyways, only changes.
if (localParticipant) {
const { id } = localParticipant;
APP.API.notifyEmailChanged(id, {
email
});
}
});
/**
* Updates the on stage participant value.
*/

View File

@@ -112,7 +112,7 @@ export function getColumnCount(stateful: Object | Function) {
return 2;
}
return Math.min(participantCount <= 6 ? 3 : 4, participantCount);
return Math.min(3, participantCount);
}
/**

View File

@@ -1,7 +1,7 @@
import { GiphyContent, GiphyGridView, GiphyMediaType } from '@giphy/react-native-sdk';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Image, Text, View } from 'react-native';
import { Image, Keyboard, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useDispatch } from 'react-redux';
import { createGifSentEvent, sendAnalytics } from '../../../analytics';
@@ -16,7 +16,7 @@ import styles from './styles';
const GifsMenu = () => {
const [ searchQuery, setSearchQuery ] = useState('');
const dispatch = useDispatch();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const content = searchQuery === ''
? GiphyContent.trending({ mediaType: GiphyMediaType.Gif })
@@ -34,32 +34,33 @@ const GifsMenu = () => {
goBack();
}, []);
const onScroll = useCallback(Keyboard.dismiss, []);
const footerComponent = () => (
<View style = { styles.credit }>
return (<JitsiScreen
style = { styles.container }>
<ClearableInput
autoFocus = { true }
customStyles = { styles.clearableInput }
onChange = { setSearchQuery }
placeholder = 'Search GIPHY'
value = { searchQuery } />
<GiphyGridView
cellPadding = { 5 }
content = { content }
onMediaSelect = { sendGif }
onScroll = { onScroll }
style = { styles.grid } />
<View
style = { [ styles.credit, {
bottom: insets.bottom,
left: insets.left,
right: insets.right
} ] }>
<Text
style = { styles.creditText }>Powered by</Text>
<Image source = { require('../../../../../images/GIPHY_logo.png') } />
</View>
);
return (
<JitsiScreen
/* eslint-disable-next-line react/jsx-no-bind */
footerComponent = { footerComponent }
style = { styles.container }>
<ClearableInput
customStyles = { styles.clearableInput }
onChange = { setSearchQuery }
placeholder = { t('giphy.search') }
value = { searchQuery } />
<GiphyGridView
cellPadding = { 5 }
content = { content }
onMediaSelect = { sendGif }
style = { styles.grid } />
</JitsiScreen>
);
</JitsiScreen>);
};
export default GifsMenu;

View File

@@ -22,15 +22,15 @@ export default {
},
credit: {
alignItems: 'center',
backgroundColor: BaseTheme.palette.ui01,
width: '100%',
height: 40,
position: 'absolute',
marginBottom: 0,
display: 'flex',
flexDirection: 'row',
height: 56,
justifyContent: 'center',
marginBottom: BaseTheme.spacing[0],
paddingBottom: BaseTheme.spacing[4],
width: '100%'
alignItems: 'center',
justifyContent: 'center'
},
creditText: {

View File

@@ -96,8 +96,6 @@ StateListenerRegistry.register(
});
conference.on(JitsiConferenceEvents.LOBBY_USER_JOINED, (id, name) => {
const { soundsParticipantKnocking } = getState()['features/base/settings'];
batch(() => {
dispatch(
participantIsKnockingOrUpdated({
@@ -105,9 +103,7 @@ StateListenerRegistry.register(
name
})
);
if (soundsParticipantKnocking) {
dispatch(playSound(KNOCKING_PARTICIPANT_SOUND_ID));
}
dispatch(playSound(KNOCKING_PARTICIPANT_SOUND_ID));
const isParticipantsPaneVisible = getParticipantsPaneOpen(getState());

View File

@@ -152,19 +152,13 @@ export const conferenceScreenOptions = {
* Tab bar options for chat screen.
*/
export const chatTabBarOptions = {
tabBarActiveTintColor: BaseTheme.palette.field02,
tabBarActiveTintColor: BaseTheme.palette.screen01Header,
tabBarLabelStyle: {
fontSize: BaseTheme.typography.labelRegular.fontSize,
textTransform: 'capitalize'
fontSize: BaseTheme.typography.labelRegular.fontSize
},
tabBarInactiveTintColor: BaseTheme.palette.text03,
tabBarInactiveTintColor: BaseTheme.palette.text01,
tabBarIndicatorStyle: {
backgroundColor: BaseTheme.palette.field02
},
tabBarStyle: {
backgroundColor: BaseTheme.palette.ui01,
borderBottomColor: BaseTheme.palette.border05,
borderBottomWidth: 1
backgroundColor: BaseTheme.palette.screen01Header
}
};

View File

@@ -26,6 +26,7 @@ export function enterPictureInPicture() {
// fine to enter PiP mode.
if (getFeatureFlag(getState, PIP_ENABLED)) {
const { PictureInPicture } = NativeModules;
console.log("XXX enter PIP")
const p
= Platform.OS === 'android'
? PictureInPicture

View File

@@ -3,9 +3,7 @@
import React from 'react';
import { Switch, Text, View } from 'react-native';
import { Button } from 'react-native-paper';
import { useSelector } from 'react-redux';
import { getLocalParticipant } from '../../../base/participants';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { BUTTON_MODES } from '../../../chat/constants';
import { isSubmitAnswerDisabled } from '../../functions';
@@ -26,15 +24,12 @@ const PollAnswer = (props: AbstractProps) => {
t
} = props;
const { changingVote } = poll;
const localParticipant = useSelector(getLocalParticipant);
return (
<>
<Text style = { dialogStyles.questionText } >{ poll.question }</Text>
<Text style = { dialogStyles.questionOwnerText } >{
t('polls.by', { name: localParticipant.name })
}
</Text>
<View>
<View>
<Text style = { dialogStyles.question } >{ poll.question }</Text>
</View>
<View style = { chatStyles.answerContent }>
{poll.answers.map((answer, index) => (
<View
@@ -43,7 +38,6 @@ const PollAnswer = (props: AbstractProps) => {
<Switch
/* eslint-disable react/jsx-no-bind */
onValueChange = { state => setCheckbox(index, state) }
trackColor = {{ true: BaseTheme.palette.action01 }}
value = { checkBoxStates[index] } />
<Text style = { chatStyles.switchLabel }>{answer.name}</Text>
</View>
@@ -52,14 +46,13 @@ const PollAnswer = (props: AbstractProps) => {
<View style = { chatStyles.buttonRow }>
<Button
color = { BaseTheme.palette.action02 }
labelStyle = { chatStyles.pollButtonLabel }
mode = { BUTTON_MODES.CONTAINED }
onPress = { changingVote ? skipChangeVote : skipAnswer }
style = { chatStyles.pollCreateButton } >
{ t('polls.answer.skip') }
{t('polls.answer.skip')}
</Button>
<Button
color = { BaseTheme.palette.action01 }
color = { BaseTheme.palette.screen01Header }
disabled = { isSubmitAnswerDisabled(checkBoxStates) }
labelStyle = {
isSubmitAnswerDisabled(checkBoxStates)
@@ -69,10 +62,10 @@ const PollAnswer = (props: AbstractProps) => {
mode = { BUTTON_MODES.CONTAINED }
onPress = { submitAnswer }
style = { chatStyles.pollCreateButton } >
{ t('polls.answer.submit') }
{t('polls.answer.submit')}
</Button>
</View>
</>
</View>
);
};

View File

@@ -1,14 +1,13 @@
// @flow
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { View, Text, TextInput, FlatList } from 'react-native';
import { Button, Divider, TouchableRipple } from 'react-native-paper';
import { View, TextInput, FlatList, TouchableOpacity } from 'react-native';
import { Button } from 'react-native-paper';
import { Icon, IconClose } from '../../../base/icons';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { BUTTON_MODES } from '../../../chat/constants';
import styles
from '../../../welcome/components/native/settings/components/styles';
import { ANSWERS_LIMIT, CHAR_LIMIT } from '../../constants';
import { CHAR_LIMIT } from '../../constants';
import AbstractPollCreate from '../AbstractPollCreate';
import type { AbstractProps } from '../AbstractPollCreate';
@@ -35,7 +34,7 @@ const PollCreate = (props: AbstractProps) => {
/*
* This ref stores the Array of answer input fields, allowing us to focus on them.
* This array is maintained by registerFieldRef and the useEffect below.
* This array is maintained by registerfieldRef and the useEffect below.
*/
const answerInputs = useRef([]);
const registerFieldRef = useCallback((i, input) => {
@@ -87,14 +86,16 @@ const PollCreate = (props: AbstractProps) => {
}, [ answers, addAnswer, removeAnswer, requestFocus ]);
/* eslint-disable react/no-multi-comp */
const createRemoveOptionButton = onPress => (
<TouchableRipple
const createIconButton = (icon, onPress, style) => (
<TouchableOpacity
activeOpacity = { 0.8 }
onPress = { onPress }
rippleColor = { 'transparent' } >
<Text style = { dialogStyles.optionRemoveButtonText }>
{ t('polls.create.removeOption') }
</Text>
</TouchableRipple>
style = { [ dialogStyles.buttonContainer, style ] }>
<Icon
size = { 24 }
src = { icon }
style = { dialogStyles.icon } />
</TouchableOpacity>
);
@@ -105,9 +106,6 @@ const PollCreate = (props: AbstractProps) => {
(
<View
style = { dialogStyles.optionContainer }>
<Text style = { dialogStyles.optionFieldLabel }>
{ t('polls.create.pollOption', { index: index + 1 }) }
</Text>
<TextInput
blurOnSubmit = { false }
maxLength = { CHAR_LIMIT }
@@ -117,13 +115,13 @@ const PollCreate = (props: AbstractProps) => {
placeholder = { t('polls.create.answerPlaceholder', { index: index + 1 }) }
placeholderTextColor = { BaseTheme.palette.text03 }
ref = { input => registerFieldRef(index, input) }
selectionColor = { BaseTheme.palette.action01 }
selectionColor = { BaseTheme.palette.text03 }
style = { dialogStyles.field }
value = { answers[index] } />
{
answers.length > 2
&& createRemoveOptionButton(() => removeAnswer(index))
&& createIconButton(IconClose, () => removeAnswer(index))
}
</View>
);
@@ -131,9 +129,6 @@ const PollCreate = (props: AbstractProps) => {
return (
<View style = { chatStyles.pollCreateContainer }>
<View style = { chatStyles.pollCreateSubContainer }>
<Text style = { chatStyles.questionFieldLabel }>
{ t('polls.create.pollQuestion') }
</Text>
<TextInput
autoFocus = { true }
blurOnSubmit = { false }
@@ -143,10 +138,9 @@ const PollCreate = (props: AbstractProps) => {
onSubmitEditing = { onQuestionKeyDown }
placeholder = { t('polls.create.questionPlaceholder') }
placeholderTextColor = { BaseTheme.palette.text03 }
selectionColor = { BaseTheme.palette.action01 }
style = { dialogStyles.questionField }
selectionColor = { BaseTheme.palette.text03 }
style = { dialogStyles.question }
value = { question } />
<Divider style = { styles.fieldSeparator } />
<FlatList
blurOnSubmit = { true }
data = { answers }
@@ -157,8 +151,6 @@ const PollCreate = (props: AbstractProps) => {
<View style = { chatStyles.pollCreateButtonsContainer }>
<Button
color = { BaseTheme.palette.action02 }
disabled = { answers.length >= ANSWERS_LIMIT }
labelStyle = { chatStyles.pollButtonLabel }
mode = { BUTTON_MODES.CONTAINED }
onPress = { () => {
// adding and answer
@@ -166,20 +158,19 @@ const PollCreate = (props: AbstractProps) => {
requestFocus(answers.length);
} }
style = { chatStyles.pollCreateAddButton }>
{ t('polls.create.addOption') }
{t('polls.create.addOption')}
</Button>
<View
style = { chatStyles.buttonRow }>
<Button
color = { BaseTheme.palette.action02 }
labelStyle = { chatStyles.pollButtonLabel }
mode = { BUTTON_MODES.CONTAINED }
onPress = { () => setCreateMode(false) }
style = { chatStyles.pollCreateButton } >
{ t('polls.create.cancel') }
{t('polls.create.cancel')}
</Button>
<Button
color = { BaseTheme.palette.action01 }
color = { BaseTheme.palette.screen01Header }
disabled = { isSubmitDisabled }
labelStyle = {
isSubmitDisabled
@@ -189,7 +180,7 @@ const PollCreate = (props: AbstractProps) => {
mode = { BUTTON_MODES.CONTAINED }
onPress = { onSubmit }
style = { chatStyles.pollCreateButton } >
{ t('polls.create.send') }
{t('polls.create.send')}
</Button>
</View>
</View>

View File

@@ -2,9 +2,7 @@
import React, { useCallback } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useSelector } from 'react-redux';
import { getLocalParticipant } from '../../../base/participants';
import AbstractPollResults from '../AbstractPollResults';
import type { AbstractProps, AnswerInfo } from '../AbstractPollResults';
@@ -22,12 +20,13 @@ const PollResults = (props: AbstractProps) => {
answers,
changeVote,
haveVoted,
question,
showDetails,
question,
t,
toggleIsDetailed
} = props;
/* eslint-disable react/no-multi-comp */
/**
* Render a header summing up answer information.
*
@@ -42,6 +41,11 @@ const PollResults = (props: AbstractProps) => {
<View>
<Text style = { resultsStyles.answer }>({nbVotes}) {percentage}%</Text>
</View>
{/* <Text style = { resultsStyles.answer }>{ answer } - { percentage }%</Text>
<Text style = { resultsStyles.answerVoteCount }>
{ t('polls.answer.vote', { count: nbVotes }) }
</Text> */}
</View>
);
@@ -58,9 +62,6 @@ const PollResults = (props: AbstractProps) => {
return (
<View style = { resultsStyles.answerContainer }>
{ renderHeader(name, percentage, voterCount) }
<View style = { resultsStyles.barContainer }>
<View style = { [ resultsStyles.bar, { width: `${percentage}%` } ] } />
</View>
{ voters && voterCount > 0
&& <View style = { resultsStyles.voters }>
{voters.map(({ id, name: voterName }) =>
@@ -88,14 +89,13 @@ const PollResults = (props: AbstractProps) => {
);
}, [ showDetails ]);
const localParticipant = useSelector(getLocalParticipant);
/* eslint-disable react/jsx-no-bind */
return (
<View>
<Text style = { dialogStyles.questionText } >{ question }</Text>
<Text style = { dialogStyles.questionOwnerText } >{ t('polls.by', { name: localParticipant.name }) }</Text>
<View>
<Text style = { dialogStyles.question } >{ question }</Text>
</View>
<FlatList
data = { answers }
keyExtractor = { (item, index) => index.toString() }
@@ -104,24 +104,17 @@ const PollResults = (props: AbstractProps) => {
<TouchableOpacity onPress = { toggleIsDetailed }>
<Text
style = { chatStyles.toggleText }>
{
showDetails
? t('polls.results.hideDetailedResults')
: t('polls.results.showDetailedResults')
}
{showDetails ? t('polls.results.hideDetailedResults') : t('polls.results.showDetailedResults')}
</Text>
</TouchableOpacity>
<TouchableOpacity onPress = { changeVote }>
<Text
style = { chatStyles.toggleText }>
{
haveVoted
? t('polls.results.changeVote')
: t('polls.results.vote')
}
{haveVoted ? t('polls.results.changeVote') : t('polls.results.vote')}
</Text>
</TouchableOpacity>
</View>
</View>
);
};

View File

@@ -1,13 +1,9 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { FlatList, View } from 'react-native';
import { FlatList } from 'react-native';
import { Text } from 'react-native-paper';
import { useSelector } from 'react-redux';
import { Icon, IconChatUnread } from '../../../base/icons';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import PollItem from './PollItem';
import { chatStyles } from './styles';
@@ -37,17 +33,11 @@ const PollsList = () => {
<>
{
listPolls.length === 0
&& <View style = { chatStyles.noPollContent }>
<Icon
color = { BaseTheme.palette.icon03 }
size = { 160 }
src = { IconChatUnread } />
<Text style = { chatStyles.noPollText } >
{
t('polls.results.empty')
}
</Text>
</View>
&& <Text style = { chatStyles.noPollText } >
{
t('polls.results.empty')
}
</Text>
}
<FlatList
data = { listPolls }

View File

@@ -36,9 +36,9 @@ const PollsPane = (props: AbstractProps) => {
return (
<JitsiScreen
contentContainerStyle = { chatStyles.pollPane }
contentContainerStyle = { chatStyles.PollPane }
hasTabNavigator = { true }
style = { chatStyles.pollPaneContainer }>
style = { chatStyles.PollPaneContainer }>
{
createMode
? <PollCreate setCreateMode = { setCreateMode } />
@@ -47,8 +47,7 @@ const PollsPane = (props: AbstractProps) => {
}
{
!createMode && <Button
color = { palette.action01 }
labelStyle = { chatStyles.pollButtonLabel }
color = { palette.screen01Header }
mode = { BUTTON_MODES.CONTAINED }
onPress = { onCreate }
style = { chatStyles.createPollButton } >

View File

@@ -1,63 +1,60 @@
// @flow
import { createStyleSheet } from '../../../base/styles';
import { ColorPalette, createStyleSheet } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export const answerStyles = createStyleSheet({
question: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 6
},
answer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 3
},
option: {
flexShrink: 1
}
});
export const dialogStyles = createStyleSheet({
questionText: {
...BaseTheme.typography.bodyShortBold,
question: {
color: BaseTheme.palette.text01,
marginBottom: BaseTheme.spacing[2],
marginLeft: BaseTheme.spacing[2]
},
questionOwnerText: {
...BaseTheme.typography.bodyShortBold,
color: BaseTheme.palette.text03,
marginBottom: BaseTheme.spacing[2],
marginLeft: BaseTheme.spacing[2]
},
questionField: {
borderWidth: 1,
borderColor: BaseTheme.palette.border05,
borderRadius: BaseTheme.shape.borderRadius,
color: BaseTheme.palette.text01,
fontSize: 14,
marginHorizontal: BaseTheme.spacing[3],
marginBottom: BaseTheme.spacing[3],
paddingBottom: BaseTheme.spacing[2],
paddingLeft: BaseTheme.spacing[3],
paddingRight: BaseTheme.spacing[3],
paddingTop: BaseTheme.spacing[2]
fontSize: 16,
fontWeight: 'bold',
marginVertical: 4
},
optionContainer: {
flexDirection: 'column',
marginTop: BaseTheme.spacing[3],
marginHorizontal: BaseTheme.spacing[3]
},
optionFieldLabel: {
color: BaseTheme.palette.text03,
marginBottom: BaseTheme.spacing[2]
},
optionRemoveButtonText: {
color: BaseTheme.palette.actionDangerActive
flexDirection: 'row'
},
field: {
borderWidth: 1,
borderColor: BaseTheme.palette.border05,
borderRadius: BaseTheme.shape.borderRadius,
color: BaseTheme.palette.text01,
borderBottomWidth: 1,
borderColor: ColorPalette.blue,
fontSize: 14,
paddingBottom: BaseTheme.spacing[2],
paddingLeft: BaseTheme.spacing[3],
paddingRight: BaseTheme.spacing[3],
paddingTop: BaseTheme.spacing[2]
flexGrow: 1,
paddingBottom: 0,
flexShrink: 1
},
buttonContainer: {
justifyContent: 'flex-end',
alignItems: 'center'
},
icon: {
color: ColorPalette.white,
backgroundColor: ColorPalette.blue,
borderRadius: 5,
margin: 0
},
plusButton: {
marginTop: 8
}
});
@@ -76,18 +73,18 @@ export const resultsStyles = createStyleSheet({
},
bar: {
backgroundColor: BaseTheme.palette.action01,
borderRadius: BaseTheme.shape.borderRadius,
backgroundColor: ColorPalette.blue,
borderRadius: 3,
height: 6
},
voters: {
backgroundColor: BaseTheme.palette.ui04,
borderColor: BaseTheme.palette.border03,
borderRadius: BaseTheme.shape.borderRadius,
borderRadius: 3,
borderWidth: 1,
padding: BaseTheme.spacing[2],
marginTop: BaseTheme.spacing[2]
borderColor: 'gray',
padding: 2,
marginHorizontal: 8,
marginVertical: 4
},
voter: {
@@ -95,8 +92,7 @@ export const resultsStyles = createStyleSheet({
},
answerContainer: {
marginHorizontal: BaseTheme.spacing[2],
marginVertical: BaseTheme.spacing[3],
marginVertical: 2,
maxWidth: '100%'
},
@@ -120,65 +116,58 @@ export const resultsStyles = createStyleSheet({
});
export const chatStyles = createStyleSheet({
questionFieldLabel: {
color: BaseTheme.palette.text03,
marginBottom: BaseTheme.spacing[2],
marginLeft: BaseTheme.spacing[3]
messageFooter: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 11,
marginTop: 6
},
noPollContent: {
alignItems: 'center',
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
paddingTop: '4%'
showDetails: {
fontWeight: 'bold'
},
noPollText: {
flex: 1,
color: BaseTheme.palette.text03,
textAlign: 'center',
maxWidth: '70%'
paddingTop: '8%'
},
pollItemContainer: {
backgroundColor: BaseTheme.palette.ui02,
borderColor: BaseTheme.palette.border05,
borderRadius: BaseTheme.shape.borderRadius,
boxShadow: BaseTheme.shape.boxShadow,
borderWidth: 1,
padding: BaseTheme.spacing[2],
margin: BaseTheme.spacing[3]
borderRadius: 4,
borderColor: '#2183ad',
borderWidth: 2,
padding: 16,
marginBottom: 8
},
pollCreateContainer: {
flex: 1
flex: 1,
justifyContent: 'space-between'
},
pollCreateSubContainer: {
flex: 1,
marginTop: BaseTheme.spacing[3]
flex: 1
},
pollCreateButtonsContainer: {
marginHorizontal: BaseTheme.spacing[3],
marginVertical: '8%'
paddingVertical: '8%'
},
pollCreateButton: {
flex: 1,
padding: 4,
marginHorizontal: BaseTheme.spacing[2]
},
pollSendLabel: {
color: BaseTheme.palette.text01,
textTransform: 'capitalize'
color: BaseTheme.palette.text01
},
pollSendDisabledLabel: {
color: BaseTheme.palette.text03,
textTransform: 'capitalize'
color: BaseTheme.palette.text03
},
buttonRow: {
@@ -192,7 +181,7 @@ export const chatStyles = createStyleSheet({
switchRow: {
alignItems: 'center',
flexDirection: 'row',
padding: BaseTheme.spacing[2]
padding: 6
},
switchLabel: {
@@ -200,39 +189,39 @@ export const chatStyles = createStyleSheet({
marginLeft: BaseTheme.spacing[2]
},
pollButtonLabel: {
textTransform: 'capitalize'
},
pollCreateAddButton: {
margin: BaseTheme.spacing[2],
padding: BaseTheme.spacing[1]
margin: BaseTheme.spacing[2]
},
toggleText: {
color: BaseTheme.palette.action01,
color: ColorPalette.blue,
paddingTop: BaseTheme.spacing[3]
},
createPollButton: {
padding: 4,
marginHorizontal: BaseTheme.spacing[4],
marginVertical: '8%'
padding: 8,
marginHorizontal: BaseTheme.spacing[2],
marginVertical: BaseTheme.spacing[4]
},
pollPane: {
PollPane: {
flex: 1,
padding: 8
},
pollPaneContainer: {
PollPaneContainer: {
backgroundColor: BaseTheme.palette.ui01,
flex: 1
},
PollPaneContent: {
justifyContent: 'space-between',
padding: BaseTheme.spacing[3],
flex: 1
},
bottomLinks: {
flexDirection: 'row',
justifyContent: 'space-between',
marginHorizontal: BaseTheme.spacing[2]
justifyContent: 'space-between'
}
});

View File

@@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Icon, IconMenu } from '../../../base/icons';
import { Tooltip } from '../../../base/tooltip';
import { ANSWERS_LIMIT, CHAR_LIMIT } from '../../constants';
import { CHAR_LIMIT } from '../../constants';
import AbstractPollCreate from '../AbstractPollCreate';
import type { AbstractProps } from '../AbstractPollCreate';
@@ -237,7 +237,6 @@ const PollCreate = (props: AbstractProps) => {
<button
aria-label = { 'Add option' }
className = 'poll-button poll-button-secondary'
disabled = { answers.length >= ANSWERS_LIMIT }
onClick = { () => {
addAnswer();
requestFocus(answers.length);

View File

@@ -4,5 +4,4 @@ export const COMMAND_NEW_POLL = 'new-poll';
export const COMMAND_ANSWER_POLL = 'answer-poll';
export const COMMAND_OLD_POLLS = 'old-polls';
export const CHAR_LIMIT = 500;
export const ANSWERS_LIMIT = 255;
export const CHAR_LIMIT = 1000;

View File

@@ -160,11 +160,8 @@ export function isPrejoinPageVisible(state: Object): boolean {
* @returns {boolean}
*/
export function shouldAutoKnock(state: Object): boolean {
const { iAmRecorder, iAmSipGateway, autoKnockLobby, prejoinConfig } = state['features/base/config'];
const { userSelectedSkipPrejoin } = state['features/base/settings'];
const isPrejoinEnabled = prejoinConfig?.enabled;
const { iAmRecorder, iAmSipGateway, autoKnockLobby } = state['features/base/config'];
return ((isPrejoinEnabled && !userSelectedSkipPrejoin)
|| autoKnockLobby || (iAmRecorder && iAmSipGateway))
return (isPrejoinPageVisible(state) || autoKnockLobby || (iAmRecorder && iAmSipGateway))
&& !state['features/lobby'].knocking;
}

View File

@@ -71,8 +71,7 @@ export const SET_MEETING_HIGHLIGHT_BUTTON_STATE = 'SET_MEETING_HIGHLIGHT_BUTTON_
* Attempts to start the local recording.
*
* {
* type: START_LOCAL_RECORDING,
* onlySelf: boolean
* type: START_LOCAL_RECORDING
* }
*/
export const START_LOCAL_RECORDING = 'START_LOCAL_RECORDING';

View File

@@ -338,13 +338,11 @@ function _setPendingRecordingNotificationUid(uid: ?number, streamType: string) {
/**
* Starts local recording.
*
* @param {boolean} onlySelf - Whether to only record the local streams.
* @returns {Object}
*/
export function startLocalVideoRecording(onlySelf) {
export function startLocalVideoRecording() {
return {
type: START_LOCAL_RECORDING,
onlySelf
type: START_LOCAL_RECORDING
};
}

View File

@@ -139,7 +139,6 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
= this._onSelectedRecordingServiceChanged.bind(this);
this._onSharingSettingChanged = this._onSharingSettingChanged.bind(this);
this._toggleScreenshotCapture = this._toggleScreenshotCapture.bind(this);
this._onLocalRecordingSelfChange = this._onLocalRecordingSelfChange.bind(this);
let selectedRecordingService;
@@ -158,8 +157,7 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
userName: undefined,
sharingEnabled: true,
spaceLeft: undefined,
selectedRecordingService,
localRecordingOnlySelf: false
selectedRecordingService
};
}
@@ -213,19 +211,6 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
});
}
_onLocalRecordingSelfChange: () => void;
/**
* Callback to handle local recording only self setting change.
*
* @returns {void}
*/
_onLocalRecordingSelfChange() {
this.setState({
localRecordingOnlySelf: !this.state.localRecordingOnlySelf
});
}
_onSelectedRecordingServiceChanged: (string) => void;
/**
@@ -341,7 +326,7 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
break;
}
case RECORDING_TYPES.LOCAL: {
dispatch(startLocalVideoRecording(this.state.localRecordingOnlySelf));
dispatch(startLocalVideoRecording());
return true;
}
@@ -404,17 +389,18 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
*/
export function mapStateToProps(state: Object) {
const {
transcription,
recordingService,
autoCaptionOnRecord = false,
fileRecordingsServiceEnabled = false,
fileRecordingsServiceSharingEnabled = false,
dropbox = {}
} = state['features/base/config'];
return {
_appKey: dropbox.appKey,
_autoCaptionOnRecord: transcription?.autoCaptionOnRecord ?? false,
_autoCaptionOnRecord: autoCaptionOnRecord,
_conference: state['features/base/conference'].conference,
_fileRecordingsServiceEnabled: recordingService?.enabled ?? false,
_fileRecordingsServiceSharingEnabled: recordingService?.sharingEnabled ?? false,
_fileRecordingsServiceEnabled: fileRecordingsServiceEnabled,
_fileRecordingsServiceSharingEnabled: fileRecordingsServiceSharingEnabled,
_isDropboxEnabled: isDropboxEnabled(state),
_rToken: state['features/dropbox'].rToken,
_tokenExpireDate: state['features/dropbox'].expireDate,

View File

@@ -7,7 +7,6 @@ import {
sendAnalytics
} from '../../../analytics';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { setVideoMuted } from '../../../base/media';
import { stopLocalVideoRecording } from '../../actions';
import { getActiveSession } from '../../functions';
@@ -39,11 +38,6 @@ export type Props = {
*/
dispatch: Function,
/**
* The user trying to stop the video while local recording is running.
*/
localRecordingVideoStop?: boolean,
/**
* Invoked to obtain translated strings.
*/
@@ -84,9 +78,6 @@ export default class AbstractStopRecordingDialog<P: Props>
if (this.props._localRecording) {
this.props.dispatch(stopLocalVideoRecording());
if (this.props.localRecordingVideoStop) {
this.props.dispatch(setVideoMuted(true));
}
} else {
const { _fileRecordingSession } = this.props;

View File

@@ -6,23 +6,16 @@ import { getRoomName } from '../../../base/conference';
// @ts-ignore
import { MEDIA_TYPE } from '../../../base/media';
// @ts-ignore
import { getTrackState, getLocalTrack } from '../../../base/tracks';
import { getTrackState } from '../../../base/tracks';
import { inIframe } from '../../../base/util/iframeUtils';
// @ts-ignore
import { stopLocalVideoRecording } from '../../actions.any';
declare var APP: any;
interface IReduxStore {
dispatch: Function;
getState: Function;
}
interface SelfRecording {
on: boolean;
withVideo: boolean;
}
interface ILocalRecordingManager {
recordingData: Blob[];
recorder: MediaRecorder|undefined;
@@ -37,10 +30,9 @@ interface ILocalRecordingManager {
getFilename: () => string;
saveRecording: (recordingData: Blob[], filename: string) => void;
stopLocalRecording: () => void;
startLocalRecording: (store: IReduxStore, onlySelf: boolean) => void;
startLocalRecording: (store: IReduxStore) => void;
isRecordingLocally: () => boolean;
totalSize: number;
selfRecording: SelfRecording;
}
const getMimeType = (): string => {
@@ -71,10 +63,6 @@ const LocalRecordingManager: ILocalRecordingManager = {
audioDestination: undefined,
roomName: '',
totalSize: 1073741824, // 1GB in bytes
selfRecording: {
on: false,
withVideo: false
},
get mediaType() {
if (!preferredMediaType) {
@@ -105,9 +93,6 @@ const LocalRecordingManager: ILocalRecordingManager = {
* Adds audio track to the recording stream.
*/
addAudioTrackToLocalRecording(track) {
if(this.selfRecording.on) {
return;
}
if (track) {
const stream = new MediaStream([ track ]);
@@ -158,85 +143,58 @@ const LocalRecordingManager: ILocalRecordingManager = {
/**
* Starts a local recording.
*/
async startLocalRecording(store, onlySelf) {
async startLocalRecording(store) {
const { dispatch, getState } = store;
// @ts-ignore
const supportsCaptureHandle = Boolean(navigator.mediaDevices.setCaptureHandleConfig) && !inIframe();
const tabId = uuidV4();
this.selfRecording.on = onlySelf;
this.recordingData = [];
this.roomName = getRoomName(getState());
let gdmStream: MediaStream = new MediaStream();
const tracks = getTrackState(getState());
if(onlySelf) {
let audioTrack: MediaStreamTrack | undefined = getLocalTrack(tracks, MEDIA_TYPE.AUDIO)?.jitsiTrack?.track;
let videoTrack: MediaStreamTrack | undefined = getLocalTrack(tracks, MEDIA_TYPE.VIDEO)?.jitsiTrack?.track;
if(!audioTrack) {
APP.conference.muteAudio(false);
setTimeout(() => APP.conference.muteAudio(true), 100);
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
if(videoTrack && videoTrack.readyState !== 'live') {
videoTrack = undefined;
}
audioTrack = getLocalTrack(getTrackState(getState()), MEDIA_TYPE.AUDIO)?.jitsiTrack?.track;
if(!audioTrack && !videoTrack) {
throw new Error('NoLocalStreams')
}
this.selfRecording.withVideo = Boolean(videoTrack);
const localTracks = [];
audioTrack && localTracks.push(audioTrack);
videoTrack && localTracks.push(videoTrack);
this.stream = new MediaStream(localTracks);
} else {
if (supportsCaptureHandle) {
// @ts-ignore
navigator.mediaDevices.setCaptureHandleConfig({
handle: `JitsiMeet-${tabId}`,
permittedOrigins: [ '*' ]
});
}
if (supportsCaptureHandle) {
// @ts-ignore
gdmStream = await navigator.mediaDevices.getDisplayMedia({
// @ts-ignore
video: { displaySurface: 'browser', frameRate: 30 },
audio: {
autoGainControl: false,
channelCount: 2,
echoCancellation: false,
noiseSuppression: false
}
navigator.mediaDevices.setCaptureHandleConfig({
handle: `JitsiMeet-${tabId}`,
permittedOrigins: [ '*' ]
});
// @ts-ignore
const isBrowser = gdmStream.getVideoTracks()[0].getSettings().displaySurface === 'browser';
if (!isBrowser || (supportsCaptureHandle // @ts-ignore
&& gdmStream.getVideoTracks()[0].getCaptureHandle()?.handle !== `JitsiMeet-${tabId}`)) {
gdmStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
throw new Error('WrongSurfaceSelected');
}
this.initializeAudioMixer();
this.mixAudioStream(gdmStream);
tracks.forEach((track: any) => {
if (track.mediaType === MEDIA_TYPE.AUDIO) {
const audioTrack = track?.jitsiTrack?.track;
this.addAudioTrackToLocalRecording(audioTrack);
}
});
this.stream = new MediaStream([
...(this.audioDestination?.stream.getAudioTracks() || []),
gdmStream.getVideoTracks()[0]
]);
}
this.recordingData = [];
// @ts-ignore
const gdmStream = await navigator.mediaDevices.getDisplayMedia({
// @ts-ignore
video: { displaySurface: 'browser', frameRate: 30 },
audio: {
autoGainControl: false,
channelCount: 2,
echoCancellation: false,
noiseSuppression: false
}
});
// @ts-ignore
const isBrowser = gdmStream.getVideoTracks()[0].getSettings().displaySurface === 'browser';
if (!isBrowser || (supportsCaptureHandle // @ts-ignore
&& gdmStream.getVideoTracks()[0].getCaptureHandle()?.handle !== `JitsiMeet-${tabId}`)) {
gdmStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
throw new Error('WrongSurfaceSelected');
}
this.initializeAudioMixer();
this.mixAudioStream(gdmStream);
this.roomName = getRoomName(getState());
const tracks = getTrackState(getState());
tracks.forEach((track: any) => {
if (track.mediaType === MEDIA_TYPE.AUDIO) {
const audioTrack = track?.jitsiTrack?.track;
this.addAudioTrackToLocalRecording(audioTrack);
}
});
this.stream = new MediaStream([
...(this.audioDestination?.stream.getAudioTracks() || []),
gdmStream.getVideoTracks()[0]
]);
this.recorder = new MediaRecorder(this.stream, {
mimeType: this.mediaType,
videoBitsPerSecond: VIDEO_BIT_RATE
@@ -251,20 +209,18 @@ const LocalRecordingManager: ILocalRecordingManager = {
}
});
if(!onlySelf) {
this.recorder.addEventListener('stop', () => {
this.stream?.getTracks().forEach((track: MediaStreamTrack) => track.stop());
gdmStream?.getTracks().forEach((track: MediaStreamTrack) => track.stop());
});
this.recorder.addEventListener('stop', () => {
this.stream?.getTracks().forEach((track: MediaStreamTrack) => track.stop());
gdmStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
});
gdmStream?.addEventListener('inactive', () => {
dispatch(stopLocalVideoRecording());
});
gdmStream.addEventListener('inactive', () => {
dispatch(stopLocalVideoRecording());
});
this.stream.addEventListener('inactive', () => {
dispatch(stopLocalVideoRecording());
});
}
this.stream.addEventListener('inactive', () => {
dispatch(stopLocalVideoRecording());
});
this.recorder.start(5000);
},

View File

@@ -44,11 +44,6 @@ type Props = {
*/
_dialogStyles: StyleType,
/**
* Whether to hide the storage warning or not.
*/
_hideStorageWarning: boolean,
/**
* Whether local recording is enabled or not.
*/
@@ -101,22 +96,12 @@ type Props = {
*/
isVpaas: boolean,
/**
* Whether or not we should only record the local streams.
*/
localRecordingOnlySelf: boolean,
/**
* The function will be called when there are changes related to the
* switches.
*/
onChange: Function,
/**
* Callback to change the local recording only self setting.
*/
onLocalRecordingSelfChange: Function,
/**
* Callback to be invoked on sharing setting change.
*/
@@ -216,15 +201,9 @@ class StartRecordingDialogContent extends Component<Props> {
* @returns {boolean}
*/
_shouldRenderFileSharingContent() {
const {
fileRecordingsServiceEnabled,
fileRecordingsServiceSharingEnabled,
isVpaas,
selectedRecordingService
} = this.props;
const { fileRecordingsServiceSharingEnabled, isVpaas, selectedRecordingService } = this.props;
if (!fileRecordingsServiceEnabled
|| !fileRecordingsServiceSharingEnabled
if (!fileRecordingsServiceSharingEnabled
|| isVpaas
|| selectedRecordingService !== RECORDING_TYPES.JITSI_REC_SERVICE) {
return false;
@@ -291,14 +270,13 @@ class StartRecordingDialogContent extends Component<Props> {
_renderUploadToTheCloudInfo() {
const {
_dialogStyles,
_hideStorageWarning,
_styles: styles,
isVpaas,
selectedRecordingService,
t
} = this.props;
if (!(isVpaas && selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) || _hideStorageWarning) {
if (!(isVpaas && selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE)) {
return null;
}
@@ -330,8 +308,9 @@ class StartRecordingDialogContent extends Component<Props> {
*/
_shouldRenderNoIntegrationsContent() {
// show the non integrations part only if fileRecordingsServiceEnabled
// is enabled
if (!this.props.fileRecordingsServiceEnabled) {
// is enabled or when there are no integrations enabled
if (!(this.props.fileRecordingsServiceEnabled
|| !this.props.integrationsEnabled)) {
return false;
}
@@ -650,76 +629,45 @@ class StartRecordingDialogContent extends Component<Props> {
}
return (
<>
<Container>
<Container>
<Container
className = 'recording-header recording-header-line'
style = { styles.header }>
<Container
className = 'recording-header recording-header-line'
style = { styles.header }>
<Container
className = 'recording-icon-container'>
<Image
className = 'recording-icon'
src = { LOCAL_RECORDING }
style = { styles.recordingIcon } />
</Container>
<Text
className = 'recording-title'
style = {{
..._dialogStyles.text,
...styles.title
}}>
{ t('recording.saveLocalRecording') }
</Text>
<Switch
className = 'recording-switch'
disabled = { isValidating }
onValueChange = { this._onLocalRecordingSwitchChange }
style = { styles.switch }
trackColor = {{ false: TRACK_COLOR }}
value = { this.props.selectedRecordingService
=== RECORDING_TYPES.LOCAL } />
className = 'recording-icon-container'>
<Image
className = 'recording-icon'
src = { LOCAL_RECORDING }
style = { styles.recordingIcon } />
</Container>
<Text
className = 'recording-title'
style = {{
..._dialogStyles.text,
...styles.title
}}>
{ t('recording.saveLocalRecording') }
</Text>
<Switch
className = 'recording-switch'
disabled = { isValidating }
onValueChange = { this._onLocalRecordingSwitchChange }
style = { styles.switch }
trackColor = {{ false: TRACK_COLOR }}
value = { this.props.selectedRecordingService
=== RECORDING_TYPES.LOCAL } />
</Container>
{selectedRecordingService === RECORDING_TYPES.LOCAL && (
<>
<Container>
<Container
className = 'recording-header space-top'
style = { styles.header }>
<Container className = 'recording-icon-container file-sharing-icon-container'>
<Image
className = 'recording-file-sharing-icon'
src = { ICON_USERS }
style = { styles.recordingIcon } />
</Container>
<Text
className = 'recording-title'
style = {{
..._dialogStyles.text,
...styles.title
}}>
{t('recording.onlyRecordSelf')}
</Text>
<Switch
className = 'recording-switch'
disabled = { isValidating }
onValueChange = { this.props.onLocalRecordingSelfChange }
style = { styles.switch }
trackColor = {{ false: TRACK_COLOR }}
value = { this.props.localRecordingOnlySelf } />
</Container>
</Container>
{selectedRecordingService === RECORDING_TYPES.LOCAL
&& <>
<Text className = 'local-recording-warning text'>
{t('recording.localRecordingWarning')}
</Text>
{_localRecordingNoNotification && !this.props.localRecordingOnlySelf
&& <Text className = 'local-recording-warning notification'>
{t('recording.localRecordingNoNotificationWarning')}
</Text>
}
{_localRecordingNoNotification && <Text className = 'local-recording-warning notification'>
{t('recording.localRecordingNoNotificationWarning')}
</Text>}
</>
)}
</>
}
</Container>
);
}
@@ -759,9 +707,8 @@ function _mapStateToProps(state) {
return {
..._abstractMapStateToProps(state),
isVpaas: isVpaasMeeting(state),
_hideStorageWarning: state['features/base/config'].recording?.hideStorageWarning,
_localRecordingEnabled: !state['features/base/config'].localRecording?.disable,
_localRecordingNoNotification: !state['features/base/config'].localRecording?.notifyAllParticipants,
_localRecordingEnabled: !state['features/base/config'].localRecording.disable,
_localRecordingNoNotification: !state['features/base/config'].localRecording.notifyAllParticipants,
_styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent')
};
}

View File

@@ -55,7 +55,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
const {
isTokenValid,
isValidating,
localRecordingOnlySelf,
selectedRecordingService,
sharingEnabled,
spaceLeft,
@@ -79,9 +78,7 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
integrationsEnabled = { this._areIntegrationsEnabled() }
isTokenValid = { isTokenValid }
isValidating = { isValidating }
localRecordingOnlySelf = { localRecordingOnlySelf }
onChange = { this._onSelectedRecordingServiceChanged }
onLocalRecordingSelfChange = { this._onLocalRecordingSelfChange }
onSharingSettingChanged = { this._onSharingSettingChanged }
selectedRecordingService = { selectedRecordingService }
sharingSetting = { sharingEnabled }
@@ -108,7 +105,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
_onSubmit: () => boolean;
_onSelectedRecordingServiceChanged: (string) => void;
_onSharingSettingChanged: () => void;
_onLocalRecordingSelfChange: () => void;
}
/**

View File

@@ -25,7 +25,7 @@ class StopRecordingDialog extends AbstractStopRecordingDialog<Props> {
* @returns {ReactElement}
*/
render() {
const { t, localRecordingVideoStop } = this.props;
const { t } = this.props;
return (
<Dialog
@@ -33,7 +33,7 @@ class StopRecordingDialog extends AbstractStopRecordingDialog<Props> {
onSubmit = { this._onSubmit }
titleKey = 'dialog.recording'
width = 'small'>
{t(localRecordingVideoStop ? 'recording.localRecordingVideoStop' : 'dialog.stopRecordingWarning') }
{ t('dialog.stopRecordingWarning') }
</Dialog>
);
}

View File

@@ -151,19 +151,11 @@ export function getRecordButtonProps(state: Object): ?string {
const isModerator = isLocalParticipantModerator(state);
const {
enableFeaturesBasedOnToken,
recordingService,
localRecording
fileRecordingsEnabled
} = state['features/base/config'];
const { features = {} } = getLocalParticipant(state);
let localRecordingEnabled = !localRecording?.disable;
if (navigator.product === 'ReactNative') {
localRecordingEnabled = false;
}
const dropboxEnabled = isDropboxEnabled(state);
visible = isModerator && (recordingService?.enabled || localRecordingEnabled || dropboxEnabled);
visible = isModerator && fileRecordingsEnabled;
if (enableFeaturesBasedOnToken) {
visible = visible && String(features.recording) === 'true';

View File

@@ -133,39 +133,27 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
case START_LOCAL_RECORDING: {
const { localRecording } = getState()['features/base/config'];
const { onlySelf } = action;
try {
await LocalRecordingManager.startLocalRecording({ dispatch,
getState }, action.onlySelf);
getState });
const props = {
descriptionKey: 'recording.on',
titleKey: 'dialog.recording'
};
if (localRecording?.notifyAllParticipants && !onlySelf) {
if (localRecording.notifyAllParticipants) {
dispatch(playSound(RECORDING_ON_SOUND_ID));
}
dispatch(showNotification(props, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
dispatch(showNotification({
titleKey: 'recording.localRecordingStartWarningTitle',
descriptionKey: 'recording.localRecordingStartWarning'
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
dispatch(updateLocalRecordingStatus(true, onlySelf));
sendAnalytics(createRecordingEvent('started', `local${onlySelf ? '.self' : ''}`));
dispatch(updateLocalRecordingStatus(true));
sendAnalytics(createRecordingEvent('started', 'local'));
} catch (err) {
logger.error('Capture failed', err);
let descriptionKey = 'recording.error';
if (err.message === 'WrongSurfaceSelected') {
descriptionKey = 'recording.surfaceError';
} else if (err.message === 'NoLocalStreams') {
descriptionKey = 'recording.noStreams';
}
const noTabError = err.message === 'WrongSurfaceSelected';
const props = {
descriptionKey,
descriptionKey: noTabError ? 'recording.surfaceError' : 'recording.error',
titleKey: 'recording.failedToStart'
};
@@ -176,12 +164,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
case STOP_LOCAL_RECORDING: {
const { localRecording } = getState()['features/base/config'];
const { onlySelf } = action;
if (LocalRecordingManager.isRecordingLocally()) {
LocalRecordingManager.stopLocalRecording();
dispatch(updateLocalRecordingStatus(false));
if (localRecording?.notifyAllParticipants && !onlySelf) {
if (localRecording.notifyAllParticipants) {
dispatch(playSound(RECORDING_OFF_SOUND_ID));
}
}

View File

@@ -98,10 +98,10 @@ function SecurityDialog({
setPassword = { setPassword }
setPasswordEditEnabled = { setPasswordEditEnabled } />
{
_showE2ee ? <>
<>
<div className = 'separator-line' />
<E2EESection />
</> : null
</>
}
</div>

View File

@@ -191,7 +191,6 @@ export function submitSoundsTab(newState: Object): Function {
const shouldNotUpdateReactionSounds = getModeratorTabProps(getState()).startReactionsMuted;
const shouldUpdate = (newState.soundsIncomingMessage !== currentState.soundsIncomingMessage)
|| (newState.soundsParticipantJoined !== currentState.soundsParticipantJoined)
|| (newState.soundsParticipantKnocking !== currentState.soundsParticipantKnocking)
|| (newState.soundsParticipantLeft !== currentState.soundsParticipantLeft)
|| (newState.soundsTalkWhileMuted !== currentState.soundsTalkWhileMuted)
|| (newState.soundsReactions !== currentState.soundsReactions);
@@ -200,7 +199,6 @@ export function submitSoundsTab(newState: Object): Function {
const settingsToUpdate = {
soundsIncomingMessage: newState.soundsIncomingMessage,
soundsParticipantJoined: newState.soundsParticipantJoined,
soundsParticipantKnocking: newState.soundsParticipantKnocking,
soundsParticipantLeft: newState.soundsParticipantLeft,
soundsTalkWhileMuted: newState.soundsTalkWhileMuted,
soundsReactions: newState.soundsReactions

View File

@@ -268,8 +268,7 @@ function _mapStateToProps(state, ownProps) {
const moderatorTabProps = getModeratorTabProps(state);
const { showModeratorSettings } = moderatorTabProps;
const { showLanguageSettings, showNotificationsSettings, showPrejoinSettings } = moreTabProps;
const showMoreTab
= configuredTabs.includes('more') && (showLanguageSettings || showNotificationsSettings || showPrejoinSettings);
const showMoreTab = showLanguageSettings || showNotificationsSettings || showPrejoinSettings;
const showProfileSettings
= configuredTabs.includes('profile') && !state['features/base/config'].disableProfile;
const showCalendarSettings

View File

@@ -30,11 +30,6 @@ export type Props = {
*/
soundsParticipantJoined: Boolean,
/**
* Whether or not the sound for the participant entering the lobby should play.
*/
soundsParticipantKnocking: Boolean,
/**
* Whether or not the sound for the participant left should play.
*/
@@ -103,7 +98,6 @@ class SoundsTab extends AbstractDialogTab<Props> {
const {
soundsIncomingMessage,
soundsParticipantJoined,
soundsParticipantKnocking,
soundsParticipantLeft,
soundsTalkWhileMuted,
soundsReactions,
@@ -146,11 +140,6 @@ class SoundsTab extends AbstractDialogTab<Props> {
label = { t('settings.talkWhileMuted') }
name = 'soundsTalkWhileMuted'
onChange = { this._onChange } />
<Checkbox
isChecked = { soundsParticipantKnocking }
label = { t('settings.participantKnocking') }
name = 'soundsParticipantKnocking'
onChange = { this._onChange } />
</div>
);
}

View File

@@ -230,7 +230,6 @@ export function getSoundsTabProps(stateful: Object | Function) {
const {
soundsIncomingMessage,
soundsParticipantJoined,
soundsParticipantKnocking,
soundsParticipantLeft,
soundsTalkWhileMuted,
soundsReactions
@@ -241,7 +240,6 @@ export function getSoundsTabProps(stateful: Object | Function) {
return {
soundsIncomingMessage,
soundsParticipantJoined,
soundsParticipantKnocking,
soundsParticipantLeft,
soundsTalkWhileMuted,
soundsReactions,

View File

@@ -91,12 +91,12 @@ export class AbstractClosedCaptionButton
*/
export function _abstractMapStateToProps(state: Object, ownProps: Object) {
const { _requestingSubtitles } = state['features/subtitles'];
const { transcription } = state['features/base/config'];
const { transcribingEnabled } = state['features/base/config'];
const { isTranscribing } = state['features/transcribing'];
// if the participant is moderator, it can enable transcriptions and if
// transcriptions are already started for the meeting, guests can just show them
const { visible = Boolean(transcription?.enabled
const { visible = Boolean(transcribingEnabled
&& (isLocalParticipantModerator(state) || isTranscribing)) } = ownProps;
return {

View File

@@ -15,10 +15,10 @@ const DEFAULT_TRANSCRIBER_LANG = 'en-US';
* @returns {string}
*/
export function determineTranscriptionLanguage(config: Object) {
const { transcription } = config;
const { preferredTranscribeLanguage, transcribeWithAppLanguage = true, transcribingEnabled } = config;
// if transcriptions are not enabled nothing to determine
if (!transcription?.enabled) {
if (!transcribingEnabled) {
return undefined;
}
@@ -26,9 +26,7 @@ export function determineTranscriptionLanguage(config: Object) {
// config BCP47 value.
// Jitsi language detections uses custom language tags, but the transcriber expects BCP-47 compliant tags,
// we use a mapping file to convert them.
const bcp47Locale = transcription?.useAppLanguage ?? true
? JITSI_TO_BCP47_MAP[i18next.language]
: transcription?.preferredLanguage;
const bcp47Locale = transcribeWithAppLanguage ? JITSI_TO_BCP47_MAP[i18next.language] : preferredTranscribeLanguage;
// Check if the obtained language is supported by the transcriber
let safeBCP47Locale = TRANSCRIBER_LANGS[bcp47Locale] && bcp47Locale;

View File

@@ -1,7 +1,6 @@
// @flow
import { MiddlewareRegistry } from '../base/redux';
import { toggleRequestingSubtitles } from '../subtitles';
import {
HIDDEN_PARTICIPANT_JOINED,
@@ -9,7 +8,6 @@ import {
PARTICIPANT_UPDATED
} from './../base/participants';
import {
_TRANSCRIBER_JOINED,
_TRANSCRIBER_LEFT
} from './actionTypes';
import {
@@ -36,17 +34,9 @@ MiddlewareRegistry.register(store => next => action => {
} = store.getState()['features/transcribing'];
switch (action.type) {
case _TRANSCRIBER_LEFT: {
case _TRANSCRIBER_LEFT:
store.dispatch(showStoppedTranscribingNotification());
const state = store.getState();
const { transcription } = state['features/base/config'];
const { _requestingSubtitles } = state['features/subtitles'];
if (_requestingSubtitles && !transcription?.disableStartForAll) {
store.dispatch(toggleRequestingSubtitles());
}
break;
}
case HIDDEN_PARTICIPANT_JOINED:
if (action.displayName
&& action.displayName === TRANSCRIBER_DISPLAY_NAME) {
@@ -72,16 +62,6 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case _TRANSCRIBER_JOINED: {
const state = store.getState();
const { transcription } = state['features/base/config'];
const { _requestingSubtitles } = state['features/subtitles'];
if (!_requestingSubtitles && !transcription?.disableStartForAll) {
store.dispatch(toggleRequestingSubtitles());
}
break;
}
}
return next(action);

View File

@@ -97,7 +97,7 @@ function getConfig(options = {}) {
const { detectCircularDeps, minimize } = options;
return {
devtool: minimize ? 'source-map' : 'eval-source-map',
devtool: 'source-map',
mode: minimize ? 'production' : 'development',
module: {
rules: [ {
@@ -282,7 +282,7 @@ module.exports = (_env, argv) => {
const mode = typeof argv.mode === 'undefined' ? 'production' : argv.mode;
const isProduction = mode === 'production';
const configOptions = {
detectCircularDeps: Boolean(process.env.DETECT_CIRCULAR_DEPS),
detectCircularDeps: Boolean(process.env.DETECT_CIRCULAR_DEPS) || !isProduction,
minimize: isProduction
};
const config = getConfig(configOptions);