mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-26 15:47:01 +00:00
Compare commits
1 Commits
6299
...
new-mobile
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5916cdb3a |
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
70
config.js
70
config.js
@@ -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.
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
font-size: 14px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
&.space-top {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-header-line {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
188
ios/Podfile.lock
188
ios/Podfile.lock
@@ -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
|
||||
|
||||
@@ -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
35
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -222,7 +222,6 @@ export default [
|
||||
'toolbarConfig',
|
||||
'tileView',
|
||||
'transcribingEnabled',
|
||||
'transcription',
|
||||
'useHostPageLocalStorage',
|
||||
'useTurnUdp',
|
||||
'videoQuality',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import './middleware.any.js';
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -30,7 +30,6 @@ const DEFAULT_STATE = {
|
||||
hideShareAudioHelper: false,
|
||||
soundsIncomingMessage: true,
|
||||
soundsParticipantJoined: true,
|
||||
soundsParticipantKnocking: true,
|
||||
soundsParticipantLeft: true,
|
||||
soundsTalkWhileMuted: true,
|
||||
soundsReactions: true,
|
||||
|
||||
@@ -243,8 +243,8 @@ export const colorMap = {
|
||||
|
||||
// Line separators
|
||||
border03: 'surface04',
|
||||
|
||||
border04: 'primary12',
|
||||
border05: 'surface07',
|
||||
|
||||
// Color for error border & message
|
||||
borderError: 'error06',
|
||||
|
||||
@@ -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 } />
|
||||
|
||||
@@ -41,8 +41,7 @@ export default {
|
||||
alignSelf: 'center',
|
||||
flex: 1,
|
||||
padding: BoxModel.padding,
|
||||
paddingTop: '8%',
|
||||
maxWidth: '80%'
|
||||
paddingTop: '8%'
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 } >
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +98,10 @@ function SecurityDialog({
|
||||
setPassword = { setPassword }
|
||||
setPasswordEditEnabled = { setPasswordEditEnabled } />
|
||||
{
|
||||
_showE2ee ? <>
|
||||
<>
|
||||
<div className = 'separator-line' />
|
||||
<E2EESection />
|
||||
</> : null
|
||||
</>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user