Compare commits

..

1 Commits

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

View File

@@ -32,7 +32,6 @@
android:name=".JitsiMeetActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:theme="@style/JitsiMeetActivityStyle"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize"/>

View File

@@ -51,7 +51,7 @@ public class JitsiMeetOngoingConferenceService extends Service
private boolean isAudioMuted;
public static void launch(Context context, HashMap<String, Object> extraData) {
static void launch(Context context, HashMap<String, Object> extraData) {
OngoingNotification.createOngoingConferenceNotificationChannel();
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
@@ -80,7 +80,7 @@ public class JitsiMeetOngoingConferenceService extends Service
}
}
public static void abort(Context context) {
static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent);
}

View File

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

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
<resources>
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar"/>
</resources>

View File

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

View File

@@ -143,7 +143,6 @@ var config = {
// Disable measuring of audio levels.
// disableAudioLevels: false,
// audioLevelsInterval: 200,
// Enabling this will run the lib-jitsi-meet no audio detection module which
@@ -272,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.
@@ -284,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.
@@ -318,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.
@@ -416,7 +383,7 @@ var config = {
// // This will result in Safari not being able to decode video from endpoints sending VP9 video.
// // When set to false, the conference falls back to VP8 whenever there is an endpoint that doesn't support the
// // preferred codec and goes back to the preferred codec when that endpoint leaves.
// enforcePreferredCodec: false,
// // enforcePreferredCodec: false,
//
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
// // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
@@ -584,8 +551,8 @@ var config = {
// enableFeaturesBasedOnToken: false,
// When enabled the password used for locking a room is restricted to up to the number of digits specified
// default: roomPasswordNumberOfDigits: false,
// roomPasswordNumberOfDigits: 10,
// default: roomPasswordNumberOfDigits: false,
// Message to show the users. Example: 'The service will be down for
// maintenance at 01:00 AM GMT,
@@ -1206,8 +1173,7 @@ var config = {
// 'transcribing',
// 'video-quality',
// 'insecure-room',
// 'highlight-moment',
// 'top-panel-toggle'
// 'highlight-moment'
// ]
// },
@@ -1401,14 +1367,7 @@ var config = {
// // Disables the stage filmstrip
// // (displaying multiple participants on stage besides the vertical filmstrip)
// disableStageFilmstrip: false,
// // Disables the top panel (only shown when a user is sharing their screen).
// disableTopPanel: false,
// // The minimum number of participants that must be in the call for
// // the top panel layout to be used.
// minParticipantCountForTopPanel: 50
// disableStageFilmstrip: false
// },
// Tile view related config options.

View File

@@ -21,7 +21,8 @@ import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-mee
import { getCustomerDetails } from './react/features/jaas/actions.any';
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
import {
setPrejoinDisplayNameRequired
setPrejoinDisplayNameRequired,
setPrejoinPageVisibility
} from './react/features/prejoin/actions';
const logger = Logger.getLogger(__filename);
@@ -246,6 +247,7 @@ function requestAuth(roomName) {
resolve(connection);
};
APP.store.dispatch(setPrejoinPageVisibility(false));
APP.store.dispatch(
openDialog(LoginDialog, { onSuccess,
roomName })

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,14 +24,8 @@
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */; };
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */; };
4EBA6E61286072E300B31882 /* JitsiMeetViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */; };
4EBA6E62286072E300B31882 /* JitsiMeetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */; };
4EBA6E652860B1E800B31882 /* JitsiMeetRenderingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */; };
4EBA6E662860B1E800B31882 /* JitsiMeetRenderingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */; };
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */; };
4EEC9630286C73A2008705FA /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */; };
4EEC9631286C73A2008705FA /* JitsiMeetView+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */; };
6F08DF7D4458EE3CF3F36F6D /* libPods-JitsiMeetSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4376CA6886DE68FD7A4294B /* libPods-JitsiMeetSDK.a */; };
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
@@ -85,15 +79,9 @@
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScheenshareEventEmiter.h; sourceTree = "<group>"; };
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScheenshareEventEmiter.m; sourceTree = "<group>"; };
4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewController.h; sourceTree = "<group>"; };
4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetViewController.m; sourceTree = "<group>"; };
4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetRenderingView.h; sourceTree = "<group>"; };
4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetRenderingView.m; sourceTree = "<group>"; };
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "JitsiMeetView+Private.m"; sourceTree = "<group>"; };
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
@@ -106,6 +94,7 @@
C69EFA0B209A0F660027712B /* JMCallKitListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitListener.swift; sourceTree = "<group>"; };
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExternalAPI.h; sourceTree = "<group>"; };
C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfoPlistUtil.h; sourceTree = "<group>"; };
C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InfoPlistUtil.m; sourceTree = "<group>"; };
@@ -205,17 +194,12 @@
DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */,
DE81A2DD2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m */,
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */,
4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */,
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */,
4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */,
4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */,
4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */,
DE81A2D72316AC7600AE1940 /* LogBridge.m */,
DE65AAC92317FFCD00290BEC /* LogUtils.h */,
DEAFA777229EAD3B0033A7FA /* RNRootView.h */,
DEAFA778229EAD520033A7FA /* RNRootView.m */,
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
C6A3426B204F127900E062DD /* picture-in-picture */,
@@ -300,14 +284,11 @@
DEA9F284258A5D9900D4CD74 /* JitsiMeetSDK.h in Headers */,
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */,
DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */,
4EBA6E652860B1E800B31882 /* JitsiMeetRenderingView.h in Headers */,
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */,
4EEC9630286C73A2008705FA /* JitsiMeetView+Private.h in Headers */,
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
DE81A2D42316AC4D00AE1940 /* JitsiMeetLogger.h in Headers */,
DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */,
4EBA6E61286072E300B31882 /* JitsiMeetViewController.h in Headers */,
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
C81E9AB925AC5AD800B134D9 /* ExternalAPI.h in Headers */,
C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */,
@@ -468,7 +449,6 @@
files = (
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
DE81A2DF2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m in Sources */,
4EBA6E662860B1E800B31882 /* JitsiMeetRenderingView.m in Sources */,
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */,
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
DE81A2D92316AC7600AE1940 /* LogBridge.m in Sources */,
@@ -485,11 +465,9 @@
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */,
4EEC9631286C73A2008705FA /* JitsiMeetView+Private.m in Sources */,
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */,
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
4EBA6E62286072E300B31882 /* JitsiMeetViewController.m in Sources */,
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
DE81A2D52316AC4D00AE1940 /* JitsiMeetLogger.m in Sources */,
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */,

View File

@@ -16,8 +16,6 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
static NSString * const sendEventNotificationName = @"org.jitsi.meet.SendEvent";
@interface ExternalAPI : RCTEventEmitter<RCTBridgeModule>
- (void)sendHangUp;

View File

@@ -15,6 +15,7 @@
*/
#import "ExternalAPI.h"
#import "JitsiMeetView+Private.h"
// Events
static NSString * const hangUpAction = @"org.jitsi.meet.HANG_UP";
@@ -90,14 +91,31 @@ RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(sendEvent:(NSString *)name
data:(NSDictionary *)data
scope:(NSString *)scope) {
// The JavaScript App needs to provide uniquely identifying information to
// the native ExternalAPI module so that the latter may match the former
// to the native JitsiMeetView which hosts it.
JitsiMeetView *view = [JitsiMeetView viewForExternalAPIScope:scope];
if (!view) {
return;
}
id delegate = view.delegate;
if (!delegate) {
return;
}
if ([name isEqual: @"PARTICIPANTS_INFO_RETRIEVED"]) {
[self onParticipantsInfoRetrieved: data];
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:sendEventNotificationName
object:nil
userInfo:@{@"name": name, @"data": data}];
SEL sel = NSSelectorFromString([self methodNameFromEventName:name]);
if (sel && [delegate respondsToSelector:sel]) {
[delegate performSelector:sel withObject:data];
}
}
- (void) onParticipantsInfoRetrieved:(NSDictionary *)data {
@@ -109,6 +127,28 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
[participantInfoCompletionHandlers removeObjectForKey:completionHandlerId];
}
/**
* Converts a specific event name i.e. redux action type description to a
* method name.
*
* @param eventName The event name to convert to a method name.
* @return A method name constructed from the specified `eventName`.
*/
- (NSString *)methodNameFromEventName:(NSString *)eventName {
NSMutableString *methodName
= [NSMutableString stringWithCapacity:eventName.length];
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
if (c.length) {
[methodName appendString:
methodName.length ? c.capitalizedString : c.lowercaseString];
}
}
[methodName appendString:@":"];
return methodName;
}
- (void)sendHangUp {
[self sendEventWithName:hangUpAction body:nil];
}

View File

@@ -15,8 +15,6 @@
*/
#import <Intents/Intents.h>
#import <RNGoogleSignin/RNGoogleSignin.h>
#import <WebRTC/RTCLogging.h>
#import "Dropbox.h"
#import "JitsiMeet+Private.h"
@@ -27,6 +25,9 @@
#import "RNSplashScreen.h"
#import "ScheenshareEventEmiter.h"
#import <RNGoogleSignin/RNGoogleSignin.h>
#import <WebRTC/RTCLogging.h>
@implementation JitsiMeet {
RCTBridgeWrapper *_bridgeWrapper;
NSDictionary *_launchOptions;
@@ -86,12 +87,8 @@
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *))restorationHandler {
JitsiMeetConferenceOptions *options = [self optionsFromUserActivity:userActivity];
if (options) {
[JitsiMeetView updateProps:[options asProps]];
return true;
}
return false;
return options && [JitsiMeetView setPropsInViews:[options asProps]];
}
- (BOOL)application:(UIApplication *)app
@@ -115,9 +112,8 @@
JitsiMeetConferenceOptions *conferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
builder.room = [url absoluteString];
}];
[JitsiMeetView updateProps:[conferenceOptions asProps]];
return true;
return [JitsiMeetView setPropsInViews:[conferenceOptions asProps]];
}
#pragma mark - Utility methods

View File

@@ -1,30 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
#import "JitsiMeetViewDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@interface JitsiMeetRenderingView : UIView
@property (nonatomic, assign) BOOL isPiPEnabled;
- (void)setProps:(NSDictionary *_Nonnull)newProps;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,103 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <mach/mach_time.h>
#import "JitsiMeetRenderingView.h"
#import "ReactUtils.h"
#import "RNRootView.h"
#import "JitsiMeet+Private.h"
/**
* Backwards compatibility: turn the boolean prop into a feature flag.
*/
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
@interface JitsiMeetRenderingView ()
/**
* The unique identifier of this `JitsiMeetView` within the process for the
* purposes of `ExternalAPI`. The name scope was inspired by postis which we
* use on Web for the similar purposes of the iframe-based external API.
*/
@property (nonatomic, strong) NSString *externalAPIScope;
@end
@implementation JitsiMeetRenderingView {
/**
* React Native view where the entire content will be rendered.
*/
RNRootView *rootView;
}
- (instancetype)init {
self = [super init];
if (self) {
// Hook this JitsiMeetView into ExternalAPI.
self.externalAPIScope = [NSUUID UUID].UUIDString;
}
return self;
}
/**
* Passes the given props to the React Native application. The props which we pass
* are a combination of 3 different sources:
*
* - JitsiMeet.defaultConferenceOptions
* - This function's parameters
* - Some extras which are added by this function
*/
- (void)setProps:(NSDictionary *_Nonnull)newProps {
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
// Set the PiP flag if it wasn't manually set.
NSMutableDictionary *featureFlags = props[@"flags"];
// TODO: temporary implementation
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
featureFlags[PiPEnabledFeatureFlag] = @(self.isPiPEnabled);
}
props[@"externalAPIScope"] = self.externalAPIScope;
// This method is supposed to be imperative i.e. a second
// invocation with one and the same URL is expected to join the respective
// conference again if the first invocation was followed by leaving the
// conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one and
// the same URL will not trigger an automatic re-render in the JavaScript
// source code. The workaround implemented below introduces imperativeness
// in React Component props by defining a unique value per invocation.
props[@"timestamp"] = @(mach_absolute_time());
if (rootView) {
// Update props with the new URL.
rootView.appProperties = props;
} else {
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
rootView = [[RNRootView alloc] initWithBridge:bridge
moduleName:@"App"
initialProperties:props];
rootView.backgroundColor = self.backgroundColor;
// Add rootView as a subview which completely covers this one.
[rootView setFrame:[self bounds]];
rootView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addSubview:rootView];
}
}
@end

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2022-present 8x8, Inc.
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +15,11 @@
* limitations under the License.
*/
#import <JitsiMeetSDK/JitsiMeetSDK.h>
#import "JitsiMeetView.h"
NS_ASSUME_NONNULL_BEGIN
@interface JitsiMeetView ()
static NSString * const updateViewPropsNotificationName = @"org.jitsi.meet.UpdateViewProps";
@interface JitsiMeetView (Private)
+ (void)updateProps:(NSDictionary *_Nonnull)newProps;
+ (instancetype _Nullable)viewForExternalAPIScope:(NSString *_Nonnull)externalAPIScope;
+ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,25 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "JitsiMeetView+Private.h"
@implementation JitsiMeetView (Private)
+ (void)updateProps:(NSDictionary *_Nonnull)newProps {
[[NSNotificationCenter defaultCenter] postNotificationName:updateViewPropsNotificationName object:nil userInfo:@{@"props": newProps}];
}
@end

View File

@@ -20,22 +20,43 @@
#import "ExternalAPI.h"
#import "JitsiMeet+Private.h"
#import "JitsiMeetConferenceOptions+Private.h"
#import "JitsiMeetView.h"
#import "JitsiMeetViewController.h"
#import "JitsiMeetView+Private.h"
#import "ReactUtils.h"
#import "RNRootView.h"
@interface JitsiMeetView ()
@property (nonatomic, strong) JitsiMeetViewController *jitsiMeetViewController;
@property (nonatomic, strong) UINavigationController *navController;
@property (nonatomic, readonly) BOOL isPiPEnabled;
/**
* Backwards compatibility: turn the boolean prop into a feature flag.
*/
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
@end
@implementation JitsiMeetView
@implementation JitsiMeetView {
/**
* The unique identifier of this `JitsiMeetView` within the process for the
* purposes of `ExternalAPI`. The name scope was inspired by postis which we
* use on Web for the similar purposes of the iframe-based external API.
*/
NSString *externalAPIScope;
@dynamic isPiPEnabled;
/**
* React Native view where the entire content will be rendered.
*/
RNRootView *rootView;
}
/**
* The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
* identifiers within the process).
*/
static NSMapTable<NSString *, JitsiMeetView *> *views;
/**
* This gets called automagically when the program starts.
*/
__attribute__((constructor))
static void initializeViewsMap() {
views = [NSMapTable strongToWeakObjectsMapTable];
}
#pragma mark Initializers
@@ -66,10 +87,6 @@
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
/**
* Internal initialization:
*
@@ -77,57 +94,70 @@
* - initializes the external API scope
*/
- (void)initWithXXX {
self.jitsiMeetViewController = [[JitsiMeetViewController alloc] init];
self.jitsiMeetViewController.view.frame = [self bounds];
[self addSubview:self.jitsiMeetViewController.view];
[self registerObservers];
// Hook this JitsiMeetView into ExternalAPI.
externalAPIScope = [NSUUID UUID].UUIDString;
[views setObject:self forKey:externalAPIScope];
// Set a background color which is in accord with the JavaScript and Android
// parts of the application and causes less perceived visual flicker than
// the default background color.
self.backgroundColor
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
}
#pragma mark API
- (void)join:(JitsiMeetConferenceOptions *)options {
[self.jitsiMeetViewController join:options withPiP:self.isPiPEnabled];
[self setProps:options == nil ? @{} : [options asProps]];
}
- (void)leave {
[self.jitsiMeetViewController leave];
[self setProps:@{}];
}
- (void)hangUp {
[self.jitsiMeetViewController hangUp];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendHangUp];
}
- (void)setAudioMuted:(BOOL)muted {
[self.jitsiMeetViewController setAudioMuted:muted];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetAudioMuted:muted];
}
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
[self.jitsiMeetViewController sendEndpointTextMessage:message :to];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendEndpointTextMessage:message :to];
}
- (void)toggleScreenShare:(BOOL)enabled {
[self.jitsiMeetViewController toggleScreenShare:enabled];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI toggleScreenShare:enabled];
}
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
[self.jitsiMeetViewController retrieveParticipantsInfo:completionHandler];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI retrieveParticipantsInfo:completionHandler];
}
- (void)openChat:(NSString*)to {
[self.jitsiMeetViewController openChat:to];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI openChat:to];
}
- (void)closeChat {
[self.jitsiMeetViewController closeChat];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI closeChat];
}
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
[self.jitsiMeetViewController sendChatMessage:message :to];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendChatMessage:message :to];
}
- (void)setVideoMuted:(BOOL)muted {
[self.jitsiMeetViewController setVideoMuted:muted];
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetVideoMuted:muted];
}
- (void)setClosedCaptionsEnabled:(BOOL)enabled {
@@ -135,47 +165,79 @@
[externalAPI sendSetClosedCaptionsEnabled:enabled];
}
#pragma mark Private
#pragma mark Private methods
- (BOOL)isPiPEnabled {
return self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)];
}
/**
* Passes the given props to the React Native application. The props which we pass
* are a combination of 3 different sources:
*
* - JitsiMeet.defaultConferenceOptions
* - This function's parameters
* - Some extras which are added by this function
*/
- (void)setProps:(NSDictionary *_Nonnull)newProps {
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
- (void)registerObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSendEventNotification:) name:sendEventNotificationName object:nil];
}
// Set the PiP flag if it wasn't manually set.
NSMutableDictionary *featureFlags = props[@"flags"];
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
featureFlags[PiPEnabledFeatureFlag]
= [NSNumber numberWithBool:
self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]];
}
- (void)handleSendEventNotification:(NSNotification *)notification {
NSString *eventName = notification.userInfo[@"name"];
NSString *eventData = notification.userInfo[@"data"];
SEL sel = NSSelectorFromString([self methodNameFromEventName:eventName]);
props[@"externalAPIScope"] = externalAPIScope;
if (sel && [self.delegate respondsToSelector:sel]) {
[self.delegate performSelector:sel withObject:eventData];
// This method is supposed to be imperative i.e. a second
// invocation with one and the same URL is expected to join the respective
// conference again if the first invocation was followed by leaving the
// conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one and
// the same URL will not trigger an automatic re-render in the JavaScript
// source code. The workaround implemented below introduces imperativeness
// in React Component props by defining a unique value per invocation.
props[@"timestamp"] = @(mach_absolute_time());
if (rootView) {
// Update props with the new URL.
rootView.appProperties = props;
} else {
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
rootView
= [[RNRootView alloc] initWithBridge:bridge
moduleName:@"App"
initialProperties:props];
rootView.backgroundColor = self.backgroundColor;
// Add rootView as a subview which completely covers this one.
[rootView setFrame:[self bounds]];
rootView.autoresizingMask
= UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight;
[self addSubview:rootView];
}
}
/**
* Converts a specific event name i.e. redux action type description to a
* method name.
*
* @param eventName The event name to convert to a method name.
* @return A method name constructed from the specified `eventName`.
*/
- (NSString *)methodNameFromEventName:(NSString *)eventName {
NSMutableString *methodName
= [NSMutableString stringWithCapacity:eventName.length];
+ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps {
BOOL handled = NO;
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
if (c.length) {
[methodName appendString:
methodName.length ? c.capitalizedString : c.lowercaseString];
}
}
[methodName appendString:@":"];
if (views) {
for (NSString *externalAPIScope in views) {
JitsiMeetView *view
= [self viewForExternalAPIScope:externalAPIScope];
return methodName;
if (view) {
[view setProps:newProps];
handled = YES;
}
}
}
return handled;
}
+ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope {
return [views objectForKey:externalAPIScope];
}
@end

View File

@@ -1,38 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
#import "JitsiMeetConferenceOptions.h"
NS_ASSUME_NONNULL_BEGIN
@interface JitsiMeetViewController : UIViewController
- (void)join:(JitsiMeetConferenceOptions *)options withPiP:(BOOL)enablePiP;
- (void)leave;
- (void)hangUp;
- (void)setAudioMuted:(BOOL)muted;
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
- (void)toggleScreenShare:(BOOL)enabled;
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler;
- (void)openChat:(NSString*)to;
- (void)closeChat;
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
- (void)setVideoMuted:(BOOL)muted;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,127 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "JitsiMeetViewController.h"
#import "JitsiMeet+Private.h"
#import "JitsiMeetConferenceOptions+Private.h"
#import "JitsiMeetRenderingView.h"
#import "JitsiMeetView+Private.h"
@interface JitsiMeetViewController ()
@property (strong, nonatomic) JitsiMeetRenderingView *view;
@end
@implementation JitsiMeetViewController
@dynamic view;
- (instancetype)init {
self = [super init];
if (self) {
[self registerObservers];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)loadView {
[super loadView];
self.view = [[JitsiMeetRenderingView alloc] init];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Set a background color which is in accord with the JavaScript and Android
// parts of the application and causes less perceived visual flicker than
// the default background color.
self.view.backgroundColor = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
}
- (void)join:(JitsiMeetConferenceOptions *)options withPiP:(BOOL)enablePiP {
self.view.isPiPEnabled = enablePiP;
[self.view setProps:options == nil ? @{} : [options asProps]];
}
- (void)leave {
[self.view setProps:@{}];
}
- (void)hangUp {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendHangUp];
}
- (void)setAudioMuted:(BOOL)muted {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetAudioMuted:muted];
}
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendEndpointTextMessage:message :to];
}
- (void)toggleScreenShare:(BOOL)enabled {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI toggleScreenShare:enabled];
}
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI retrieveParticipantsInfo:completionHandler];
}
- (void)openChat:(NSString*)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI openChat:to];
}
- (void)closeChat {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI closeChat];
}
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendChatMessage:message :to];
}
- (void)setVideoMuted:(BOOL)muted {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendSetVideoMuted:muted];
}
#pragma mark Private
- (void)registerObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleUpdateViewPropsNotification:) name:updateViewPropsNotificationName object:nil];
}
- (void)handleUpdateViewPropsNotification:(NSNotification *)notification {
NSDictionary *props = [notification.userInfo objectForKey:@"props"];
[self.view setProps:props];
}
@end

View File

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

View File

@@ -17,6 +17,7 @@ import {
import { getReplaceParticipant } from '../../../react/features/base/config/functions';
import { isDialogOpen } from '../../../react/features/base/dialog';
import { setJWT } from '../../../react/features/base/jwt';
import { setPrejoinPageVisibility } from '../../../react/features/prejoin';
import UIUtil from '../util/UIUtil';
import ExternalLoginDialog from './LoginDialog';
@@ -180,6 +181,7 @@ function authenticate(room: Object, lockPassword: string) {
if (isTokenAuthEnabled(config) || room.isExternalAuthEnabled()) {
doExternalAuth(room, lockPassword);
} else {
APP.store.dispatch(setPrejoinPageVisibility(false));
APP.store.dispatch(openLoginDialog());
}
}

60
package-lock.json generated
View File

@@ -74,7 +74,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1461.0.0+96664436/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.2",
"moment-duration-format": "2.2.2",
@@ -104,7 +104,7 @@
"react-native-performance": "2.1.0",
"react-native-reanimated": "https://git@github.com/software-mansion/react-native-reanimated#c4a6b6f687ede090f6081064abe83a2ef9a05784",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "3.13.1",
"react-native-screens": "3.10.1",
"react-native-sound": "0.11.1",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "12.1.0",
@@ -125,7 +125,6 @@
"redux-thunk": "2.2.0",
"resemblejs": "4.0.0",
"rnnoise-wasm": "https://git@github.com/jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",
"util": "0.12.1",
"uuid": "8.3.2",
@@ -143,7 +142,6 @@
"@babel/preset-react": "7.16.0",
"@babel/runtime": "7.16.0",
"@jitsi/eslint-config": "4.0.0",
"@types/lodash": "4.14.182",
"@types/react": "17.0.14",
"@types/react-native": "0.67.6",
"@types/react-redux": "7.1.24",
@@ -5444,12 +5442,6 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"node_modules/@types/lodash": {
"version": "4.14.182",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
"dev": true
},
"node_modules/@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
@@ -7895,9 +7887,9 @@
"integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dependencies": {
"ms": "2.1.2"
},
@@ -12176,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",
@@ -15542,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"
@@ -16840,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",
@@ -24116,12 +24103,6 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/lodash": {
"version": "4.14.182",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
"dev": true
},
"@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
@@ -26099,9 +26080,9 @@
"integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"requires": {
"ms": "2.1.2"
}
@@ -29388,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",
@@ -32037,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"
@@ -32901,11 +32882,6 @@
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.3.0.tgz",
"integrity": "sha1-V6lXWUIEHYV3qGnXx01MOgvYiPY="
},
"seamless-scroll-polyfill": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/seamless-scroll-polyfill/-/seamless-scroll-polyfill-2.1.8.tgz",
"integrity": "sha512-cF92Op90//vEpHphRx25rttJGXIgxcTB1WR5y0ODQhN7O4d0lSEOp5+l3sQDx0aAZ2MfXCqFEb/rG/3ghvVDIQ=="
},
"seedrandom": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",

View File

@@ -79,7 +79,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1461.0.0+96664436/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.2",
"moment-duration-format": "2.2.2",
@@ -109,7 +109,7 @@
"react-native-performance": "2.1.0",
"react-native-reanimated": "https://git@github.com/software-mansion/react-native-reanimated#c4a6b6f687ede090f6081064abe83a2ef9a05784",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "3.13.1",
"react-native-screens": "3.10.1",
"react-native-sound": "0.11.1",
"react-native-splash-screen": "3.3.0",
"react-native-svg": "12.1.0",
@@ -130,7 +130,6 @@
"redux-thunk": "2.2.0",
"resemblejs": "4.0.0",
"rnnoise-wasm": "https://git@github.com/jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",
"util": "0.12.1",
"uuid": "8.3.2",
@@ -148,7 +147,6 @@
"@babel/preset-react": "7.16.0",
"@babel/runtime": "7.16.0",
"@jitsi/eslint-config": "4.0.0",
"@types/lodash": "4.14.182",
"@types/react": "17.0.14",
"@types/react-native": "0.67.6",
"@types/react-redux": "7.1.24",

View File

@@ -1,4 +1,6 @@
import ReducerRegistry from '../base/redux/ReducerRegistry';
// @flow
import { ReducerRegistry } from '../base/redux';
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
@@ -28,22 +30,6 @@ const DEFAULT_STATE = {
}
};
interface Value {
startedTime: number,
value: number
}
export interface IAnalyticsState {
localTracksDuration: {
audio: Value,
video: {
camera: Value,
desktop: Value
},
conference: Value
}
}
/**
* Listen for actions which changes the state of the analytics feature.
*
@@ -52,7 +38,7 @@ export interface IAnalyticsState {
* @param {string} action.type - Type of action.
* @returns {Object}
*/
ReducerRegistry.register('features/analytics', (state: IAnalyticsState = DEFAULT_STATE, action: any) => {
ReducerRegistry.register('features/analytics', (state = DEFAULT_STATE, action) => {
switch (action.type) {
case UPDATE_LOCAL_TRACKS_DURATION:
return {

View File

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

View File

@@ -1,22 +1,4 @@
import { IAnalyticsState } from "../analytics/reducer"
import { IAuthenticationState } from "../authentication/reducer"
import { IAVModerationState } from "../av-moderation/reducer"
import { IAppState } from "../base/app/reducer"
import { IAudioOnlyState } from "../base/audio-only/reducer"
import { IConferenceState } from "../base/conference/reducer"
import { IConfig } from "../base/config/configType"
export interface IStore {
getState: Function,
dispatch: Function
}
export interface IState {
'features/analytics': IAnalyticsState,
'features/authentication': IAuthenticationState,
'features/av-moderation': IAVModerationState,
'features/base/app': IAppState,
'features/base/audio-only': IAudioOnlyState,
'features/base/conference': IConferenceState,
'features/base/config': IConfig,
}

View File

@@ -15,6 +15,7 @@ import {
JitsiConnectionErrors
} from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux';
import { setPrejoinPageVisibility } from '../prejoin';
import {
CANCEL_LOGIN,
@@ -120,6 +121,7 @@ MiddlewareRegistry.register(store => next => action => {
&& error.name === JitsiConnectionErrors.PASSWORD_REQUIRED
&& typeof error.recoverable === 'undefined') {
error.recoverable = true;
store.dispatch(setPrejoinPageVisibility(false));
store.dispatch(openLoginDialog());
}
break;

View File

@@ -1,6 +1,6 @@
// @ts-ignore
import { assign } from '../base/redux/functions';
import ReducerRegistry from '../base/redux/ReducerRegistry';
// @flow
import { assign, ReducerRegistry } from '../base/redux';
import {
CANCEL_LOGIN,
@@ -10,13 +10,6 @@ import {
WAIT_FOR_OWNER
} from './actionTypes';
export interface IAuthenticationState {
error?: Object|undefined;
progress?: number|undefined;
thenableWithCancel?: Object|undefined;
waitForOwnerTimeoutID?: number;
}
/**
* Listens for actions which change the state of the authentication feature.
*
@@ -25,7 +18,7 @@ export interface IAuthenticationState {
* @param {string} action.type - Type of action.
* @returns {Object}
*/
ReducerRegistry.register('features/authentication', (state: IAuthenticationState = {}, action: any) => {
ReducerRegistry.register('features/authentication', (state = {}, action) => {
switch (action.type) {
case CANCEL_LOGIN:
return assign(state, {

View File

@@ -1,9 +1,11 @@
// @flow
import { MEDIA_TYPE, type MediaType } from '../base/media/constants';
/**
* Mapping between a media type and the witelist reducer key.
*/
export const MEDIA_TYPE_TO_WHITELIST_STORE_KEY: { [key: string]: string } = {
export const MEDIA_TYPE_TO_WHITELIST_STORE_KEY: {[key: MediaType]: string} = {
[MEDIA_TYPE.AUDIO]: 'audioWhitelist',
[MEDIA_TYPE.VIDEO]: 'videoWhitelist'
};
@@ -11,7 +13,7 @@ export const MEDIA_TYPE_TO_WHITELIST_STORE_KEY: { [key: string]: string } = {
/**
* Mapping between a media type and the pending reducer key.
*/
export const MEDIA_TYPE_TO_PENDING_STORE_KEY: {[key: string]: string} = {
export const MEDIA_TYPE_TO_PENDING_STORE_KEY: {[key: MediaType]: string} = {
[MEDIA_TYPE.AUDIO]: 'pendingAudio',
[MEDIA_TYPE.VIDEO]: 'pendingVideo'
};

View File

@@ -1,11 +1,12 @@
/* @flow */
import { MEDIA_TYPE } from '../base/media/constants';
import type { MediaType } from '../base/media/constants';
import {
PARTICIPANT_LEFT,
PARTICIPANT_UPDATED
// @ts-ignore
} from '../base/participants';
import ReducerRegistry from '../base/redux/ReducerRegistry';
import { ReducerRegistry } from '../base/redux';
import {
DISABLE_MODERATION,
@@ -28,19 +29,8 @@ const initialState = {
pendingVideo: []
};
export interface IAVModerationState {
audioModerationEnabled: boolean;
videoModerationEnabled: boolean;
audioWhitelist: { [id: string]: boolean };
videoWhitelist: { [id: string]: boolean };
pendingAudio: Array<{ id: string }>;
pendingVideo: Array<{ id: string }>;
audioUnmuteApproved?: boolean|undefined;
videoUnmuteApproved?: boolean|undefined;
}
/**
* Updates a participant in the state for the specified media type.
Updates a participant in the state for the specified media type.
*
* @param {MediaType} mediaType - The media type.
* @param {Object} participant - Information about participant to be modified.
@@ -48,11 +38,11 @@ export interface IAVModerationState {
* @private
* @returns {boolean} - Whether state instance was modified.
*/
function _updatePendingParticipant(mediaType: MediaType, participant: any, state: any = {}) {
function _updatePendingParticipant(mediaType: MediaType, participant, state: Object = {}) {
let arrayItemChanged = false;
const storeKey = MEDIA_TYPE_TO_PENDING_STORE_KEY[mediaType];
const arr = state[storeKey];
const newArr = arr.map((pending: { id: string} ) => {
const newArr = arr.map(pending => {
if (pending.id === participant.id) {
arrayItemChanged = true;
@@ -74,7 +64,7 @@ function _updatePendingParticipant(mediaType: MediaType, participant: any, state
return false;
}
ReducerRegistry.register('features/av-moderation', (state: IAVModerationState = initialState, action: any) => {
ReducerRegistry.register('features/av-moderation', (state = initialState, action) => {
switch (action.type) {
case DISABLE_MODERATION: {

View File

@@ -1,12 +1,10 @@
import ReducerRegistry from '../redux/ReducerRegistry';
// @flow
import { ReducerRegistry } from '../redux';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
export interface IAppState {
app?: Object|undefined;
}
ReducerRegistry.register('features/base/app', (state: IAppState = {}, action) => {
ReducerRegistry.register('features/base/app', (state = {}, action) => {
switch (action.type) {
case APP_WILL_MOUNT: {
const { app } = action;

View File

@@ -1,17 +1,16 @@
import ReducerRegistry from '../redux/ReducerRegistry';
// @flow
import { ReducerRegistry } from '../redux';
import { SET_AUDIO_ONLY } from './actionTypes';
export interface IAudioOnlyState {
enabled: boolean
}
const DEFAULT_STATE = {
enabled: false
};
ReducerRegistry.register('features/base/audio-only', (state: IAudioOnlyState = DEFAULT_STATE, action) => {
ReducerRegistry.register('features/base/audio-only', (state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_AUDIO_ONLY:
return {

View File

@@ -46,7 +46,10 @@ MiddlewareRegistry.register(store => next => action => {
case CONFERENCE_FAILED: {
const errorName = action.error?.name;
if (enableForcedReload && errorName === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
if (errorName === JitsiConferenceErrors.MEMBERS_ONLY_ERROR
|| errorName === JitsiConferenceErrors.PASSWORD_REQUIRED) {
dispatch(setPrejoinPageVisibility(false));
} else if (enableForcedReload && errorName === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
dispatch(setSkipPrejoinOnReload(true));
}

View File

@@ -1,12 +1,9 @@
// @ts-ignore
// @flow
import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../room-lock';
// @ts-ignore
import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection';
// @ts-ignore
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
// @ts-ignore
import { assign, set } from '../redux';
import ReducerRegistry from '../redux/ReducerRegistry';
import { assign, ReducerRegistry, set } from '../redux';
import {
AUTH_STATUS_CHANGED,
@@ -28,7 +25,6 @@ import {
SET_START_MUTED_POLICY,
SET_START_REACTIONS_MUTED
} from './actionTypes';
// @ts-ignore
import { isRoomValid } from './functions';
const DEFAULT_STATE = {
@@ -42,26 +38,13 @@ const DEFAULT_STATE = {
passwordRequired: undefined
};
export interface IConferenceState {
conference: Object|undefined;
e2eeSupported: boolean|undefined;
joining: Object|undefined;
leaving: Object|undefined;
locked: string|undefined;
membersOnly: boolean|undefined;
password: string|undefined;
passwordRequired: boolean|undefined;
authEnabled?: boolean|undefined;
authLogin?: string|undefined;
}
/**
* Listen for actions that contain the conference object, so that it can be
* stored for use by other action creators.
*/
ReducerRegistry.register(
'features/base/conference',
(state: IConferenceState = DEFAULT_STATE, action: any) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case AUTH_STATUS_CHANGED:
return _authStatusChanged(state, action);
@@ -142,7 +125,7 @@ ReducerRegistry.register(
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _authStatusChanged(state: any, { authEnabled, authLogin }: {authEnabled: boolean, authLogin: string}) {
function _authStatusChanged(state, { authEnabled, authLogin }) {
return assign(state, {
authEnabled,
authLogin
@@ -159,7 +142,7 @@ function _authStatusChanged(state: any, { authEnabled, authLogin }: {authEnabled
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _conferenceFailed(state: any, { conference, error }: {conference: Object, error: any}) {
function _conferenceFailed(state, { conference, error }) {
// The current (similar to getCurrentConference in
// base/conference/functions.any.js) conference which is joining or joined:
const conference_ = state.conference || state.joining;
@@ -225,7 +208,7 @@ function _conferenceFailed(state: any, { conference, error }: {conference: Objec
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _conferenceJoined(state: any, { conference }: {conference: any}) {
function _conferenceJoined(state, { conference }) {
// FIXME The indicator which determines whether a JitsiConference is locked
// i.e. password-protected is private to lib-jitsi-meet. However, the
// library does not fire LOCK_STATE_CHANGED upon joining a JitsiConference
@@ -271,7 +254,7 @@ function _conferenceJoined(state: any, { conference }: {conference: any}) {
* @returns {Object} The next/new state of the feature base/conference after the
* reduction of the specified action.
*/
function _conferenceLeftOrWillLeave(state: any, { conference, type }: {conference: Object, type: string}) {
function _conferenceLeftOrWillLeave(state, { conference, type }) {
const nextState = { ...state };
// The redux action CONFERENCE_LEFT is the last time that we should be
@@ -325,7 +308,7 @@ function _conferenceLeftOrWillLeave(state: any, { conference, type }: {conferenc
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _conferenceWillJoin(state: any, { conference }: {conference: Object}) {
function _conferenceWillJoin(state, { conference }) {
return assign(state, {
error: undefined,
joining: conference
@@ -342,7 +325,7 @@ function _conferenceWillJoin(state: any, { conference }: {conference: Object}) {
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _lockStateChanged(state: any, { conference, locked }: {conference: Object, locked: boolean}) {
function _lockStateChanged(state, { conference, locked }) {
if (state.conference !== conference) {
return state;
}
@@ -363,7 +346,7 @@ function _lockStateChanged(state: any, { conference, locked }: {conference: Obje
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _p2pStatusChanged(state: any, action: any) {
function _p2pStatusChanged(state, action) {
return set(state, 'p2p', action.p2p);
}
@@ -376,7 +359,7 @@ function _p2pStatusChanged(state: any, action: any) {
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _setPassword(state: any, { conference, method, password }: {conference: any, method: Object, password: string}) {
function _setPassword(state, { conference, method, password }) {
switch (method) {
case conference.join:
return assign(state, {
@@ -423,7 +406,7 @@ function _setPassword(state: any, { conference, method, password }: {conference:
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _setRoom(state: any, action: any) {
function _setRoom(state, action) {
let { room } = action;
if (!isRoomValid(room)) {

View File

@@ -1,467 +0,0 @@
type ToolbarButtons = 'camera' |
'chat' |
'closedcaptions' |
'desktop' |
'dock-iframe' |
'download' |
'embedmeeting' |
'etherpad' |
'feedback' |
'filmstrip' |
'fullscreen' |
'hangup' |
'help' |
'highlight' |
'invite' |
'linktosalesforce' |
'livestreaming' |
'microphone' |
'participants-pane' |
'profile' |
'raisehand' |
'recording' |
'security' |
'select-background' |
'settings' |
'shareaudio' |
'sharedvideo' |
'shortcuts' |
'stats' |
'tileview' |
'toggle-camera' |
'undock-iframe' |
'videoquality' |
'__end';
type ButtonsWithNotifyClick = 'camera' |
'chat' |
'closedcaptions' |
'desktop' |
'download' |
'embedmeeting' |
'etherpad' |
'feedback' |
'filmstrip' |
'fullscreen' |
'hangup' |
'help' |
'invite' |
'livestreaming' |
'microphone' |
'mute-everyone' |
'mute-video-everyone' |
'participants-pane' |
'profile' |
'raisehand' |
'recording' |
'security' |
'select-background' |
'settings' |
'shareaudio' |
'sharedvideo' |
'shortcuts' |
'stats' |
'tileview' |
'toggle-camera' |
'videoquality' |
'add-passcode' |
'__end';
type Sounds = 'ASKED_TO_UNMUTE_SOUND' |
'E2EE_OFF_SOUND' |
'E2EE_ON_SOUND' |
'INCOMING_MSG_SOUND' |
'KNOCKING_PARTICIPANT_SOUND' |
'LIVE_STREAMING_OFF_SOUND' |
'LIVE_STREAMING_ON_SOUND' |
'NO_AUDIO_SIGNAL_SOUND' |
'NOISY_AUDIO_INPUT_SOUND' |
'OUTGOING_CALL_EXPIRED_SOUND' |
'OUTGOING_CALL_REJECTED_SOUND' |
'OUTGOING_CALL_RINGING_SOUND' |
'OUTGOING_CALL_START_SOUND' |
'PARTICIPANT_JOINED_SOUND' |
'PARTICIPANT_LEFT_SOUND' |
'RAISE_HAND_SOUND' |
'REACTION_SOUND' |
'RECORDING_OFF_SOUND' |
'RECORDING_ON_SOUND' |
'TALK_WHILE_MUTED_SOUND';
export interface IConfig {
hosts?: {
domain: string;
anonymousdomain?: string;
authdomain?: string;
focus?: string;
muc: string;
};
bosh?: string;
websocket?: string;
focusUserJid?: string;
testing?: {
disableE2EE?: boolean;
enableThumbnailReordering?: boolean;
mobileXmppWsThreshold?: number;
p2pTestMode?: boolean;
testMode?: boolean;
noAutoPlayVideo?: boolean;
capScreenshareBitrate?: number;
setScreenSharingResolutionConstraints?: boolean;
callStatsThreshold?: number;
};
flags?: {
sourceNameSignaling?: boolean;
sendMultipleVideoStreams?: boolean;
};
disableModeratorIndicator?: boolean;
disableReactions?: boolean;
disableReactionsModeration?: boolean;
disablePolls?: boolean;
disableSelfView?: boolean;
disableSelfViewSettings?: boolean;
screenshotCapture?: {
enabled?: boolean;
mode?: 'always' | 'recording';
};
webrtcIceUdpDisable?: boolean;
webrtcIceTcpDisable?: boolean;
enableUnifiedOnChrome?: boolean;
disableAudioLevels?: boolean;
audioLevelsInterval?: number;
enableNoAudioDetection?: boolean;
enableSaveLogs?: boolean;
disableShowMoreStats?: boolean;
enableNoisyMicDetection?: boolean;
startAudioOnly?: boolean;
startAudioMuted?: boolean;
startWithAudioMuted?: boolean;
startSilent?: boolean;
enableOpusRed?: boolean;
audioQuality?: {
stereo?: boolean;
opusMaxAverageBitrate?: number|null;
};
stereo?: boolean;
opusMaxAverageBitrate?: number;
resolution?: number;
disableRemoveRaisedHandOnFocus?: boolean;
disableSpeakerStatsSearch?: boolean;
speakerStatsOrder?: Array<'role'|'name'|'hasLeft'>;
maxFullResolutionParticipants?: number;
constraints?: {
video?: {
height?: {
ideal?: number;
max?: number;
min?: number;
}
}
};
disableSimulcast?: boolean;
enableLayerSuspension?: boolean;
startVideoMuted?: number;
startWithVideoMuted?: boolean;
preferH264?: boolean;
disableH264?: boolean;
desktopSharingFrameRate?: {
min?: number;
max?: number;
};
startScreenSharing?: boolean;
fileRecordingsEnabled?: boolean;
dropbox?: {
appKey: string;
redirectURI?: string;
};
recordingService?: {
enabled?: boolean;
sharingEnabled?: boolean;
hideStorageWarning?: boolean;
};
fileRecordingsServiceEnabled?: boolean;
fileRecordingsServiceSharingEnabled?: boolean;
liveStreamingEnabled?: boolean;
localRecording?: {
disable?: boolean;
notifyAllParticipants?: boolean;
};
transcribingEnabled?: boolean;
transcribeWithAppLanguage?: boolean;
preferredTranscribeLanguage?: string;
autoCaptionOnRecord?: boolean;
transcription?: {
enabled?: boolean;
useAppLanguage?: boolean;
preferredLanguage?: string;
disableStartForAll?: boolean;
autoCaptionOnRecord?: boolean;
};
channelLastN?: number;
connectionIndicators?: {
autoHide?: boolean;
autoHideTimeout?: number;
disabled?: boolean;
disableDetails?: boolean;
inactiveDisabled?: boolean;
};
startLastN?: number;
lastNLimits?: {
[key: number]: number;
};
useNewBandwidthAllocationStrategy?: boolean;
videoQuality?: {
disabledCodec?: string;
preferredCodec?: string;
enforcePreferredCodec?: boolean;
maxBitratesVideo?: {
[key: string]: {
low?: number;
standard?: number;
high?: number;
}
};
minHeightForQualityLvl: {
[key: number]: string;
};
resizeDesktopForPresenter?: boolean;
};
notificationTimeouts?: {
short?: number;
medium?: number;
long?: number;
};
recordingLimit?: {
limit?: number;
appName?: string;
appURL?: string;
};
disableRtx?: boolean;
disableBeforeUnloadHandlers?: boolean;
enableTcc?: boolean;
enableRemb?: boolean;
enableIceRestart?: boolean;
enableForcedReload?: boolean;
useTurnUdp?: boolean;
enableEncodedTransformSupport?: boolean;
disableResponsiveTiles?: boolean;
hideLobbyButton?: boolean;
autoKnockLobby?: boolean;
enableLobbyChat?: boolean;
hideAddRoomButton?: boolean;
requireDisplayName?: boolean;
enableWelcomePage?: boolean;
disableShortcuts?: boolean;
disableInitialGUM?: boolean;
enableClosePage?: boolean;
disable1On1Mode?: boolean|null;
defaultLocalDisplayName?: string;
defaultRemoteDisplayName?: string;
hideDisplayName?: boolean;
hideDominantSpeakerBadge?: boolean;
defaultLanguage?: string;
disableProfile?: boolean;
hideEmailInSettings?: boolean;
enableFeaturesBasedOnToken?: boolean;
roomPasswordNumberOfDigits?: number;
noticeMessage?: string;
enableCalendarIntegration?: boolean;
prejoinConfig?: {
enabled?: boolean;
hideDisplayName?: boolean;
hideExtraJoinButtons?: Array<string>;
};
prejoinPageEnabled?: boolean;
readOnlyName?: boolean;
openSharedDocumentOnJoin?: boolean;
enableInsecureRoomNameWarning?: boolean;
enableAutomaticUrlCopy?: boolean;
corsAvatarURLs?: Array<string>;
gravatarBaseURL?: string;
gravatar?: {
baseUrl?: string;
disabled?: boolean;
};
inviteAppName?: string|null;
toolbarButtons?: Array<ToolbarButtons>;
toolbarConfig?: {
initialTimeout?: number;
timeout?: number;
alwaysVisible?: boolean;
autoHideWhileChatIsOpen?: boolean;
};
buttonsWithNotifyClick?: Array<ButtonsWithNotifyClick | { key: ButtonsWithNotifyClick; preventExecution: boolean }>;
hiddenPremeetingButtons?: Array<'microphone' | 'camera' | 'select-background' | 'invite' | 'settings'>;
gatherStats?: boolean;
pcStatsInterval?: number;
callStatsID?: string;
callStatsSecret?: string;
callStatsConfigParams?: {
disableBeforeUnloadHandler?: boolean;
applicationVersion?: string;
disablePrecalltest?: boolean;
siteID?: string;
additionalIDs?: {
customerID?: string;
tenantID?: string;
productName?: string;
meetingsName?: string;
serverName?: string;
pbxID?: string;
pbxExtensionID?: string;
fqExtensionID?: string;
sessionID?: string;
};
collectLegacyStats?: boolean;
collectIP?: boolean;
};
enableDisplayNameInStats?: boolean;
enableEmailInStats?: boolean;
faceLandmarks?: {
enableFaceCentering?: boolean;
enableFaceExpressionsDetection?: boolean;
enableDisplayFaceExpressions?: boolean;
enableRTCStats?: boolean;
faceCenteringThreshold?: number;
captureInterval?: number;
};
feedbackPercentage?: number;
disableThirdPartyRequests?: boolean;
p2p?: {
enabled?: boolean;
enableUnifiedOnChrome?: boolean;
iceTransportPolicy?: string;
preferH264?: boolean;
preferredCodec?: string;
disableH264?: boolean;
disabledCodec?: string;
backToP2PDelay?: number;
stunServers?: Array<{urls: string}>;
};
analytics?: {
disabled?: boolean;
googleAnalyticsTrackingId?: string;
matomoEndpoint?: string;
matomoSiteID?: string;
amplitudeAPPKey?: string;
obfuscateRoomName?: boolean;
rtcstatsEnabled?: boolean;
rtcstatsEndpoint?: string;
rtcstatsPolIInterval?: number;
scriptURLs?: Array<string>;
};
apiLogLevels?: Array<'warn' | 'log' | 'error' | 'info' | 'debug'>;
deploymentInfo?: {
shard?: string;
region?: string;
userRegion?: string;
};
disabledSounds?: Array<Sounds>;
disableRecordAudioNotification?: boolean;
disableJoinLeaveSounds?: boolean;
disableIncomingMessageSound?: boolean;
chromeExtensionBanner?: {
url?: string;
edgeUrl?: string;
chromeExtensionsInfo?: Array<{id: string; path: string}>;
};
e2ee?: {
labels?: {
tooltip?: string;
description?: string;
label?: string;
warning?: string;
};
externallyManagedKey?: boolean;
e2eeLabels?: {
tooltip?: string;
description?: string;
label?: string;
warning?: string;
};
};
e2eeLabels?: {
tooltip?: string;
description?: string;
label?: string;
warning?: string;
};
e2eping?: {
enabled?: boolean;
numRequests?: number;
maxConferenceSize?: number;
maxMessagesPerSecond?: number;
};
_desktopSharingSourceDevice?: string;
disableDeepLinking?: boolean;
disableLocalVideoFlip?: boolean;
doNotFlipLocalVideo?: boolean;
disableInviteFunctions?: boolean;
doNotStoreRoom?: boolean;
deploymentUrls?: {
userDocumentationURL?: string;
downloadAppsUrl?: string;
};
remoteVideoMenu?: {
disabled?: boolean;
disableKick?: boolean;
disableGrantModerator?: boolean;
disablePrivateChat?: boolean;
};
salesforceUrl?: string;
disableRemoteMute?: boolean;
enableLipSync?: boolean;
dynamicBrandingUrl?: string;
participantsPane?: {
hideModeratorSettingsTab?: boolean;
hideMoreActionsButton?: boolean;
hideMuteAllButton?: boolean;
};
breakoutRooms?: {
hideAddRoomButton?: boolean;
hideAutoAssignButton?: boolean;
hideJoinRoomButton?: boolean;
};
disableAddingBackgroundImages?: boolean;
disableScreensharingVirtualBackground?: boolean;
backgroundAlpha?: number;
moderatedRoomServiceUrl?: string;
disableTileView?: boolean;
disableTileEnlargement?: boolean;
conferenceInfo?: {
alwaysVisible?: Array<string>;
autoHide?: Array<string>;
};
hideConferenceSubject?: boolean;
hideConferenceTimer?: boolean;
hideRecordingLabel?: boolean;
hideParticipantsStats?: boolean;
subject?: string;
localSubject?: string;
useHostPageLocalStorage?: boolean;
etherpad_base?: string;
dialInNumbersUrl?: string;
dialInConfCodeUrl?: string;
brandingRoomAlias?: string;
mouseMoveCallbackInterval?: number;
notifications?: Array<string>;
disabledNotifications?: Array<string>;
disableFilmstripAutohiding?: boolean;
filmstrip?: {
disableResizable?: boolean;
disableStageFilmstrip?: boolean;
disableTopPanel?: boolean;
minParticipantCountForTopPanel?: number;
};
tileView?: {
numberOfVisibleTiles?: number;
};
disableChatSmileys?: boolean;
giphy?: {
enabled?: boolean;
sdkKey?: '';
displayMode?: 'all' | 'tile' | 'chat';
tileTime?: number;
};
locationURL?: string;
}

View File

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

View File

@@ -1,9 +1,9 @@
// @flow
import _ from 'lodash';
import { CONFERENCE_INFO } from '../../conference/components/constants';
// @ts-ignore
import { equals } from '../redux';
import ReducerRegistry from '../redux/ReducerRegistry';
import { equals, ReducerRegistry } from '../redux';
import {
UPDATE_CONFIG,
@@ -12,11 +12,9 @@ import {
SET_CONFIG,
OVERWRITE_CONFIG
} from './actionTypes';
import { IConfig } from './configType';
// @ts-ignore
import { _cleanupConfig } from './functions';
declare var interfaceConfig: any;
declare var interfaceConfig: Object;
/**
* The initial state of the feature base/config when executing in a
@@ -28,7 +26,7 @@ declare var interfaceConfig: any;
*
* @type {Object}
*/
const INITIAL_NON_RN_STATE: IConfig = {
const INITIAL_NON_RN_STATE = {
};
/**
@@ -40,7 +38,7 @@ const INITIAL_NON_RN_STATE: IConfig = {
*
* @type {Object}
*/
const INITIAL_RN_STATE: IConfig = {
const INITIAL_RN_STATE = {
analytics: {},
// FIXME The support for audio levels in lib-jitsi-meet polls the statistics
@@ -63,14 +61,14 @@ const INITIAL_RN_STATE: IConfig = {
* Mapping between old configs controlling the conference info headers visibility and the
* new configs. Needed in order to keep backwards compatibility.
*/
const CONFERENCE_HEADER_MAPPING: any = {
const CONFERENCE_HEADER_MAPPING = {
hideConferenceTimer: [ 'conference-timer' ],
hideConferenceSubject: [ 'subject' ],
hideParticipantsStats: [ 'participants-count' ],
hideRecordingLabel: [ 'recording' ]
};
ReducerRegistry.register('features/base/config', (state: IConfig = _getInitialState(), action: any) => {
ReducerRegistry.register('features/base/config', (state = _getInitialState(), action) => {
switch (action.type) {
case UPDATE_CONFIG:
return _updateConfig(state, action);
@@ -141,12 +139,12 @@ function _getInitialState() {
* Reduces a specific Redux action SET_CONFIG of the feature
* base/lib-jitsi-meet.
*
* @param {IConfig} state - The Redux state of the feature base/config.
* @param {Object} state - The Redux state of the feature base/config.
* @param {Action} action - The Redux action SET_CONFIG to reduce.
* @private
* @returns {Object} The new state after the reduction of the specified action.
*/
function _setConfig(state: IConfig, { config }: {config: IConfig}) {
function _setConfig(state, { config }) {
// 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. The downloading is planned for implementation in the
@@ -189,10 +187,10 @@ function _setConfig(state: IConfig, { config }: {config: IConfig}) {
/**
* Processes the conferenceInfo object against the defaults.
*
* @param {IConfig} config - The old config.
* @param {Object} config - The old config.
* @returns {Object} The processed conferenceInfo object.
*/
function _getConferenceInfo(config: IConfig) {
function _getConferenceInfo(config) {
const { conferenceInfo } = config;
if (conferenceInfo) {
@@ -209,7 +207,11 @@ function _getConferenceInfo(config: IConfig) {
/**
* 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
@@ -217,11 +219,11 @@ function _getConferenceInfo(config: IConfig) {
* @returns {Object} A config {@code Object} which is in the latest format
* supported by jitsi-meet.
*/
function _translateInterfaceConfig(oldValue: IConfig) {
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;
}
@@ -229,7 +231,6 @@ function _translateInterfaceConfig(oldValue: IConfig) {
oldValue.toolbarConfig = {};
}
newValue.toolbarConfig = oldValue.toolbarConfig || {};
if (typeof oldValue.toolbarConfig.alwaysVisible !== 'boolean'
&& typeof interfaceConfig === 'object'
&& typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE === 'boolean') {
@@ -248,80 +249,39 @@ function _translateInterfaceConfig(oldValue: IConfig) {
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: IConfig) {
const newValue = _translateInterfaceConfig(oldValue);
// Translate deprecated config values to new config values.
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key as keyof IConfig]);
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key]);
if (filteredConferenceInfo.length) {
newValue.conferenceInfo = _getConferenceInfo(oldValue);
filteredConferenceInfo.forEach(key => {
newValue.conferenceInfo = oldValue.conferenceInfo ?? {};
// hideRecordingLabel does not mean not render it at all, but autoHide it
if (key === 'hideRecordingLabel') {
newValue.conferenceInfo.alwaysVisible
= (newValue.conferenceInfo?.alwaysVisible ?? []).filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
= newValue.conferenceInfo.alwaysVisible.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
newValue.conferenceInfo.autoHide
= _.union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
} else {
newValue.conferenceInfo.alwaysVisible
= (newValue.conferenceInfo.alwaysVisible ?? []).filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
= newValue.conferenceInfo.alwaysVisible.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
newValue.conferenceInfo.autoHide
= (newValue.conferenceInfo.autoHide ?? []).filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
= newValue.conferenceInfo.autoHide.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
}
});
}
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')
@@ -355,15 +315,33 @@ function _translateLegacyConfig(oldValue: IConfig) {
};
}
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 */
@@ -375,46 +353,6 @@ function _translateLegacyConfig(oldValue: IConfig) {
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;
}
@@ -426,7 +364,7 @@ function _translateLegacyConfig(oldValue: IConfig) {
* @private
* @returns {Object} The new state after the reduction of the specified action.
*/
function _updateConfig(state: IConfig, { config }: {config: IConfig}) {
function _updateConfig(state, { config }) {
const newState = _.merge({}, state, config);
_cleanupConfig(newState);

View File

@@ -1,3 +1,5 @@
// @flow
/**
* The set of facing modes for camera.
*
@@ -15,7 +17,7 @@ export type MediaType = 'audio' | 'video' | 'presenter' | 'screenshare';
*
* @enum {string}
*/
export const MEDIA_TYPE: {[key: string]: MediaType} = {
export const MEDIA_TYPE = {
AUDIO: 'audio',
PRESENTER: 'presenter',
SCREENSHARE: 'screenshare',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,20 @@
import { Action, combineReducers } from 'redux';
/* @flow */
import { combineReducers } from 'redux';
import type { Reducer } from 'redux';
/**
* The type of the dictionary/map which associates a reducer (function) with the
* name of he Redux state property managed by the reducer.
*/
declare type NameReducerMap<S, A> = { [name: string]: Reducer<S, Action<any>> };
declare type NameReducerMap<S, A> = { [name: string]: Reducer<S, A> };
/**
* A registry for Redux reducers, allowing features to register themselves
* without needing to create additional inter-feature dependencies.
*/
class ReducerRegistry {
_elements: NameReducerMap<any, any>;
_elements: NameReducerMap<*, *>;
/**
* Creates a ReducerRegistry instance.
@@ -35,7 +37,7 @@ class ReducerRegistry {
* included (such as reducers from third-party modules).
* @returns {Function}
*/
combineReducers(additional: NameReducerMap<any, any> = {}) {
combineReducers(additional: NameReducerMap<*, *> = {}) {
// $FlowExpectedError
return combineReducers({
...this._elements,
@@ -53,7 +55,7 @@ class ReducerRegistry {
* @param {Reducer} reducer - A Redux reducer.
* @returns {void}
*/
register(name: string, reducer: Reducer<any, any>) {
register(name: string, reducer: Reducer<*, *>) {
this._elements[name] = reducer;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,6 @@ export const CONFERENCE_INFO = {
'e2ee',
'transcribing',
'video-quality',
'insecure-room',
'top-panel-toggle'
'insecure-room'
]
};

View File

@@ -11,7 +11,7 @@ import { translate } from '../../../base/i18n';
import { connect as reactReduxConnect } from '../../../base/redux';
import { setColorAlpha } from '../../../base/util';
import { Chat } from '../../../chat';
import { MainFilmstrip, StageFilmstrip, ScreenshareFilmstrip } from '../../../filmstrip';
import { MainFilmstrip, StageFilmstrip } from '../../../filmstrip';
import { CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { LobbyScreen } from '../../../lobby';
@@ -239,7 +239,6 @@ class Conference extends AbstractConference<Props, *> {
{
_showPrejoin || _showLobby || (<>
<StageFilmstrip />
<ScreenshareFilmstrip />
<MainFilmstrip />
</>)
}

View File

@@ -20,7 +20,6 @@ import InsecureRoomNameLabel from './InsecureRoomNameLabel';
import ParticipantsCount from './ParticipantsCount';
import RaisedHandsCountLabel from './RaisedHandsCountLabel';
import SubjectText from './SubjectText';
import ToggleTopPanelLabel from './ToggleTopPanelLabel';
/**
* The type of the React {@code Component} props of {@link Subject}.
@@ -83,10 +82,6 @@ const COMPONENTS = [
{
Component: InsecureRoomNameLabel,
id: 'insecure-room'
},
{
Component: ToggleTopPanelLabel,
id: 'top-panel-toggle'
}
];

View File

@@ -1,31 +0,0 @@
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
// @ts-ignore
import { IconMenuDown } from '../../../base/icons';
// @ts-ignore
import { Label } from '../../../base/label';
// @ts-ignore
import { Tooltip } from '../../../base/tooltip';
// @ts-ignore
import { setTopPanelVisible } from '../../../filmstrip/actions.web';
const ToggleTopPanelLabel = () => {
const dispatch = useDispatch();
const { t } = useTranslation();
const topPanelHidden = !useSelector((state: any) => state['features/filmstrip'].topPanelVisible);
const onClick = useCallback(() => {
dispatch(setTopPanelVisible(true));
}, []);
return topPanelHidden && (<Tooltip
content={t('toggleTopPanelLabel') }
position = { 'bottom' }>
<Label
icon={IconMenuDown}
onClick = { onClick }/>
</Tooltip>);
};
export default ToggleTopPanelLabel;

View File

@@ -22,7 +22,7 @@ export function fetchCustomBrandingData() {
const { customizationReady } = state['features/dynamic-branding'];
if (!customizationReady) {
const url = await getDynamicBrandingUrl(state);
const url = await getDynamicBrandingUrl();
if (url) {
try {

View File

@@ -6,7 +6,6 @@ import {
setDynamicBrandingFailed,
setDynamicBrandingReady
} from './actions.any';
import { getDynamicBrandingUrl } from './functions.any';
import logger from './logger';
@@ -20,7 +19,7 @@ import logger from './logger';
export function fetchCustomBrandingData() {
return async function(dispatch: Function, getState: Function) {
const state = getState();
const dynamicBrandingUrl = await getDynamicBrandingUrl(state);
const { dynamicBrandingUrl } = state['features/base/config'];
if (dynamicBrandingUrl) {
try {

View File

@@ -1,7 +1,6 @@
// @flow
import { toState } from '../base/redux';
import { loadConfig } from '../base/lib-jitsi-meet/functions';
/**
* Extracts the fqn part from a path, where fqn represents
@@ -30,13 +29,10 @@ export function extractFqnFromPath(state?: Object) {
/**
* Returns the url used for fetching dynamic branding.
*
* @param {Object | Function} stateful - The redux store, state, or
* {@code getState} function.
* @returns {string}
*/
export async function getDynamicBrandingUrl(stateful: Object | Function) {
const state = toState(stateful);
const config = state['features/base/config'];
export async function getDynamicBrandingUrl() {
const config = await loadConfig(window.location.href);
const { dynamicBrandingUrl } = config;
if (dynamicBrandingUrl) {
@@ -44,7 +40,7 @@ export async function getDynamicBrandingUrl(stateful: Object | Function) {
}
const { brandingDataUrl: baseUrl } = config;
const fqn = extractFqnFromPath(state);
const fqn = extractFqnFromPath();
if (baseUrl && fqn) {
return `${baseUrl}?conferenceFqn=${encodeURIComponent(fqn)}`;

View File

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

View File

@@ -3,9 +3,13 @@ import { View } from 'react-native';
import { WebView } from 'react-native-webview';
import { translate } from '../../../base/i18n';
import { IconArrowBack } from '../../../base/icons';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import HeaderNavigationButton
from '../../../mobile/navigation/components/HeaderNavigationButton';
import { goBack } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { getSharedDocumentUrl } from '../../functions';
import styles, { INDICATOR_COLOR } from './styles';
@@ -46,6 +50,25 @@ class SharedDocument extends PureComponent<Props> {
this._renderLoading = this._renderLoading.bind(this);
}
/**
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately after this component is mounted.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
const { navigation } = this.props;
navigation.setOptions({
headerLeft: () => (
<HeaderNavigationButton
onPress = { goBack }
src = { IconArrowBack } />
)
});
}
/**
* Implements React's {@link Component#render()}.
*

View File

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

View File

@@ -92,15 +92,6 @@ export const SET_VOLUME = 'SET_VOLUME';
*/
export const SET_VISIBLE_REMOTE_PARTICIPANTS = 'SET_VISIBLE_REMOTE_PARTICIPANTS';
/**
* The type of action which sets the height for the top panel filmstrip.
* {
* type: SET_FILMSTRIP_HEIGHT,
* height: number
* }
*/
export const SET_FILMSTRIP_HEIGHT = 'SET_FILMSTRIP_HEIGHT';
/**
* The type of action which sets the width for the vertical filmstrip.
* {
@@ -110,15 +101,6 @@ export const SET_FILMSTRIP_HEIGHT = 'SET_FILMSTRIP_HEIGHT';
*/
export const SET_FILMSTRIP_WIDTH = 'SET_FILMSTRIP_WIDTH';
/**
* The type of action which sets the height for the top panel filmstrip (user resized).
* {
* type: SET_USER_FILMSTRIP_HEIGHT,
* height: number
* }
*/
export const SET_USER_FILMSTRIP_HEIGHT = 'SET_USER_FILMSTRIP_HEIGHT';
/**
* The type of action which sets the width for the vertical filmstrip (user resized).
* {
@@ -205,19 +187,3 @@ export const TOGGLE_PIN_STAGE_PARTICIPANT = 'TOGGLE_PIN_STAGE_PARTICIPANT';
* }
*/
export const CLEAR_STAGE_PARTICIPANTS = 'CLEAR_STAGE_PARTICIPANTS';
/**
* The type of Redux action which sets the dimensions of the screenshare tile.
* {
* type: SET_SCREENSHARING_TILE_DIMENSIONS
* }
*/
export const SET_SCREENSHARING_TILE_DIMENSIONS = 'SET_SCREENSHARING_TILE_DIMENSIONS';
/**
* The type of Redux action which sets the visibility of the top panel.
* {
* type: SET_TOP_PANEL_VISIBILITY
* }
*/
export const SET_TOP_PANEL_VISIBILITY = 'SET_TOP_PANEL_VISIBILITY';

View File

@@ -25,11 +25,7 @@ import {
SET_VOLUME,
SET_MAX_STAGE_PARTICIPANTS,
TOGGLE_PIN_STAGE_PARTICIPANT,
CLEAR_STAGE_PARTICIPANTS,
SET_SCREENSHARING_TILE_DIMENSIONS,
SET_USER_FILMSTRIP_HEIGHT,
SET_FILMSTRIP_HEIGHT,
SET_TOP_PANEL_VISIBILITY
CLEAR_STAGE_PARTICIPANTS
} from './actionTypes';
import {
HORIZONTAL_FILMSTRIP_MARGIN,
@@ -37,13 +33,11 @@ import {
SCROLL_SIZE,
STAGE_VIEW_THUMBNAIL_VERTICAL_BORDER,
TILE_HORIZONTAL_MARGIN,
TILE_MIN_HEIGHT_SMALL,
TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN,
TILE_VERTICAL_MARGIN,
TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES,
TILE_VIEW_GRID_HORIZONTAL_MARGIN,
TILE_VIEW_GRID_VERTICAL_MARGIN,
TOP_FILMSTRIP_HEIGHT,
VERTICAL_FILMSTRIP_VERTICAL_MARGIN
} from './constants';
import {
@@ -54,7 +48,6 @@ import {
getNumberOfPartipantsForTileView,
getVerticalViewMaxWidth,
isFilmstripResizable,
isStageFilmstripTopPanel,
showGridInVerticalView
} from './functions';
import { isStageFilmstripAvailable } from './functions.web';
@@ -277,7 +270,7 @@ export function setStageFilmstripViewDimensions() {
const {
tileView = {}
} = state['features/base/config'];
const { visible, topPanelHeight } = state['features/filmstrip'];
const { visible } = state['features/filmstrip'];
const verticalWidth = visible ? getVerticalViewMaxWidth(state) : 0;
const { numberOfVisibleTiles = MAX_ACTIVE_PARTICIPANTS } = tileView;
const numberOfParticipants = state['features/filmstrip'].activeParticipants.length;
@@ -287,7 +280,6 @@ export function setStageFilmstripViewDimensions() {
disableResponsiveTiles: false,
disableTileEnlargement: false
});
const topPanel = isStageFilmstripTopPanel(state);
const {
height,
@@ -296,13 +288,12 @@ export function setStageFilmstripViewDimensions() {
rows
} = calculateResponsiveTileViewDimensions({
clientWidth: availableWidth,
clientHeight: topPanel ? topPanelHeight?.current || TOP_FILMSTRIP_HEIGHT : clientHeight,
clientHeight,
disableTileEnlargement: false,
maxColumns,
noHorizontalContainerMargin: verticalWidth > 0,
numberOfParticipants,
numberOfVisibleTiles,
minTileHeight: topPanel ? TILE_MIN_HEIGHT_SMALL : null
numberOfVisibleTiles
});
const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
const hasScroll = clientHeight < thumbnailsTotalHeight;
@@ -377,22 +368,6 @@ export function setVolume(participantId: string, volume: number) {
};
}
/**
* Sets the top filmstrip's height.
*
* @param {number} height - The new height of the filmstrip.
* @returns {{
* type: SET_FILMSTRIP_HEIGHT,
* height: number
* }}
*/
export function setFilmstripHeight(height: number) {
return {
type: SET_FILMSTRIP_HEIGHT,
height
};
}
/**
* Sets the filmstrip's width.
*
@@ -409,22 +384,6 @@ export function setFilmstripWidth(width: number) {
};
}
/**
* Sets the filmstrip's height and the user preferred height.
*
* @param {number} height - The new height of the filmstrip.
* @returns {{
* type: SET_USER_FILMSTRIP_WIDTH,
* height: number
* }}
*/
export function setUserFilmstripHeight(height: number) {
return {
type: SET_USER_FILMSTRIP_HEIGHT,
height
};
}
/**
* Sets the filmstrip's width and the user preferred width.
*
@@ -531,45 +490,3 @@ export function clearStageParticipants() {
type: CLEAR_STAGE_PARTICIPANTS
};
}
/**
* Set the screensharing tile dimensions.
*
* @returns {Object}
*/
export function setScreensharingTileDimensions() {
return (dispatch: Dispatch<any>, getState: Function) => {
const state = getState();
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
const { visible, topPanelHeight, topPanelVisible } = state['features/filmstrip'];
const verticalWidth = visible ? getVerticalViewMaxWidth(state) : 0;
const availableWidth = clientWidth - verticalWidth;
const topPanel = isStageFilmstripTopPanel(state) && topPanelVisible;
const availableHeight = clientHeight - (topPanel ? topPanelHeight?.current || TOP_FILMSTRIP_HEIGHT : 0);
dispatch({
type: SET_SCREENSHARING_TILE_DIMENSIONS,
dimensions: {
filmstripHeight: availableHeight,
filmstripWidth: availableWidth,
thumbnailSize: {
width: availableWidth - TILE_HORIZONTAL_MARGIN,
height: availableHeight - TILE_VERTICAL_MARGIN
}
}
});
};
}
/**
* Sets the visibility of the top panel.
*
* @param {boolean} visible - Whether it should be visible or not.
* @returns {Object}
*/
export function setTopPanelVisible(visible) {
return {
type: SET_TOP_PANEL_VISIBILITY,
visible
};
}

View File

@@ -23,26 +23,20 @@ import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.we
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
import {
setFilmstripVisible,
setUserFilmstripHeight,
setVisibleRemoteParticipants,
setUserFilmstripWidth,
setUserIsResizing,
setTopPanelVisible,
setVisibleRemoteParticipants
setUserIsResizing
} from '../../actions';
import {
ASPECT_RATIO_BREAKPOINT,
DEFAULT_FILMSTRIP_WIDTH,
FILMSTRIP_TYPE,
MIN_STAGE_VIEW_HEIGHT,
MIN_STAGE_VIEW_WIDTH,
TILE_HORIZONTAL_MARGIN,
TILE_VERTICAL_MARGIN,
TOP_FILMSTRIP_HEIGHT
TILE_VERTICAL_MARGIN
} from '../../constants';
import {
getVerticalViewMaxWidth,
shouldRemoteVideosBeVisible,
isStageFilmstripTopPanel
shouldRemoteVideosBeVisible
} from '../../functions';
import AudioTracksContainer from './AudioTracksContainer';
@@ -118,21 +112,11 @@ type Props = {
*/
_localScreenShare: Object,
/**
* Whether or not the filmstrip videos should currently be displayed.
*/
_mainFilmstripVisible: boolean,
/**
* The maximum width of the vertical filmstrip.
*/
_maxFilmstripWidth: number,
/**
* The maximum height of the top panel.
*/
_maxTopPanelHeight: number,
/**
* The participants in the call.
*/
@@ -153,6 +137,11 @@ type Props = {
*/
_rows: number,
/**
* Whether or not this is the stage filmstrip.
*/
_stageFilmstrip: boolean,
/**
* The height of the thumbnail.
*/
@@ -168,26 +157,6 @@ type Props = {
*/
_thumbnailsReordered: Boolean,
/**
* Whether or not the filmstrip is top panel.
*/
_topPanelFilmstrip: boolean,
/**
* The max height of the top panel.
*/
_topPanelMaxHeight: number,
/**
* The height of the top panel (user resized).
*/
_topPanelHeight: ?number,
/**
* Whether or not the top panel is visible.
*/
_topPanelVisible: boolean,
/**
* The width of the vertical filmstrip (user resized).
*/
@@ -213,6 +182,11 @@ type Props = {
*/
_videosClassName: string,
/**
* Whether or not the filmstrip videos should currently be displayed.
*/
_visible: boolean,
/**
* An object containing the CSS classes.
*/
@@ -223,11 +197,6 @@ type Props = {
*/
dispatch: Dispatch<any>,
/**
* The type of filmstrip to be displayed.
*/
filmstripType: string,
/**
* Invoked to obtain translated strings.
*/
@@ -249,12 +218,7 @@ type State = {
/**
* Initial filmstrip width on drag handle mouse down.
*/
dragFilmstripWidth: ?number,
/**
* Initial top panel height on drag handle mouse down.
*/
dragFilmstripHeight: ?number
dragFilmstripWidth: ?number
}
/**
@@ -343,45 +307,25 @@ class Filmstrip extends PureComponent <Props, State> {
_currentLayout,
_disableSelfView,
_localScreenShare,
_mainFilmstripVisible,
_resizableFilmstrip,
_topPanelFilmstrip,
_topPanelMaxHeight,
_topPanelVisible,
_stageFilmstrip,
_visible,
_verticalViewBackground,
_verticalViewGrid,
_verticalViewMaxWidth,
classes,
filmstripType
classes
} = this.props;
const { isMouseDown } = this.state;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.STAGE) {
if (_topPanelFilmstrip) {
filmstripStyle.maxHeight = `${_topPanelMaxHeight}px`;
filmstripStyle.zIndex = 1;
if (!_topPanelVisible) {
filmstripStyle.top = `-${_topPanelMaxHeight}px`;
}
}
if (_mainFilmstripVisible) {
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && _stageFilmstrip) {
if (_visible) {
filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
}
} else if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
if (_mainFilmstripVisible) {
filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
}
if (_topPanelVisible) {
filmstripStyle.maxHeight = `calc(100% - ${_topPanelMaxHeight}px)`;
}
filmstripStyle.bottom = 0;
filmstripStyle.top = 'auto';
} else if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|| (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.MAIN)) {
|| (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && !_stageFilmstrip)) {
filmstripStyle.maxWidth = _verticalViewMaxWidth;
if (!_mainFilmstripVisible) {
if (!_visible) {
filmstripStyle.right = `-${filmstripStyle.maxWidth}px`;
}
}
@@ -389,17 +333,14 @@ class Filmstrip extends PureComponent <Props, State> {
let toolbar = null;
if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled
&& _currentLayout !== LAYOUTS.TILE_VIEW && (filmstripType === FILMSTRIP_TYPE.MAIN
|| (filmstripType === FILMSTRIP_TYPE.STAGE && _topPanelFilmstrip))) {
&& _currentLayout !== LAYOUTS.TILE_VIEW && !_stageFilmstrip) {
toolbar = this._renderToggleButton();
}
const filmstrip = (<>
<div
className = { clsx(this.props._videosClassName,
!tileViewActive && (filmstripType === FILMSTRIP_TYPE.MAIN
|| (filmstripType === FILMSTRIP_TYPE.STAGE && _topPanelFilmstrip))
&& !_resizableFilmstrip && 'filmstrip-hover',
!tileViewActive && !_stageFilmstrip && !_resizableFilmstrip && 'filmstrip-hover',
_verticalViewGrid && 'vertical-view-grid') }
id = 'remoteVideos'>
{!_disableSelfView && !_verticalViewGrid && (
@@ -407,10 +348,8 @@ class Filmstrip extends PureComponent <Props, State> {
className = 'filmstrip__videos'
id = 'filmstripLocalVideo'>
{
!tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN
&& <div id = 'filmstripLocalVideoThumbnail'>
!tileViewActive && !_stageFilmstrip && <div id = 'filmstripLocalVideoThumbnail'>
<Thumbnail
filmstripType = { FILMSTRIP_TYPE.MAIN }
key = 'local' />
</div>
}
@@ -422,9 +361,10 @@ class Filmstrip extends PureComponent <Props, State> {
id = 'filmstripLocalScreenShare'>
<div id = 'filmstripLocalScreenShareThumbnail'>
{
!tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN && <Thumbnail
!tileViewActive && !_stageFilmstrip && <Thumbnail
key = 'localScreenShare'
participantID = { _localScreenShare.id } />
}
</div>
</div>
@@ -445,14 +385,11 @@ class Filmstrip extends PureComponent <Props, State> {
style = { filmstripStyle }>
{ toolbar }
{_resizableFilmstrip
? <div
className = { clsx('resizable-filmstrip', classes.resizableFilmstripContainer,
_topPanelFilmstrip && 'top-panel-filmstrip') }>
? <div className = { clsx('resizable-filmstrip', classes.resizableFilmstripContainer) }>
<div
className = { clsx('dragHandleContainer',
classes.dragHandleContainer,
isMouseDown && 'visible',
_topPanelFilmstrip && 'top-panel')
isMouseDown && 'visible')
}
onMouseDown = { this._onDragHandleMouseDown }>
<div className = { clsx(classes.dragHandle, 'dragHandle') } />
@@ -475,13 +412,10 @@ class Filmstrip extends PureComponent <Props, State> {
* @returns {void}
*/
_onDragHandleMouseDown(e) {
const { _topPanelFilmstrip, _topPanelHeight, _verticalFilmstripWidth } = this.props;
this.setState({
isMouseDown: true,
mousePosition: _topPanelFilmstrip ? e.clientY : e.clientX,
dragFilmstripWidth: _verticalFilmstripWidth || DEFAULT_FILMSTRIP_WIDTH,
dragFilmstripHeight: _topPanelHeight || TOP_FILMSTRIP_HEIGHT
mousePosition: e.clientX,
dragFilmstripWidth: this.props._verticalFilmstripWidth || DEFAULT_FILMSTRIP_WIDTH
});
this.props.dispatch(setUserIsResizing(true));
}
@@ -512,36 +446,16 @@ class Filmstrip extends PureComponent <Props, State> {
*/
_onFilmstripResize(e) {
if (this.state.isMouseDown) {
const {
dispatch,
_verticalFilmstripWidth,
_maxFilmstripWidth,
_topPanelHeight,
_maxTopPanelHeight,
_topPanelFilmstrip
} = this.props;
const { dragFilmstripWidth, dragFilmstripHeight, mousePosition } = this.state;
const { dispatch, _verticalFilmstripWidth, _maxFilmstripWidth } = this.props;
const { dragFilmstripWidth, mousePosition } = this.state;
const diff = mousePosition - e.clientX;
const width = Math.max(
Math.min(dragFilmstripWidth + diff, _maxFilmstripWidth),
DEFAULT_FILMSTRIP_WIDTH
);
if (_topPanelFilmstrip) {
const diff = e.clientY - mousePosition;
const height = Math.max(
Math.min(dragFilmstripHeight + diff, _maxTopPanelHeight),
TOP_FILMSTRIP_HEIGHT
);
if (height !== _topPanelHeight) {
dispatch(setUserFilmstripHeight(height));
}
} else {
const diff = mousePosition - e.clientX;
const width = Math.max(
Math.min(dragFilmstripWidth + diff, _maxFilmstripWidth),
DEFAULT_FILMSTRIP_WIDTH
);
if (width !== _verticalFilmstripWidth) {
dispatch(setUserFilmstripWidth(width));
}
if (width !== _verticalFilmstripWidth) {
dispatch(setUserFilmstripWidth(width));
}
}
}
@@ -581,7 +495,7 @@ class Filmstrip extends PureComponent <Props, State> {
* @returns {void}
*/
_onTabIn() {
if (!this.props._isToolboxVisible && this.props._mainFilmstripVisible) {
if (!this.props._isToolboxVisible && this.props._visible) {
this.props.dispatch(showToolbox());
}
}
@@ -691,10 +605,10 @@ class Filmstrip extends PureComponent <Props, State> {
_remoteParticipantsLength,
_resizableFilmstrip,
_rows,
_stageFilmstrip,
_thumbnailHeight,
_thumbnailWidth,
_verticalViewGrid,
filmstripType
_verticalViewGrid
} = this.props;
if (!_thumbnailWidth || isNaN(_thumbnailWidth) || !_thumbnailHeight
@@ -703,7 +617,7 @@ class Filmstrip extends PureComponent <Props, State> {
return null;
}
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || filmstripType !== FILMSTRIP_TYPE.MAIN) {
if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || _stageFilmstrip) {
return (
<FixedSizeGrid
className = 'filmstrip__videos remote-videos'
@@ -712,7 +626,7 @@ class Filmstrip extends PureComponent <Props, State> {
height = { _filmstripHeight }
initialScrollLeft = { 0 }
initialScrollTop = { 0 }
itemData = {{ filmstripType }}
itemData = {{ stageFilmstrip: _stageFilmstrip }}
itemKey = { this._gridItemKey }
onItemsRendered = { this._onGridItemsRendered }
overscanRowCount = { 1 }
@@ -780,11 +694,7 @@ class Filmstrip extends PureComponent <Props, State> {
* @returns {void}
*/
_doToggleFilmstrip() {
const { dispatch, _mainFilmstripVisible, _topPanelFilmstrip, _topPanelVisible } = this.props;
_topPanelFilmstrip
? dispatch(setTopPanelVisible(!_topPanelVisible))
: dispatch(setFilmstripVisible(!_mainFilmstripVisible));
this.props.dispatch(setFilmstripVisible(!this.props._visible));
}
_onShortcutToggleFilmstrip: () => void;
@@ -800,7 +710,7 @@ class Filmstrip extends PureComponent <Props, State> {
sendAnalytics(createShortcutEvent(
'toggle.filmstrip',
{
enable: this.props._mainFilmstripVisible
enable: this.props._visible
}));
this._doToggleFilmstrip();
@@ -819,7 +729,7 @@ class Filmstrip extends PureComponent <Props, State> {
sendAnalytics(createToolbarEvent(
'toggle.filmstrip.button',
{
enable: this.props._mainFilmstripVisible
enable: this.props._visible
}));
this._doToggleFilmstrip();
@@ -848,15 +758,8 @@ class Filmstrip extends PureComponent <Props, State> {
* @returns {ReactElement}
*/
_renderToggleButton() {
const {
t,
classes,
_isVerticalFilmstrip,
_mainFilmstripVisible,
_topPanelFilmstrip,
_topPanelVisible
} = this.props;
const icon = (_topPanelFilmstrip ? _topPanelVisible : _mainFilmstripVisible) ? IconMenuDown : IconMenuUp;
const icon = this.props._visible ? IconMenuDown : IconMenuUp;
const { t, classes, _isVerticalFilmstrip } = this.props;
const actions = isMobileBrowser()
? { onTouchStart: this._onToggleButtonTouch }
: { onClick: this._onToolbarToggleFilmstrip };
@@ -865,11 +768,9 @@ class Filmstrip extends PureComponent <Props, State> {
<div
className = { clsx(classes.toggleFilmstripContainer,
_isVerticalFilmstrip && classes.toggleVerticalFilmstripContainer,
_topPanelFilmstrip && classes.toggleTopPanelContainer,
_topPanelFilmstrip && !_topPanelVisible && classes.toggleTopPanelContainerHidden,
'toggleFilmstripContainer') }>
<button
aria-expanded = { this.props._mainFilmstripVisible }
aria-expanded = { this.props._visible }
aria-label = { t('toolbar.accessibilityLabel.toggleFilmstrip') }
className = { classes.toggleFilmstripButton }
id = 'toggleFilmstripButton'
@@ -894,38 +795,32 @@ class Filmstrip extends PureComponent <Props, State> {
* @returns {Props}
*/
function _mapStateToProps(state, ownProps) {
const { _hasScroll = false, filmstripType, _topPanelFilmstrip, _remoteParticipants } = ownProps;
const { _hasScroll = false } = ownProps;
const toolbarButtons = getToolbarButtons(state);
const { testing = {}, iAmRecorder } = state['features/base/config'];
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
const { topPanelHeight, topPanelVisible, visible, width: verticalFilmstripWidth } = state['features/filmstrip'];
const { visible, width: verticalFilmstripWidth } = state['features/filmstrip'];
const { localScreenShare } = state['features/base/participants'];
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
const remoteVideosVisible = shouldRemoteVideosBeVisible(state);
const { isOpen: shiftRight } = state['features/chat'];
const disableSelfView = shouldHideSelfView(state);
const { clientWidth, clientHeight } = state['features/base/responsive-ui'];
const { clientWidth } = state['features/base/responsive-ui'];
const collapseTileView = reduceHeight
&& isMobileBrowser()
&& clientWidth <= ASPECT_RATIO_BREAKPOINT;
const shouldReduceHeight = reduceHeight && isMobileBrowser();
const _topPanelVisible = isStageFilmstripTopPanel(state) && topPanelVisible;
let isVisible = visible || filmstripType !== FILMSTRIP_TYPE.MAIN;
if (_topPanelFilmstrip) {
isVisible = _topPanelVisible;
}
const videosClassName = `filmstrip__videos${isVisible ? '' : ' hidden'}${_hasScroll ? ' has-scroll' : ''}`;
const videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}${_hasScroll ? ' has-scroll' : ''}`;
const className = `${remoteVideosVisible || ownProps._verticalViewGrid ? '' : 'hide-videos'} ${
shouldReduceHeight ? 'reduce-height' : ''
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${isVisible ? '' : 'hidden'}`.trim();
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${visible ? '' : 'hidden'}`.trim();
const _currentLayout = getCurrentLayout(state);
const _isVerticalFilmstrip = _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
|| (filmstripType === FILMSTRIP_TYPE.MAIN && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
|| (!ownProps._stageFilmstrip && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
return {
_className: className,
@@ -938,14 +833,8 @@ function _mapStateToProps(state, ownProps) {
_isToolboxVisible: isToolboxVisible(state),
_isVerticalFilmstrip,
_localScreenShare: getSourceNameSignalingFeatureFlag(state) && localScreenShare,
_mainFilmstripVisible: visible,
_maxFilmstripWidth: clientWidth - MIN_STAGE_VIEW_WIDTH,
_maxTopPanelHeight: clientHeight - MIN_STAGE_VIEW_HEIGHT,
_remoteParticipantsLength: _remoteParticipants.length,
_thumbnailsReordered: enableThumbnailReordering,
_topPanelHeight: topPanelHeight.current,
_topPanelMaxHeight: topPanelHeight.current || TOP_FILMSTRIP_HEIGHT,
_topPanelVisible,
_verticalFilmstripWidth: verticalFilmstripWidth.current,
_verticalViewMaxWidth: getVerticalViewMaxWidth(state),
_videosClassName: videosClassName

View File

@@ -9,7 +9,6 @@ import {
ASPECT_RATIO_BREAKPOINT,
FILMSTRIP_BREAKPOINT,
FILMSTRIP_BREAKPOINT_OFFSET,
FILMSTRIP_TYPE,
TOOLBAR_HEIGHT,
TOOLBAR_HEIGHT_MOBILE } from '../../constants';
import { isFilmstripResizable, showGridInVerticalView } from '../../functions.web';
@@ -86,16 +85,15 @@ type Props = {
/**
* Additional CSS class names to add to the container of all the thumbnails.
*/
_videosClassName: string
_videosClassName: string,
/**
* Whether or not the filmstrip videos should currently be displayed.
*/
_visible: boolean
};
const MainFilmstrip = (props: Props) => (
<span>
<Filmstrip
{ ...props }
filmstripType = { FILMSTRIP_TYPE.MAIN } />
</span>
);
const MainFilmstrip = (props: Props) => <span><Filmstrip { ...props } /></span>;
/**
* Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props.
@@ -106,7 +104,7 @@ const MainFilmstrip = (props: Props) => (
*/
function _mapStateToProps(state) {
const toolbarButtons = getToolbarButtons(state);
const { remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip'];
const { visible, remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip'];
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
const {
gridDimensions: dimensions = {},
@@ -191,11 +189,13 @@ function _mapStateToProps(state) {
_filmstripHeight: remoteFilmstripHeight,
_filmstripWidth: remoteFilmstripWidth,
_hasScroll,
_remoteParticipantsLength: remoteParticipants.length,
_remoteParticipants: remoteParticipants,
_resizableFilmstrip,
_rows: gridDimensions.rows,
_thumbnailWidth: _thumbnailSize?.width,
_thumbnailHeight: _thumbnailSize?.height,
_visible: visible,
_verticalViewGrid,
_verticalViewBackground: verticalFilmstripWidth.current + FILMSTRIP_BREAKPOINT_OFFSET >= FILMSTRIP_BREAKPOINT
};

View File

@@ -1,125 +0,0 @@
// @flow
import React from 'react';
import { connect } from '../../../base/redux';
import { LAYOUT_CLASSNAMES } from '../../../conference/components/web/Conference';
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
import {
FILMSTRIP_TYPE
} from '../../constants';
import Filmstrip from './Filmstrip';
type Props = {
/**
* The current layout of the filmstrip.
*/
_currentLayout: string,
/**
* The number of columns in tile view.
*/
_columns: number,
/**
* The width of the filmstrip.
*/
_filmstripWidth: number,
/**
* The height of the filmstrip.
*/
_filmstripHeight: number,
/**
* Whether or not the current layout is vertical filmstrip.
*/
_isVerticalFilmstrip: boolean,
/**
* The participants in the call.
*/
_remoteParticipants: Array<Object>,
/**
* The length of the remote participants array.
*/
_remoteParticipantsLength: number,
/**
* Whether or not the filmstrip should be user-resizable.
*/
_resizableFilmstrip: boolean,
/**
* The number of rows in tile view.
*/
_rows: number,
/**
* The height of the thumbnail.
*/
_thumbnailHeight: number,
/**
* The width of the thumbnail.
*/
_thumbnailWidth: number,
/**
* Whether or not the vertical filmstrip should have a background color.
*/
_verticalViewBackground: boolean,
/**
* Whether or not the vertical filmstrip should be displayed as grid.
*/
_verticalViewGrid: boolean,
/**
* Additional CSS class names to add to the container of all the thumbnails.
*/
_videosClassName: string
};
const ScreenshareFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW
&& props._remoteParticipants.length === 1 && (
<span className = { LAYOUT_CLASSNAMES[LAYOUTS.TILE_VIEW] }>
<Filmstrip
{ ...props }
filmstripType = { FILMSTRIP_TYPE.SCREENSHARE } />
</span>
);
/**
* Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {Props}
*/
function _mapStateToProps(state) {
const {
filmstripHeight,
filmstripWidth,
thumbnailSize
} = state['features/filmstrip'].screenshareFilmstripDimensions;
const screenshares = state['features/video-layout'].remoteScreenShares;
return {
_columns: 1,
_currentLayout: getCurrentLayout(state),
_filmstripHeight: filmstripHeight,
_filmstripWidth: filmstripWidth,
_remoteParticipants: screenshares.length ? [ screenshares[0] ] : [],
_resizableFilmstrip: false,
_rows: 1,
_thumbnailWidth: thumbnailSize?.width,
_thumbnailHeight: thumbnailSize?.height,
_verticalViewGrid: false,
_verticalViewBackground: false
};
}
export default connect(_mapStateToProps)(ScreenshareFilmstrip);

View File

@@ -8,11 +8,9 @@ import { LAYOUT_CLASSNAMES } from '../../../conference/components/web/Conference
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
import {
ASPECT_RATIO_BREAKPOINT,
FILMSTRIP_TYPE,
TOOLBAR_HEIGHT_MOBILE
} from '../../constants';
import { getActiveParticipantsIds } from '../../functions';
import { isFilmstripResizable, isStageFilmstripTopPanel } from '../../functions.web';
import Filmstrip from './Filmstrip';
@@ -86,14 +84,19 @@ type Props = {
/**
* Additional CSS class names to add to the container of all the thumbnails.
*/
_videosClassName: string
_videosClassName: string,
/**
* Whether or not the filmstrip videos should currently be displayed.
*/
_visible: boolean
};
const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && (
<span className = { LAYOUT_CLASSNAMES[LAYOUTS.TILE_VIEW] }>
<Filmstrip
{ ...props }
filmstripType = { FILMSTRIP_TYPE.STAGE } />
_stageFilmstrip = { true } />
</span>
);
@@ -106,6 +109,7 @@ const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_
*/
function _mapStateToProps(state) {
const toolbarButtons = getToolbarButtons(state);
const { visible } = state['features/filmstrip'];
const activeParticipants = getActiveParticipantsIds(state);
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
const {
@@ -135,19 +139,19 @@ function _mapStateToProps(state) {
&& clientWidth <= ASPECT_RATIO_BREAKPOINT;
const remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0);
const _topPanelFilmstrip = isStageFilmstripTopPanel(state);
return {
_columns: gridDimensions.columns,
_currentLayout: getCurrentLayout(state),
_filmstripHeight: remoteFilmstripHeight,
_filmstripWidth: filmstripWidth,
_remoteParticipantsLength: activeParticipants.length,
_remoteParticipants: activeParticipants,
_resizableFilmstrip: isFilmstripResizable(state) && _topPanelFilmstrip,
_resizableFilmstrip: false,
_rows: gridDimensions.rows,
_thumbnailWidth: thumbnailSize?.width,
_thumbnailHeight: thumbnailSize?.height,
_topPanelFilmstrip,
_visible: visible,
_verticalViewGrid: false,
_verticalViewBackground: false
};

View File

@@ -37,7 +37,6 @@ import { togglePinStageParticipant } from '../../actions';
import {
DISPLAY_MODE_TO_CLASS_NAME,
DISPLAY_VIDEO,
FILMSTRIP_TYPE,
SHOW_TOOLBAR_CONTEXT_MENU_AFTER,
THUMBNAIL_TYPE,
VIDEO_TEST_EVENTS
@@ -235,11 +234,6 @@ export type Props = {|
*/
dispatch: Function,
/**
* The type of filmstrip the tile is displayed in.
*/
filmstripType: string,
/**
* The horizontal offset in px for the thumbnail. Used to center the thumbnails from the last row in tile view.
*/
@@ -250,6 +244,11 @@ export type Props = {|
*/
participantID: ?string,
/**
* Whether the tile is displayed in the stage filmstrip or not.
*/
stageFilmstrip: boolean,
/**
* Styles that will be set to the Thumbnail's main span element.
*/
@@ -994,7 +993,7 @@ class Thumbnail extends Component<Props, State> {
_thumbnailType,
_videoTrack,
classes,
filmstripType
stageFilmstrip
} = this.props;
const { id } = _participant || {};
const { isHovered, popoverVisible } = this.state;
@@ -1032,8 +1031,8 @@ class Thumbnail extends Component<Props, State> {
<span
className = { containerClassName }
id = { local
? `localVideoContainer${filmstripType === FILMSTRIP_TYPE.MAIN ? '' : `_${filmstripType}`}`
: `participant_${id}${filmstripType === FILMSTRIP_TYPE.MAIN ? '' : `_${filmstripType}`}`
? `localVideoContainer${stageFilmstrip ? '_stage' : ''}`
: `participant_${id}${stageFilmstrip ? '_stage' : ''}`
}
{ ...(_isMobile
? {
@@ -1169,7 +1168,7 @@ class Thumbnail extends Component<Props, State> {
* @returns {Props}
*/
function _mapStateToProps(state, ownProps): Object {
const { participantID, filmstripType = FILMSTRIP_TYPE.MAIN } = ownProps;
const { participantID, stageFilmstrip } = ownProps;
const participant = getParticipantByIdOrUndefined(state, participantID);
const id = participant?.id;
@@ -1200,7 +1199,7 @@ function _mapStateToProps(state, ownProps): Object {
const { localFlipX } = state['features/base/settings'];
const _isMobile = isMobileBrowser();
const activeParticipants = getActiveParticipantsIds(state);
const tileType = getThumbnailTypeFromLayout(_currentLayout, filmstripType);
const tileType = getThumbnailTypeFromLayout(_currentLayout, stageFilmstrip);
switch (tileType) {
case THUMBNAIL_TYPE.VERTICAL:
@@ -1245,8 +1244,7 @@ function _mapStateToProps(state, ownProps): Object {
const {
stageFilmstripDimensions = {
thumbnailSize: {}
},
screenshareFilmstripDimensions
}
} = state['features/filmstrip'];
size = {
@@ -1254,16 +1252,9 @@ function _mapStateToProps(state, ownProps): Object {
_height: thumbnailSize?.height
};
if (filmstripType === FILMSTRIP_TYPE.STAGE) {
if (stageFilmstrip) {
const { width: _width, height: _height } = stageFilmstripDimensions.thumbnailSize;
size = {
_width,
_height
};
} else if (filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
const { width: _width, height: _height } = screenshareFilmstripDimensions.thumbnailSize;
size = {
_width,
_height

View File

@@ -7,7 +7,7 @@ import { getLocalParticipant } from '../../../base/participants';
import { connect } from '../../../base/redux';
import { shouldHideSelfView } from '../../../base/settings/functions.any';
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
import { TILE_ASPECT_RATIO, TILE_HORIZONTAL_MARGIN, FILMSTRIP_TYPE } from '../../constants';
import { TILE_ASPECT_RATIO, TILE_HORIZONTAL_MARGIN } from '../../constants';
import { showGridInVerticalView, getActiveParticipantsIds } from '../../functions';
import Thumbnail from './Thumbnail';
@@ -22,11 +22,6 @@ type Props = {
*/
_disableSelfView: boolean,
/**
* The type of filmstrip this thumbnail is displayed in.
*/
_filmstripType: string,
/**
* The horizontal offset in px for the thumbnail. Used to center the thumbnails in the last row in tile view.
*/
@@ -42,6 +37,11 @@ type Props = {
*/
_isLocalScreenShare: boolean,
/**
* Whether or not the filmstrip is used a stage filmstrip.
*/
_stageFilmstrip: boolean,
/**
* The width of the thumbnail. Used for expanding the width of the thumbnails on last row in case
* there is empty space.
@@ -97,10 +97,10 @@ class ThumbnailWrapper extends Component<Props> {
render() {
const {
_disableSelfView,
_filmstripType = FILMSTRIP_TYPE.MAIN,
_isLocalScreenShare = false,
_horizontalOffset = 0,
_participantID,
_stageFilmstrip,
_thumbnailWidth,
style
} = this.props;
@@ -112,9 +112,9 @@ class ThumbnailWrapper extends Component<Props> {
if (_participantID === 'local') {
return _disableSelfView ? null : (
<Thumbnail
filmstripType = { _filmstripType }
horizontalOffset = { _horizontalOffset }
key = 'local'
stageFilmstrip = { _stageFilmstrip }
style = { style }
width = { _thumbnailWidth } />);
}
@@ -122,20 +122,20 @@ class ThumbnailWrapper extends Component<Props> {
if (_isLocalScreenShare) {
return _disableSelfView ? null : (
<Thumbnail
filmstripType = { _filmstripType }
horizontalOffset = { _horizontalOffset }
key = 'localScreenShare'
participantID = { _participantID }
stageFilmstrip = { _stageFilmstrip }
style = { style }
width = { _thumbnailWidth } />);
}
return (
<Thumbnail
filmstripType = { _filmstripType }
horizontalOffset = { _horizontalOffset }
key = { `remote_${_participantID}` }
participantID = { _participantID }
stageFilmstrip = { _stageFilmstrip }
style = { style }
width = { _thumbnailWidth } />);
}
@@ -158,8 +158,7 @@ function _mapStateToProps(state, ownProps) {
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
const sourceNameSignalingEnabled = getSourceNameSignalingFeatureFlag(state);
const _verticalViewGrid = showGridInVerticalView(state);
const filmstripType = ownProps.data?.filmstripType;
const stageFilmstrip = filmstripType === FILMSTRIP_TYPE.STAGE;
const stageFilmstrip = ownProps.data?.stageFilmstrip;
const sortedActiveParticipants = activeParticipants.sort();
const remoteParticipants = stageFilmstrip ? sortedActiveParticipants : remote;
const remoteParticipantsLength = remoteParticipants.length;
@@ -236,9 +235,9 @@ function _mapStateToProps(state, ownProps) {
if (stageFilmstrip) {
return {
_disableSelfView: disableSelfView,
_filmstripType: filmstripType,
_participantID: remoteParticipants[index] === localId ? 'local' : remoteParticipants[index],
_horizontalOffset: horizontalOffset,
_stageFilmstrip: stageFilmstrip,
_thumbnailWidth: thumbnailWidth
};
}
@@ -261,7 +260,6 @@ function _mapStateToProps(state, ownProps) {
if (!iAmRecorder && index === localIndex) {
return {
_disableSelfView: disableSelfView,
_filmstripType: filmstripType,
_participantID: 'local',
_horizontalOffset: horizontalOffset,
_thumbnailWidth: thumbnailWidth
@@ -271,7 +269,6 @@ function _mapStateToProps(state, ownProps) {
if (sourceNameSignalingEnabled && !iAmRecorder && localScreenShare && index === localScreenShareIndex) {
return {
_disableSelfView: disableSelfView,
_filmstripType: filmstripType,
_isLocalScreenShare: true,
_participantID: localScreenShare?.id,
_horizontalOffset: horizontalOffset,
@@ -280,22 +277,12 @@ function _mapStateToProps(state, ownProps) {
}
return {
_filmstripType: filmstripType,
_participantID: remoteParticipants[remoteIndex],
_horizontalOffset: horizontalOffset,
_thumbnailWidth: thumbnailWidth
};
}
if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
const { remoteScreenShares } = state['features/video-layout'];
return {
_filmstripType: filmstripType,
_participantID: remoteScreenShares[remoteScreenShares.length - 1]
};
}
const { index } = ownProps;
if (typeof index !== 'number' || remoteParticipantsLength <= index) {

View File

@@ -5,7 +5,6 @@ export { default as Filmstrip } from './Filmstrip';
export { default as MainFilmstrip } from './MainFilmstrip';
export { default as ModeratorIndicator } from './ModeratorIndicator';
export { default as RaisedHandIndicator } from './RaisedHandIndicator';
export { default as ScreenshareFilmstrip } from './ScreenshareFilmstrip';
export { default as StageFilmstrip } from './StageFilmstrip';
export { default as StatusIndicators } from './StatusIndicators';
export { default as Thumbnail } from './Thumbnail';

View File

@@ -23,7 +23,6 @@ export const styles = theme => {
left: 'calc(50% - 16px)',
opacity: 0,
transition: 'opacity .3s',
zIndex: 1,
'&:hover': {
backgroundColor: theme.palette.ui02
@@ -54,18 +53,8 @@ export const styles = theme => {
top: 'calc(50% - 12px)'
},
toggleTopPanelContainer: {
transform: 'rotate(180deg)',
bottom: 'calc(-24px - 6px)',
top: 'auto'
},
toggleTopPanelContainerHidden: {
visibility: 'hidden'
},
filmstrip: {
transition: 'background .2s ease-in-out, right 1s, bottom 1s, top 1s, height .3s ease-in',
transition: 'background .2s ease-in-out, right 1s, bottom 1s, height .3s ease-in',
right: 0,
bottom: 0,
@@ -122,10 +111,6 @@ export const styles = theme => {
'& .avatar-container': {
maxWidth: 'initial',
maxHeight: 'initial'
},
'&.top-panel-filmstrip': {
flexDirection: 'column'
}
},
@@ -152,18 +137,6 @@ export const styles = theme => {
'& .dragHandle': {
backgroundColor: theme.palette.icon01
}
},
'&.top-panel': {
order: 2,
width: '100%',
height: '9px',
cursor: 'row-resize',
'& .dragHandle': {
height: '3px',
width: '100px'
}
}
},

View File

@@ -281,12 +281,6 @@ export const FILMSTRIP_GRID_BREAKPOINT = 300;
*/
export const FILMSTRIP_BREAKPOINT_OFFSET = 5;
/**
* The minimum height for the stage view
* (used to determine the maximum height of the user-resizable top panel).
*/
export const MIN_STAGE_VIEW_HEIGHT = 700;
/**
* The minimum width for the stage view
* (used to determine the maximum width of the user-resizable vertical filmstrip).
@@ -304,21 +298,7 @@ export const VERTICAL_VIEW_HORIZONTAL_MARGIN = VERTICAL_FILMSTRIP_MIN_HORIZONTAL
*/
export const ACTIVE_PARTICIPANT_TIMEOUT = 1000 * 60;
/**
* The types of filmstrip.
*/
export const FILMSTRIP_TYPE = {
MAIN: 'main',
STAGE: 'stage',
SCREENSHARE: 'screenshare'
};
/**
* The max number of participants to be displayed on the stage filmstrip.
*/
export const MAX_ACTIVE_PARTICIPANTS = 6;
/**
* Top filmstrip default height.
*/
export const TOP_FILMSTRIP_HEIGHT = 180;

View File

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

View File

@@ -6,7 +6,6 @@ import { MEDIA_TYPE } from '../base/media';
import {
getLocalParticipant,
getParticipantById,
getParticipantCount,
getParticipantCountWithFake,
getPinnedParticipant
} from '../base/participants';
@@ -33,7 +32,6 @@ import {
DISPLAY_AVATAR,
DISPLAY_VIDEO,
FILMSTRIP_GRID_BREAKPOINT,
FILMSTRIP_TYPE,
INDICATORS_TOOLTIP_POSITION,
SCROLL_SIZE,
SQUARE_TILE_ASPECT_RATIO,
@@ -298,8 +296,7 @@ export function calculateResponsiveTileViewDimensions({
noHorizontalContainerMargin = false,
maxColumns,
numberOfParticipants,
desiredNumberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES,
minTileHeight
desiredNumberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES
}) {
let height, width;
let columns, rows;
@@ -327,8 +324,7 @@ export function calculateResponsiveTileViewDimensions({
clientHeight,
disableTileEnlargement,
disableResponsiveTiles: false,
noHorizontalContainerMargin,
minTileHeight
noHorizontalContainerMargin
});
if (size) {
@@ -417,11 +413,10 @@ export function calculateThumbnailSizeForTileView({
clientHeight,
disableResponsiveTiles = false,
disableTileEnlargement = false,
noHorizontalContainerMargin = false,
minTileHeight
noHorizontalContainerMargin = false
}: Object) {
const aspectRatio = getTileDefaultAspectRatio(disableResponsiveTiles, disableTileEnlargement, clientWidth);
const minHeight = minTileHeight || getThumbnailMinHeight(clientWidth);
const minHeight = getThumbnailMinHeight(clientWidth);
const viewWidth = clientWidth - (columns * TILE_HORIZONTAL_MARGIN)
- (noHorizontalContainerMargin ? SCROLL_SIZE : TILE_VIEW_GRID_HORIZONTAL_MARGIN);
const availableHeight = clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN;
@@ -511,7 +506,6 @@ export function getVerticalFilmstripVisibleAreaWidth() {
*/
export function computeDisplayModeFromInput(input: Object) {
const {
filmstripType,
isActiveParticipant,
isAudioOnly,
isCurrentlyOnLargeVideo,
@@ -521,6 +515,7 @@ export function computeDisplayModeFromInput(input: Object) {
isRemoteParticipant,
multipleVideoSupport,
stageParticipantsVisible,
stageFilmstrip,
tileViewActive
} = input;
const adjustedIsVideoPlayable = input.isVideoPlayable && (!isRemoteParticipant || canPlayEventReceived);
@@ -539,8 +534,8 @@ export function computeDisplayModeFromInput(input: Object) {
}
}
if (!tileViewActive && filmstripType === FILMSTRIP_TYPE.MAIN && ((isScreenSharing && isRemoteParticipant)
|| (stageParticipantsVisible && isActiveParticipant))) {
if (!tileViewActive && ((isScreenSharing && isRemoteParticipant)
|| (stageParticipantsVisible && isActiveParticipant && !stageFilmstrip))) {
return DISPLAY_AVATAR;
} else if (isCurrentlyOnLargeVideo && !tileViewActive) {
// Display name is always and only displayed when user is on the stage
@@ -574,13 +569,12 @@ export function getDisplayModeInput(props: Object, state: Object) {
_participant,
_stageParticipantsVisible,
_videoTrack,
filmstripType = FILMSTRIP_TYPE.MAIN
stageFilmstrip
} = props;
const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
const { canPlayEventReceived } = state;
return {
filmstripType,
isActiveParticipant: _isActiveParticipant,
isCurrentlyOnLargeVideo: _isCurrentlyOnLargeVideo,
isAudioOnly: _isAudioOnly,
@@ -594,6 +588,7 @@ export function getDisplayModeInput(props: Object, state: Object) {
isVirtualScreenshareParticipant: _isVirtualScreenshareParticipant,
multipleVideoSupport: _multipleVideoSupport,
stageParticipantsVisible: _stageParticipantsVisible,
stageFilmstrip,
videoStreamMuted: _videoTrack ? _videoTrack.muted : 'no stream'
};
}
@@ -722,24 +717,8 @@ export function isStageFilmstripAvailable(state, minParticipantCount = 0) {
const { remoteScreenShares } = state['features/video-layout'];
const sharedVideo = isSharingStatus(state['features/shared-video']?.status);
return isStageFilmstripEnabled(state) && !sharedVideo
&& activeParticipants.length >= minParticipantCount
&& (isTopPanelEnabled(state) || remoteScreenShares.length === 0);
}
/**
* Whether the stage filmstrip should be displayed on the top.
*
* @param {Object} state - Redux state.
* @param {number} minParticipantCount - The min number of participants for the stage filmstrip
* to be displayed.
* @returns {boolean}
*/
export function isStageFilmstripTopPanel(state, minParticipantCount = 0) {
const { remoteScreenShares } = state['features/video-layout'];
return isTopPanelEnabled(state)
&& isStageFilmstripAvailable(state, minParticipantCount) && remoteScreenShares.length > 0;
return isStageFilmstripEnabled(state) && remoteScreenShares.length === 0 && !sharedVideo
&& activeParticipants.length >= minParticipantCount;
}
/**
@@ -758,10 +737,10 @@ export function isStageFilmstripEnabled(state) {
* Gets the thumbnail type by filmstrip type.
*
* @param {string} currentLayout - Current app layout.
* @param {string} filmstripType - The current filmstrip type.
* @param {boolean} isStageFilmstrip - Whether the filmstrip is stage filmstrip or not.
* @returns {string}
*/
export function getThumbnailTypeFromLayout(currentLayout, filmstripType) {
export function getThumbnailTypeFromLayout(currentLayout, isStageFilmstrip = false) {
switch (currentLayout) {
case LAYOUTS.TILE_VIEW:
return THUMBNAIL_TYPE.TILE;
@@ -770,24 +749,10 @@ export function getThumbnailTypeFromLayout(currentLayout, filmstripType) {
case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
return THUMBNAIL_TYPE.HORIZONTAL;
case LAYOUTS.STAGE_FILMSTRIP_VIEW:
if (filmstripType !== FILMSTRIP_TYPE.MAIN) {
if (isStageFilmstrip) {
return THUMBNAIL_TYPE.TILE;
}
return THUMBNAIL_TYPE.VERTICAL;
}
}
/**
* Whether or not the top panel is enabled.
*
* @param {Object} state - Redux state.
* @returns {boolean}
*/
export function isTopPanelEnabled(state) {
const { filmstrip } = state['features/base/config'];
const participantsCount = getParticipantCount(state);
return !filmstrip?.disableTopPanel && participantsCount >= (filmstrip?.minParticipantCountForTopPanel ?? 50);
}

View File

@@ -31,7 +31,6 @@ import {
import {
addStageParticipant,
removeStageParticipant,
setFilmstripHeight,
setFilmstripWidth,
setStageParticipants
} from './actions';
@@ -39,9 +38,7 @@ import {
ACTIVE_PARTICIPANT_TIMEOUT,
DEFAULT_FILMSTRIP_WIDTH,
MAX_ACTIVE_PARTICIPANTS,
MIN_STAGE_VIEW_HEIGHT,
MIN_STAGE_VIEW_WIDTH,
TOP_FILMSTRIP_HEIGHT
MIN_STAGE_VIEW_WIDTH
} from './constants';
import {
isFilmstripResizable,
@@ -80,27 +77,19 @@ MiddlewareRegistry.register(store => next => action => {
const state = store.getState();
if (isFilmstripResizable(state)) {
const { width: filmstripWidth, topPanelHeight } = state['features/filmstrip'];
const { clientWidth, clientHeight } = action;
let height, width;
const { width: filmstripWidth } = state['features/filmstrip'];
const { clientWidth } = action;
let width;
if (filmstripWidth.current > clientWidth - MIN_STAGE_VIEW_WIDTH) {
width = Math.max(clientWidth - MIN_STAGE_VIEW_WIDTH, DEFAULT_FILMSTRIP_WIDTH);
} else {
width = Math.min(clientWidth - MIN_STAGE_VIEW_WIDTH, filmstripWidth.userSet);
}
if (width !== filmstripWidth.current) {
store.dispatch(setFilmstripWidth(width));
}
if (topPanelHeight.current > clientHeight - MIN_STAGE_VIEW_HEIGHT) {
height = Math.max(clientHeight - MIN_STAGE_VIEW_HEIGHT, TOP_FILMSTRIP_HEIGHT);
} else {
height = Math.min(clientHeight - MIN_STAGE_VIEW_HEIGHT, topPanelHeight.userSet);
}
if (height !== topPanelHeight.current) {
store.dispatch(setFilmstripHeight(height));
}
}
break;
}

View File

@@ -19,11 +19,7 @@ import {
SET_VISIBLE_REMOTE_PARTICIPANTS,
SET_VOLUME,
SET_MAX_STAGE_PARTICIPANTS,
CLEAR_STAGE_PARTICIPANTS,
SET_SCREENSHARING_TILE_DIMENSIONS,
SET_USER_FILMSTRIP_HEIGHT,
SET_FILMSTRIP_HEIGHT,
SET_TOP_PANEL_VISIBILITY
CLEAR_STAGE_PARTICIPANTS
} from './actionTypes';
const DEFAULT_STATE = {
@@ -80,11 +76,6 @@ const DEFAULT_STATE = {
*/
remoteParticipants: [],
/**
* The dimensions of the screenshare filmstrip.
*/
screenshareFilmstripDimensions: {},
/**
* The stage filmstrip view dimensions.
*
@@ -101,27 +92,6 @@ const DEFAULT_STATE = {
*/
tileViewDimensions: {},
/**
* The height of the resizable top panel.
*/
topPanelHeight: {
/**
* Current height. Affected by: user top panel resize,
* window resize.
*/
current: null,
/**
* Height set by user resize. Used as the preferred height.
*/
userSet: null
},
/**
* The indicator determines if the top panel is visible.
*/
topPanelVisible: true,
/**
* The vertical view dimensions.
*
@@ -257,15 +227,6 @@ ReducerRegistry.register(
...state
};
}
case SET_FILMSTRIP_HEIGHT:{
return {
...state,
topPanelHeight: {
...state.topPanelHeight,
current: action.height
}
};
}
case SET_FILMSTRIP_WIDTH: {
return {
...state,
@@ -275,17 +236,6 @@ ReducerRegistry.register(
}
};
}
case SET_USER_FILMSTRIP_HEIGHT: {
const { height } = action;
return {
...state,
topPanelHeight: {
current: height,
userSet: height
}
};
}
case SET_USER_FILMSTRIP_WIDTH: {
const { width } = action;
@@ -333,18 +283,6 @@ ReducerRegistry.register(
activeParticipants: []
};
}
case SET_SCREENSHARING_TILE_DIMENSIONS: {
return {
...state,
screenshareFilmstripDimensions: action.dimensions
};
}
case SET_TOP_PANEL_VISIBILITY: {
return {
...state,
topPanelVisible: action.visible
};
}
}
return state;

View File

@@ -14,7 +14,6 @@ import { getCurrentLayout, shouldDisplayTileView, LAYOUTS } from '../video-layou
import {
clearStageParticipants,
setHorizontalViewDimensions,
setScreensharingTileDimensions,
setStageFilmstripViewDimensions,
setTileViewDimensions,
setVerticalViewDimensions
@@ -24,8 +23,7 @@ import {
DISPLAY_DRAWER_THRESHOLD
} from './constants';
import {
isFilmstripResizable,
isTopPanelEnabled
isFilmstripResizable
} from './functions';
import './subscriber.any';
@@ -178,8 +176,7 @@ StateListenerRegistry.register(
visible: state['features/filmstrip'].visible,
clientWidth: state['features/base/responsive-ui'].clientWidth,
clientHeight: state['features/base/responsive-ui'].clientHeight,
tileView: state['features/video-layout'].tileViewEnabled,
height: state['features/filmstrip'].topPanelHeight?.current
tileView: state['features/video-layout'].tileViewEnabled
};
},
/* listener */(_, store) => {
@@ -201,27 +198,3 @@ StateListenerRegistry.register(
store.dispatch(selectParticipantInLargeVideo());
}
});
/**
* Listens for changes to determine the size of the screenshare filmstrip.
*/
StateListenerRegistry.register(
/* selector */ state => {
return {
length: state['features/video-layout'].remoteScreenShares.length,
clientWidth: state['features/base/responsive-ui'].clientWidth,
clientHeight: state['features/base/responsive-ui'].clientHeight,
height: state['features/filmstrip'].topPanelHeight?.current,
width: state['features/filmstrip'].width?.current,
visible: state['features/filmstrip'].visible,
topPanelVisible: state['features/filmstrip'].topPanelVisible
};
},
/* listener */({ length }, store) => {
if (length >= 1 && isTopPanelEnabled(store.getState())) {
store.dispatch(setScreensharingTileDimensions());
}
}, {
deepEquals: true
});

View File

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

View File

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

View File

@@ -134,7 +134,6 @@ export default {
borderColor: SECONDARY_COLOR,
borderRadius: BaseTheme.shape.borderRadius,
borderWidth: 2,
color: BaseTheme.palette.text06,
height: BaseTheme.spacing[7],
marginTop: BaseTheme.spacing[3],
marginHorizontal: 12,

View File

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

View File

@@ -152,19 +152,13 @@ export const conferenceScreenOptions = {
* Tab bar options for chat screen.
*/
export const chatTabBarOptions = {
tabBarActiveTintColor: BaseTheme.palette.field02,
tabBarActiveTintColor: BaseTheme.palette.screen01Header,
tabBarLabelStyle: {
fontSize: BaseTheme.typography.labelRegular.fontSize,
textTransform: 'capitalize'
fontSize: BaseTheme.typography.labelRegular.fontSize
},
tabBarInactiveTintColor: BaseTheme.palette.text03,
tabBarInactiveTintColor: BaseTheme.palette.text01,
tabBarIndicatorStyle: {
backgroundColor: BaseTheme.palette.field02
},
tabBarStyle: {
backgroundColor: BaseTheme.palette.ui01,
borderBottomColor: BaseTheme.palette.border05,
borderBottomWidth: 1
backgroundColor: BaseTheme.palette.screen01Header
}
};
@@ -268,7 +262,21 @@ export const gifsMenuOptions = presentationScreenOptions;
/**
* Screen options for shared document.
*/
export const sharedDocumentScreenOptions = presentationScreenOptions;
export const sharedDocumentScreenOptions = {
animation: 'slide_from_right',
headerBackTitleVisible: false,
headerShown: true,
headerStyle: {
backgroundColor: BaseTheme.palette.screen02Header
},
headerTitleStyle: {
color: BaseTheme.palette.text01
},
orientation: Platform.select({
ios: 'default',
android: 'all'
})
};
/**
* Screen options for connecting screen.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,11 +3,9 @@
import InlineDialog from '@atlaskit/inline-dialog';
import React, { Component } from 'react';
import { LoginDialog, WaitForOwnerDialog } from '../../authentication/components';
import { Avatar } from '../../base/avatar';
import { getRoomName } from '../../base/conference';
import { isNameReadOnly } from '../../base/config';
import { isDialogOpen } from '../../base/dialog/functions';
import { translate } from '../../base/i18n';
import { IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons';
import { isVideoMutedByUser } from '../../base/media';
@@ -16,7 +14,6 @@ import { ActionButton, InputField, PreMeetingScreen } from '../../base/premeetin
import { connect } from '../../base/redux';
import { getDisplayName, updateSettings } from '../../base/settings';
import { getLocalJitsiVideoTrack } from '../../base/tracks';
import { PasswordRequiredPrompt } from '../../room-lock/components';
import {
joinConference as joinConferenceAction,
joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
@@ -50,11 +47,6 @@ type Props = {
*/
hasJoinByPhoneButton: boolean,
/**
* Whether authentication is taking place or not.
*/
isAuthInProgress: boolean,
/**
* Joins the current meeting.
*/
@@ -350,7 +342,6 @@ class Prejoin extends Component<Props, State> {
const {
deviceStatusVisible,
hasJoinByPhoneButton,
isAuthInProgress,
joinConference,
joinConferenceWithoutAudio,
name,
@@ -387,7 +378,7 @@ class Prejoin extends Component<Props, State> {
data-testid = 'prejoin.screen'>
{this.showDisplayNameField ? (<InputField
autoComplete = { 'name' }
autoFocus = { !isAuthInProgress }
autoFocus = { true }
className = { showError ? 'error' : '' }
hasError = { showError }
onChange = { _setName }
@@ -459,14 +450,11 @@ function mapStateToProps(state): Object {
const name = getDisplayName(state);
const showErrorOnJoin = isDisplayNameRequired(state) && !name;
const { id: participantId } = getLocalParticipant(state);
const isAuthInProgress = isDialogOpen(state, WaitForOwnerDialog)
|| isDialogOpen(state, LoginDialog) || isDialogOpen(state, PasswordRequiredPrompt);
return {
canEditDisplayName: isPrejoinDisplayNameVisible(state),
deviceStatusVisible: isDeviceStatusVisible(state),
hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state),
isAuthInProgress,
name,
participantId,
prejoinConfig: state['features/base/config'].prejoinConfig,

View File

@@ -121,7 +121,6 @@ export default {
borderColor: SECONDARY_COLOR,
borderRadius: BaseTheme.shape.borderRadius,
borderWidth: 2,
color: BaseTheme.palette.text06,
height: BaseTheme.spacing[7],
marginTop: BaseTheme.spacing[3],
textAlign: 'center'

View File

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

View File

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

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