mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-06 23:02:28 +00:00
Compare commits
29 Commits
pr-testing
...
mobile-23.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
349d66dd6b | ||
|
|
9cd9a85a4d | ||
|
|
5ce3f02c03 | ||
|
|
5bd7b82502 | ||
|
|
5d77491c8c | ||
|
|
934a7128b0 | ||
|
|
1d8a3a761f | ||
|
|
c305d5a6e9 | ||
|
|
3d0b3a2f71 | ||
|
|
7d48819619 | ||
|
|
c9807cbc1b | ||
|
|
155abe258c | ||
|
|
e50477ca8d | ||
|
|
fb45fc4db4 | ||
|
|
32e018f43b | ||
|
|
e3dd39f6e4 | ||
|
|
92c43c03ad | ||
|
|
8d8bd4d8d4 | ||
|
|
8dda34a502 | ||
|
|
3a85a915bf | ||
|
|
17b0a12224 | ||
|
|
b52394d328 | ||
|
|
cadb216cea | ||
|
|
2bf8e6cfce | ||
|
|
261a8a6740 | ||
|
|
35dbcd9b9d | ||
|
|
45bf00d096 | ||
|
|
78847148d8 | ||
|
|
c53e7ff9cd |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -99,6 +99,10 @@ tsconfig.json
|
||||
#
|
||||
react-native-sdk/*.tgz
|
||||
react-native-sdk/android/src
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetOngoingConferenceService.java
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetReactNativePackage.java
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JMOngoingConferenceModule.java
|
||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/RNOngoingNotification.java
|
||||
react-native-sdk/images
|
||||
react-native-sdk/ios
|
||||
react-native-sdk/lang
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Server URL configuration -->
|
||||
<restriction
|
||||
android:defaultValue="https://meet.jit.si"
|
||||
android:description="@string/restriction_server_url_description"
|
||||
android:key="SERVER_URL"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/restriction_server_url_title"/>
|
||||
</restrictions>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Server URL configuration -->
|
||||
<restriction
|
||||
android:description="@string/restriction_server_url_description"
|
||||
android:key="SERVER_URL"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/restriction_server_url_title"/>
|
||||
</restrictions>
|
||||
|
||||
@@ -26,5 +26,5 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=99.0.0
|
||||
sdkVersion=99.0.0
|
||||
appVersion=23.6.1
|
||||
sdkVersion=8.6.1
|
||||
|
||||
@@ -373,7 +373,7 @@ PODS:
|
||||
- React
|
||||
- react-native-netinfo (9.4.1):
|
||||
- React-Core
|
||||
- react-native-orientation-locker (1.5.0):
|
||||
- react-native-orientation-locker (1.6.0):
|
||||
- React-Core
|
||||
- react-native-pager-view (6.2.0):
|
||||
- React-Core
|
||||
@@ -752,7 +752,7 @@ SPEC CHECKSUMS:
|
||||
react-native-get-random-values: dee677497c6a740b71e5612e8dbd83e7539ed5bb
|
||||
react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774
|
||||
react-native-netinfo: fefd4e98d75cbdd6e85fc530f7111a8afdf2b0c5
|
||||
react-native-orientation-locker: 851f6510d8046ea2f14aa169b1e01fcd309a94ba
|
||||
react-native-orientation-locker: 4409c5b12b65f942e75449872b4f078b6f27af81
|
||||
react-native-pager-view: 0ccb8bf60e2ebd38b1f3669fa3650ecce81db2df
|
||||
react-native-performance: 47ac22ebf2aa24f324a96a5825581f6ce18c09e8
|
||||
react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>99.0.0</string>
|
||||
<string>23.6.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
[builder setFeatureFlag:@"resolution" withValue:@(360)];
|
||||
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
|
||||
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
}];
|
||||
|
||||
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
@@ -126,7 +125,7 @@
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application
|
||||
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
|
||||
return [[JitsiMeet sharedInstance] application:application
|
||||
return [[JitsiMeet sharedInstance] application:application
|
||||
supportedInterfaceOrientationsForWindow:window];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>99.0.0</string>
|
||||
<string>23.6.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>99.0.0</string>
|
||||
<string>23.6.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>99.0.0</string>
|
||||
<string>23.6.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>99.0.0</string>
|
||||
<string>8.6.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>99.0.0</string>
|
||||
<string>8.6.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -84,7 +84,7 @@
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-orientation-locker": "https://github.com/jitsi/react-native-orientation-locker/releases/download/v1.5.0-jitsi1/react-native-orientation-locker-1.5.0.tgz",
|
||||
"react-native-orientation-locker": "1.6.0",
|
||||
"react-native-pager-view": "6.2.0",
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.0.0",
|
||||
@@ -15919,10 +15919,9 @@
|
||||
"integrity": "sha512-0Fotox+eLXQooeibVs3P60yASYUWjtRw9MZNmbuHt5UZQrgUrAKsE4jm7gTr4tPU1m1RkwGzcgUFpcOkh/ec7g=="
|
||||
},
|
||||
"node_modules/react-native-orientation-locker": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://github.com/jitsi/react-native-orientation-locker/releases/download/v1.5.0-jitsi1/react-native-orientation-locker-1.5.0.tgz",
|
||||
"integrity": "sha512-fR/lyo2Pqlf7ubWAhUmOKku7Ciaf83q6f7sNgYvPGUhDEjWUy/lyOJu2WZBnM4XdAmftBVSXl/JhBQAqXcckVw==",
|
||||
"license": "MIT",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-orientation-locker/-/react-native-orientation-locker-1.6.0.tgz",
|
||||
"integrity": "sha512-D3IOtAcaAi6S2w0Y1EUnr16I47isosQbE7F67fAu9A+gE67NkyKaJ9HL5EsZ+Uc7+7m+NsuBjx3dxuANNy8rVA==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1",
|
||||
"react-native": ">=0.63.2",
|
||||
@@ -32191,8 +32190,9 @@
|
||||
"integrity": "sha512-0Fotox+eLXQooeibVs3P60yASYUWjtRw9MZNmbuHt5UZQrgUrAKsE4jm7gTr4tPU1m1RkwGzcgUFpcOkh/ec7g=="
|
||||
},
|
||||
"react-native-orientation-locker": {
|
||||
"version": "https://github.com/jitsi/react-native-orientation-locker/releases/download/v1.5.0-jitsi1/react-native-orientation-locker-1.5.0.tgz",
|
||||
"integrity": "sha512-fR/lyo2Pqlf7ubWAhUmOKku7Ciaf83q6f7sNgYvPGUhDEjWUy/lyOJu2WZBnM4XdAmftBVSXl/JhBQAqXcckVw=="
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-orientation-locker/-/react-native-orientation-locker-1.6.0.tgz",
|
||||
"integrity": "sha512-D3IOtAcaAi6S2w0Y1EUnr16I47isosQbE7F67fAu9A+gE67NkyKaJ9HL5EsZ+Uc7+7m+NsuBjx3dxuANNy8rVA=="
|
||||
},
|
||||
"react-native-pager-view": {
|
||||
"version": "6.2.0",
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-orientation-locker": "https://github.com/jitsi/react-native-orientation-locker/releases/download/v1.5.0-jitsi1/react-native-orientation-locker-1.5.0.tgz",
|
||||
"react-native-orientation-locker": "1.6.0",
|
||||
"react-native-pager-view": "6.2.0",
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.0.0",
|
||||
|
||||
@@ -73,6 +73,13 @@ cd ios && pod install && cd ..
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
```
|
||||
- In `android/app/src/main/AndroidManifest.xml`, under the `</application>` tag, include
|
||||
```xml
|
||||
<service
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
||||
android:foregroundServiceType="mediaProjection" />
|
||||
```
|
||||
This will take care of the screen share feature.
|
||||
|
||||
If you want to test all the steps before applying them to your app, you can check our React Native SDK sample app here:
|
||||
https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
|
||||
@ReactModule(name = JMOngoingConferenceModule.NAME)
|
||||
class JMOngoingConferenceModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "JMOngoingConference";
|
||||
|
||||
public JMOngoingConferenceModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void launch() {
|
||||
Context context = getReactApplicationContext();
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
|
||||
JitsiMeetOngoingConferenceService.launch(context, currentActivity);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void abort() {
|
||||
Context context = getReactApplicationContext();
|
||||
|
||||
JitsiMeetOngoingConferenceService.abort(context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class implements an Android {@link Service}, a foreground one specifically, and it's
|
||||
* responsible for presenting an ongoing notification when a conference is in progress.
|
||||
* The service will help keep the app running while in the background.
|
||||
*
|
||||
* See: https://developer.android.com/guide/components/services
|
||||
*/
|
||||
public class JitsiMeetOngoingConferenceService extends Service {
|
||||
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
||||
|
||||
public static void launch(Context context, Activity currentActivity) {
|
||||
|
||||
RNOngoingNotification.createOngoingConferenceNotificationChannel(currentActivity);
|
||||
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
|
||||
ComponentName componentName;
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
componentName = context.startForegroundService(intent);
|
||||
} else {
|
||||
componentName = context.startService(intent);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
|
||||
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
|
||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (componentName == null) {
|
||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
|
||||
}
|
||||
}
|
||||
|
||||
public static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Notification notification = RNOngoingNotification.buildOngoingConferenceNotification(this);
|
||||
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
startForeground(RNOngoingNotification.NOTIFICATION_ID, notification);
|
||||
JitsiMeetLogger.i(TAG + " Service started");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public class JitsiMeetReactNativePackage implements ReactPackage {
|
||||
new AndroidSettingsModule(reactContext),
|
||||
new AppInfoModule(reactContext),
|
||||
new AudioModeModule(reactContext),
|
||||
new JMOngoingConferenceModule(reactContext),
|
||||
new JavaScriptSandboxModule(reactContext),
|
||||
new LocaleDetector(reactContext),
|
||||
new LogBridgeModule(reactContext),
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Helper class for creating the ongoing notification which is used with
|
||||
* {@link JitsiMeetOngoingConferenceService}. It allows the user to easily get back to the app
|
||||
* and to hangup from within the notification itself.
|
||||
*/
|
||||
class RNOngoingNotification {
|
||||
private static final String TAG = RNOngoingNotification.class.getSimpleName();
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
|
||||
static void createOngoingConferenceNotificationChannel(Activity currentActivity) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentActivity == null) {
|
||||
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationManager notificationManager
|
||||
= (NotificationManager) currentActivity.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
NotificationChannel channel
|
||||
= notificationManager.getNotificationChannel("JitsiOngoingConferenceChannel");
|
||||
if (channel != null) {
|
||||
// The channel was already created, no need to do it again.
|
||||
return;
|
||||
}
|
||||
|
||||
channel = new NotificationChannel("JitsiOngoingConferenceChannel", currentActivity.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false);
|
||||
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
static Notification buildOngoingConferenceNotification(Context context) {
|
||||
|
||||
if (context == null) {
|
||||
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
|
||||
return null;
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "JitsiOngoingConferenceChannel");
|
||||
|
||||
builder
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setContentTitle(context.getString(R.string.ongoing_notification_title))
|
||||
.setContentText(context.getString(R.string.ongoing_notification_text))
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setOngoing(true)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setUsesChronometer(true)
|
||||
.setAutoCancel(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import { setAudioMuted, setVideoMuted } from './react/features/base/media/action
|
||||
|
||||
|
||||
interface IEventListeners {
|
||||
onAudioMutedChanged?: Function;
|
||||
onVideoMutedChanged?: Function;
|
||||
onConferenceBlurred?: Function;
|
||||
onConferenceFocused?: Function;
|
||||
onConferenceJoined?: Function;
|
||||
@@ -107,6 +109,8 @@ export const JitsiMeeting = forwardRef((props: IAppProps, ref) => {
|
||||
setAppProps({
|
||||
'flags': flags,
|
||||
'rnSdkHandlers': {
|
||||
onAudioMutedChanged: eventListeners?.onAudioMutedChanged,
|
||||
onVideoMutedChanged: eventListeners?.onVideoMutedChanged,
|
||||
onConferenceBlurred: eventListeners?.onConferenceBlurred,
|
||||
onConferenceFocused: eventListeners?.onConferenceFocused,
|
||||
onConferenceJoined: eventListeners?.onConferenceJoined,
|
||||
|
||||
156
react-native-sdk/package-lock.json
generated
156
react-native-sdk/package-lock.json
generated
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@jitsi/react-native-sdk",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@jitsi/react-native-sdk",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "2.1.3",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@react-navigation/bottom-tabs": "6.5.8",
|
||||
@@ -19,7 +19,7 @@
|
||||
"@react-navigation/native": "6.1.7",
|
||||
"@react-navigation/stack": "6.3.17",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"base64-js": "1.3.1",
|
||||
"base64-js": "1.5.1",
|
||||
"grapheme-splitter": "1.0.4",
|
||||
"i18n-iso-countries": "6.8.0",
|
||||
"i18next": "17.0.6",
|
||||
@@ -27,7 +27,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1716.0.0+93c167d3/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -69,7 +69,7 @@
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-orientation-locker": "1.5.0",
|
||||
"react-native-orientation-locker": "1.6.0",
|
||||
"react-native-pager-view": "6.2.0",
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.0.0",
|
||||
@@ -80,7 +80,7 @@
|
||||
"react-native-svg": "13.13.0",
|
||||
"react-native-video": "6.0.0-alpha.7",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "111.0.3",
|
||||
"react-native-webrtc": "111.0.6",
|
||||
"react-native-webview": "13.5.1"
|
||||
}
|
||||
},
|
||||
@@ -396,13 +396,13 @@
|
||||
"integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="
|
||||
},
|
||||
"node_modules/@jitsi/js-utils": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.1.3.tgz",
|
||||
"integrity": "sha512-fXoNLu2JHQGPgzjCDG5qWPPStiXI+AxSzu1JIVY4dMULIFsw4a+doMgcjTIlSfB393J2xccKQYwZSejZUn9MEw==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.2.1.tgz",
|
||||
"integrity": "sha512-4Ia4hWO7aTMGbYftzeBr+IHIu5YxiWwTlhsSK34z6925oNAUNI863WgYYGTcXkW/1yuM6LBZrnuZBySDqosISA==",
|
||||
"dependencies": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"bowser": "2.7.0",
|
||||
"js-md5": "0.7.3"
|
||||
"js-md5": "0.7.3",
|
||||
"ua-parser-js": "1.0.35"
|
||||
}
|
||||
},
|
||||
"node_modules/@jitsi/js-utils/node_modules/js-md5": {
|
||||
@@ -993,9 +993,23 @@
|
||||
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
@@ -1010,11 +1024,6 @@
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
|
||||
},
|
||||
"node_modules/bowser": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz",
|
||||
"integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -2481,14 +2490,14 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-ky3q5gLeLDHFtenXe7+QlxAh33opz0a3bqEp8svA9urR1AICUi0QthOwmUerTdfSRFU3wfVObF9axlX1fbHZ3A==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1716.0.0+93c167d3/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-+6nI2ExiTS8aKnq3ZDpYKs1MSDtUHaK4hIhVxyI7Db6ZojrMOpZx6lnrmqz+AHWllSHvIggFz48du7tG6UYANw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "2.1.3",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@jitsi/rtcstats": "9.6.0",
|
||||
"@jitsi/sdp-interop": "git+https://github.com/jitsi/sdp-interop#3d49eb4aa26863a3f8d32d7581cdb4321244266b",
|
||||
"@jitsi/sdp-simulcast": "0.4.0",
|
||||
"@testrtc/watchrtc-sdk": "1.36.3",
|
||||
@@ -2509,6 +2518,30 @@
|
||||
"webrtc-adapter": "8.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/@jitsi/rtcstats": {
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.6.0.tgz",
|
||||
"integrity": "sha512-eFFD6vp1dFwvjAR+NRjsxlvclFvqo/nbU2RcSLzXbIjG/WraMHyxMSzllZyT1T3S2iPehS4dnZ/ZYyz5WYHTaA==",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "^2.2.0",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"sdp": "^3.0.3",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/@jitsi/rtcstats/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"node_modules/lib-jitsi-meet/node_modules/uuid": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
|
||||
@@ -3570,6 +3603,24 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "1.0.35",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz",
|
||||
"integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ua-parser-js"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/faisalman"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
@@ -4044,13 +4095,13 @@
|
||||
"integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="
|
||||
},
|
||||
"@jitsi/js-utils": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.1.3.tgz",
|
||||
"integrity": "sha512-fXoNLu2JHQGPgzjCDG5qWPPStiXI+AxSzu1JIVY4dMULIFsw4a+doMgcjTIlSfB393J2xccKQYwZSejZUn9MEw==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-2.2.1.tgz",
|
||||
"integrity": "sha512-4Ia4hWO7aTMGbYftzeBr+IHIu5YxiWwTlhsSK34z6925oNAUNI863WgYYGTcXkW/1yuM6LBZrnuZBySDqosISA==",
|
||||
"requires": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"bowser": "2.7.0",
|
||||
"js-md5": "0.7.3"
|
||||
"js-md5": "0.7.3",
|
||||
"ua-parser-js": "1.0.35"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-md5": {
|
||||
@@ -4445,9 +4496,9 @@
|
||||
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
@@ -4459,11 +4510,6 @@
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
|
||||
},
|
||||
"bowser": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz",
|
||||
"integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -5476,12 +5522,12 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-ky3q5gLeLDHFtenXe7+QlxAh33opz0a3bqEp8svA9urR1AICUi0QthOwmUerTdfSRFU3wfVObF9axlX1fbHZ3A==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1716.0.0+93c167d3/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-+6nI2ExiTS8aKnq3ZDpYKs1MSDtUHaK4hIhVxyI7Db6ZojrMOpZx6lnrmqz+AHWllSHvIggFz48du7tG6UYANw==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.1.3",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@jitsi/rtcstats": "9.6.0",
|
||||
"@jitsi/sdp-interop": "git+https://github.com/jitsi/sdp-interop#3d49eb4aa26863a3f8d32d7581cdb4321244266b",
|
||||
"@jitsi/sdp-simulcast": "0.4.0",
|
||||
"@testrtc/watchrtc-sdk": "1.36.3",
|
||||
@@ -5502,6 +5548,29 @@
|
||||
"webrtc-adapter": "8.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jitsi/rtcstats": {
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.6.0.tgz",
|
||||
"integrity": "sha512-eFFD6vp1dFwvjAR+NRjsxlvclFvqo/nbU2RcSLzXbIjG/WraMHyxMSzllZyT1T3S2iPehS4dnZ/ZYyz5WYHTaA==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "^2.2.0",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"sdp": "^3.0.3",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
|
||||
@@ -6263,6 +6332,11 @@
|
||||
"is-typed-array": "^1.1.9"
|
||||
}
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "1.0.35",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz",
|
||||
"integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA=="
|
||||
},
|
||||
"uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jitsi/react-native-sdk",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.3",
|
||||
"description": "React Native SDK for Jitsi Meet.",
|
||||
"main": "index.tsx",
|
||||
"license": "Apache-2.0",
|
||||
@@ -11,7 +11,7 @@
|
||||
"url": "git+https://github.com/jitsi/jitsi-meet.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "2.1.3",
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@react-navigation/bottom-tabs": "6.5.8",
|
||||
@@ -20,7 +20,7 @@
|
||||
"@react-navigation/native": "6.1.7",
|
||||
"@react-navigation/stack": "6.3.17",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"base64-js": "1.3.1",
|
||||
"base64-js": "1.5.1",
|
||||
"grapheme-splitter": "1.0.4",
|
||||
"i18n-iso-countries": "6.8.0",
|
||||
"i18next": "17.0.6",
|
||||
@@ -28,7 +28,7 @@
|
||||
"i18next-http-backend": "^2.2.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1687.0.0+cafe30d7/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1716.0.0+93c167d3/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -73,7 +73,7 @@
|
||||
"react-native-pager-view": "6.2.0",
|
||||
"react-native-paper": "5.10.3",
|
||||
"react-native-performance": "5.0.0",
|
||||
"react-native-orientation-locker": "1.5.0",
|
||||
"react-native-orientation-locker": "1.6.0",
|
||||
"react-native-safe-area-context": "4.7.1",
|
||||
"react-native-screens": "3.24.0",
|
||||
"react-native-sound": "0.11.2",
|
||||
@@ -81,7 +81,7 @@
|
||||
"react-native-svg": "13.13.0",
|
||||
"react-native-video": "6.0.0-alpha.7",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "111.0.3",
|
||||
"react-native-webrtc": "111.0.6",
|
||||
"react-native-webview": "13.5.1"
|
||||
},
|
||||
"overrides": {
|
||||
@@ -97,4 +97,4 @@
|
||||
"keywords": [
|
||||
"react-native"
|
||||
]
|
||||
}
|
||||
}
|
||||
26
react-native-sdk/prepare_sdk.js
vendored
26
react-native-sdk/prepare_sdk.js
vendored
@@ -6,7 +6,9 @@ const packageJSON = require('../package.json');
|
||||
const SDKPackageJSON = require('./package.json');
|
||||
|
||||
const androidSourcePath = '../android/sdk/src/main/java/org/jitsi/meet/sdk';
|
||||
const androidMainSourcePath = '../android/sdk/src/main/res';
|
||||
const androidTargetPath = './android/src/main/java/org/jitsi/meet/sdk';
|
||||
const androidMainTargetPath = './android/src/main/res';
|
||||
const iosSrcPath = '../ios/sdk/src';
|
||||
const iosDestPath = './ios/src';
|
||||
|
||||
@@ -169,6 +171,30 @@ copyFolderRecursiveSync(
|
||||
`${androidSourcePath}/log`,
|
||||
`${androidTargetPath}/log`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidMainSourcePath}/values`,
|
||||
`${androidMainTargetPath}`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidMainSourcePath}/drawable-hdpi`,
|
||||
`${androidMainTargetPath}`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidMainSourcePath}/drawable-mdpi`,
|
||||
`${androidMainTargetPath}`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidMainSourcePath}/drawable-xhdpi`,
|
||||
`${androidMainTargetPath}`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidMainSourcePath}/drawable-xxhdpi`,
|
||||
`${androidMainTargetPath}`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidMainSourcePath}/drawable-xxxhdpi`,
|
||||
`${androidMainTargetPath}`
|
||||
);
|
||||
copyFolderRecursiveSync(
|
||||
`${androidSourcePath}/net`,
|
||||
`${androidTargetPath}/log`
|
||||
|
||||
15
react-native-sdk/update_dependencies.js
vendored
15
react-native-sdk/update_dependencies.js
vendored
@@ -25,7 +25,8 @@ function updateDependencies() {
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!semver.valid(packageJSON.dependencies[key])) {
|
||||
if (!semver.valid(packageJSON.dependencies[key])
|
||||
&& packageJSON.dependencies[key] !== RNSDKpackageJSON.peerDependencies[key]) {
|
||||
packageJSON.dependencies[key] = RNSDKpackageJSON.peerDependencies[key];
|
||||
updated = true;
|
||||
|
||||
@@ -46,6 +47,18 @@ function updateDependencies() {
|
||||
|
||||
console.log(`${key} is now set to ${RNSDKpackageJSON.peerDependencies[key]}`);
|
||||
}
|
||||
|
||||
if (!semver.valid(RNSDKpackageJSON.peerDependencies[key])
|
||||
&& RNSDKpackageJSON.peerDependencies[key].includes('github')
|
||||
&& packageJSON.dependencies[key] !== RNSDKpackageJSON.peerDependencies[key]) {
|
||||
packageJSON.dependencies[key] = RNSDKpackageJSON.peerDependencies[key];
|
||||
updated = true;
|
||||
|
||||
console.log(
|
||||
`A fix for ${key} is available on ${RNSDKpackageJSON.peerDependencies[key]}.
|
||||
This is now set on your end.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
packageJSON.overrides = packageJSON.overrides || {};
|
||||
|
||||
@@ -5,12 +5,15 @@ import { openTokenAuthUrl } from '../authentication/actions';
|
||||
// @ts-ignore
|
||||
import { getTokenAuthUrl, isTokenAuthEnabled } from '../authentication/functions';
|
||||
import { getJwtExpirationDate } from '../base/jwt/functions';
|
||||
import { MEDIA_TYPE } from '../base/media/constants';
|
||||
import { isLocalTrackMuted } from '../base/tracks/functions.any';
|
||||
import { getLocationContextRoot, parseURIString } from '../base/util/uri';
|
||||
|
||||
import { addTrackStateToURL } from './functions.any';
|
||||
import logger from './logger';
|
||||
import { IStore } from './types';
|
||||
|
||||
|
||||
/**
|
||||
* Redirects to another page generated by replacing the path in the original URL
|
||||
* with the given path.
|
||||
@@ -104,7 +107,11 @@ export function maybeRedirectToTokenAuthUrl(
|
||||
dispatch: IStore['dispatch'], getState: IStore['getState'], failureCallback: Function) {
|
||||
const state = getState();
|
||||
const config = state['features/base/config'];
|
||||
const { enabled: audioOnlyEnabled } = state['features/base/audio-only'];
|
||||
const { startAudioOnly } = config;
|
||||
const { locationURL = { href: '' } as URL } = state['features/base/connection'];
|
||||
const audioMuted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.AUDIO);
|
||||
const videoMuted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (!isTokenAuthEnabled(config)) {
|
||||
return false;
|
||||
@@ -120,7 +127,18 @@ export function maybeRedirectToTokenAuthUrl(
|
||||
const room = state['features/base/conference'].room;
|
||||
const { tenant } = parseURIString(locationURL.href) || {};
|
||||
|
||||
getTokenAuthUrl(config, room, tenant, true, locationURL)
|
||||
getTokenAuthUrl(
|
||||
config,
|
||||
locationURL,
|
||||
{
|
||||
audioMuted,
|
||||
audioOnlyEnabled: audioOnlyEnabled || startAudioOnly,
|
||||
skipPrejoin: true,
|
||||
videoMuted
|
||||
},
|
||||
room,
|
||||
tenant
|
||||
)
|
||||
.then((tokenAuthServiceUrl: string | undefined) => {
|
||||
if (!tokenAuthServiceUrl) {
|
||||
logger.warn('Cannot handle login, token service URL is not set');
|
||||
|
||||
@@ -21,6 +21,7 @@ import { AbstractApp, IProps as AbstractAppProps } from './AbstractApp';
|
||||
import '../middlewares.native';
|
||||
import '../reducers.native';
|
||||
|
||||
|
||||
declare let __DEV__: any;
|
||||
|
||||
const { AppInfo } = NativeModules;
|
||||
@@ -110,7 +111,7 @@ export class App extends AbstractApp<IProps> {
|
||||
*/
|
||||
async _extraInit() {
|
||||
const { dispatch, getState } = this.state.store ?? {};
|
||||
const { flags = {} } = this.props;
|
||||
const { flags = {}, url, userInfo } = this.props;
|
||||
let callIntegrationEnabled = flags[CALL_INTEGRATION_ENABLED as keyof typeof flags];
|
||||
|
||||
// CallKit does not work on the simulator, make sure we disable it.
|
||||
@@ -153,16 +154,18 @@ export class App extends AbstractApp<IProps> {
|
||||
await rootNavigationReady;
|
||||
|
||||
// Update specified server URL.
|
||||
if (typeof this.props.url !== 'undefined') {
|
||||
if (typeof url !== 'undefined') {
|
||||
|
||||
// @ts-ignore
|
||||
const { serverURL } = this.props.url;
|
||||
const { serverURL } = url;
|
||||
|
||||
if (typeof serverURL !== 'undefined') {
|
||||
dispatch?.(updateSettings({ serverURL }));
|
||||
}
|
||||
}
|
||||
|
||||
dispatch?.(updateSettings(this.props.userInfo || {}));
|
||||
// @ts-ignore
|
||||
dispatch?.(updateSettings(userInfo || {}));
|
||||
|
||||
// Update settings with feature-flag.
|
||||
if (typeof callIntegrationEnabled !== 'undefined') {
|
||||
|
||||
@@ -55,8 +55,20 @@ function _getWebConferenceRoute(state: IReduxState) {
|
||||
&& !state['features/base/jwt'].jwt && room) {
|
||||
const { locationURL = { href: '' } as URL } = state['features/base/connection'];
|
||||
const { tenant } = parseURIString(locationURL.href) || {};
|
||||
const { startAudioOnly } = config;
|
||||
|
||||
return getTokenAuthUrl(config, room, tenant, false, locationURL)
|
||||
return getTokenAuthUrl(
|
||||
config,
|
||||
locationURL,
|
||||
{
|
||||
audioMuted: false,
|
||||
audioOnlyEnabled: startAudioOnly,
|
||||
skipPrejoin: false,
|
||||
videoMuted: false
|
||||
},
|
||||
room,
|
||||
tenant
|
||||
)
|
||||
.then((url: string | undefined) => {
|
||||
route.href = url;
|
||||
|
||||
|
||||
@@ -13,29 +13,65 @@ export const isTokenAuthEnabled = (config: IConfig): boolean =>
|
||||
/**
|
||||
* Returns the state that we can add as a parameter to the tokenAuthUrl.
|
||||
*
|
||||
* @param {URL} locationURL - The location URL.
|
||||
* @param {Object} options: - Config options {
|
||||
* audioMuted: boolean | undefined
|
||||
* audioOnlyEnabled: boolean | undefined,
|
||||
* skipPrejoin: boolean | undefined,
|
||||
* videoMuted: boolean | undefined
|
||||
* }.
|
||||
* @param {string?} roomName - The room name.
|
||||
* @param {string?} tenant - The tenant name if any.
|
||||
* @param {boolean} skipPrejoin - Whether to skip pre-join page.
|
||||
* @param {URL} locationURL - The location URL.
|
||||
*
|
||||
* @returns {Object} The state object.
|
||||
*/
|
||||
export const _getTokenAuthState = (
|
||||
locationURL: URL,
|
||||
options: {
|
||||
audioMuted: boolean | undefined;
|
||||
audioOnlyEnabled: boolean | undefined;
|
||||
skipPrejoin: boolean | undefined;
|
||||
videoMuted: boolean | undefined;
|
||||
},
|
||||
roomName: string | undefined,
|
||||
tenant: string | undefined,
|
||||
skipPrejoin: boolean | undefined = false,
|
||||
locationURL: URL): object => {
|
||||
tenant: string | undefined): object => {
|
||||
const state = {
|
||||
room: roomName,
|
||||
roomSafe: getBackendSafeRoomName(roomName),
|
||||
tenant
|
||||
};
|
||||
|
||||
const {
|
||||
audioMuted = false,
|
||||
audioOnlyEnabled = false,
|
||||
skipPrejoin = false,
|
||||
videoMuted = false
|
||||
} = options;
|
||||
|
||||
if (audioMuted) {
|
||||
|
||||
// @ts-ignore
|
||||
state['config.startWithAudioMuted'] = true;
|
||||
}
|
||||
|
||||
if (audioOnlyEnabled) {
|
||||
|
||||
// @ts-ignore
|
||||
state['config.startAudioOnly'] = true;
|
||||
}
|
||||
|
||||
if (skipPrejoin) {
|
||||
// We have already shown the prejoin screen, no need to show it again after obtaining the token.
|
||||
// @ts-ignore
|
||||
state['config.prejoinConfig.enabled'] = false;
|
||||
}
|
||||
|
||||
if (videoMuted) {
|
||||
|
||||
// @ts-ignore
|
||||
state['config.startWithVideoMuted'] = true;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(locationURL.hash);
|
||||
|
||||
for (const [ key, value ] of params) {
|
||||
|
||||
@@ -14,10 +14,15 @@ export * from './functions.any';
|
||||
* argument to this method.
|
||||
*
|
||||
* @param {Object} config - Configuration state object from store. A URL pattern pointing to the login service.
|
||||
* @param {string} roomName - The name of the conference room for which the user will be authenticated.
|
||||
* @param {string} tenant - The name of the conference tenant.
|
||||
* @param {string} skipPrejoin - The name of the conference room for which the user will be authenticated.
|
||||
* @param {URL} locationURL - The location URL.
|
||||
* @param {Object} options: - Config options {
|
||||
* audioMuted: boolean | undefined
|
||||
* audioOnlyEnabled: boolean | undefined,
|
||||
* skipPrejoin: boolean | undefined,
|
||||
* videoMuted: boolean | undefined
|
||||
* }.
|
||||
* @param {string?} roomName - The room name.
|
||||
* @param {string?} tenant - The tenant name if any.
|
||||
*
|
||||
* @returns {Promise<string|undefined>} - The URL pointing to JWT login service or
|
||||
* <tt>undefined</tt> if the pattern stored in config is not a string and the URL can not be
|
||||
@@ -25,11 +30,23 @@ export * from './functions.any';
|
||||
*/
|
||||
export const getTokenAuthUrl = (
|
||||
config: IConfig,
|
||||
locationURL: URL,
|
||||
options: {
|
||||
audioMuted: boolean | undefined;
|
||||
audioOnlyEnabled: boolean | undefined;
|
||||
skipPrejoin: boolean | undefined;
|
||||
videoMuted: boolean | undefined;
|
||||
},
|
||||
roomName: string | undefined,
|
||||
tenant: string | undefined,
|
||||
skipPrejoin: boolean | undefined = false,
|
||||
// eslint-disable-next-line max-params
|
||||
locationURL: URL): Promise<string | undefined> => {
|
||||
tenant: string | undefined): Promise<string | undefined> => {
|
||||
|
||||
const {
|
||||
audioMuted = false,
|
||||
audioOnlyEnabled = false,
|
||||
skipPrejoin = false,
|
||||
videoMuted = false
|
||||
} = options;
|
||||
|
||||
let url = config.tokenAuthUrl;
|
||||
|
||||
@@ -38,7 +55,17 @@ export const getTokenAuthUrl = (
|
||||
}
|
||||
|
||||
if (url.indexOf('{state}')) {
|
||||
const state = _getTokenAuthState(roomName, tenant, skipPrejoin, locationURL);
|
||||
const state = _getTokenAuthState(
|
||||
locationURL,
|
||||
{
|
||||
audioMuted,
|
||||
audioOnlyEnabled,
|
||||
skipPrejoin,
|
||||
videoMuted
|
||||
},
|
||||
roomName,
|
||||
tenant
|
||||
);
|
||||
|
||||
// Append ios=true or android=true to the token URL.
|
||||
// @ts-ignore
|
||||
|
||||
@@ -32,10 +32,15 @@ function _cryptoRandom() {
|
||||
* argument to this method.
|
||||
*
|
||||
* @param {Object} config - Configuration state object from store. A URL pattern pointing to the login service.
|
||||
* @param {string} roomName - The name of the conference room for which the user will be authenticated.
|
||||
* @param {string} tenant - The name of the conference tenant.
|
||||
* @param {string} skipPrejoin - The name of the conference room for which the user will be authenticated.
|
||||
* @param {URL} locationURL - The current location URL.
|
||||
* @param {URL} locationURL - The location URL.
|
||||
* @param {Object} options: - Config options {
|
||||
* audioMuted: boolean | undefined
|
||||
* audioOnlyEnabled: boolean | undefined,
|
||||
* skipPrejoin: boolean | undefined,
|
||||
* videoMuted: boolean | undefined
|
||||
* }.
|
||||
* @param {string?} roomName - The room name.
|
||||
* @param {string?} tenant - The tenant name if any.
|
||||
*
|
||||
* @returns {Promise<string|undefined>} - The URL pointing to JWT login service or
|
||||
* <tt>undefined</tt> if the pattern stored in config is not a string and the URL can not be
|
||||
@@ -43,11 +48,23 @@ function _cryptoRandom() {
|
||||
*/
|
||||
export const getTokenAuthUrl = (
|
||||
config: IConfig,
|
||||
locationURL: URL,
|
||||
options: {
|
||||
audioMuted: boolean | undefined;
|
||||
audioOnlyEnabled: boolean | undefined;
|
||||
skipPrejoin: boolean | undefined;
|
||||
videoMuted: boolean | undefined;
|
||||
},
|
||||
roomName: string | undefined,
|
||||
tenant: string | undefined,
|
||||
skipPrejoin: boolean | undefined = false,
|
||||
// eslint-disable-next-line max-params
|
||||
locationURL: URL): Promise<string | undefined> => {
|
||||
tenant: string | undefined): Promise<string | undefined> => {
|
||||
|
||||
const {
|
||||
audioMuted = false,
|
||||
audioOnlyEnabled = false,
|
||||
skipPrejoin = false,
|
||||
videoMuted = false
|
||||
} = options;
|
||||
|
||||
let url = config.tokenAuthUrl;
|
||||
|
||||
@@ -56,7 +73,17 @@ export const getTokenAuthUrl = (
|
||||
}
|
||||
|
||||
if (url.indexOf('{state}')) {
|
||||
const state = _getTokenAuthState(roomName, tenant, skipPrejoin, locationURL);
|
||||
const state = _getTokenAuthState(
|
||||
locationURL,
|
||||
{
|
||||
audioMuted,
|
||||
audioOnlyEnabled,
|
||||
skipPrejoin,
|
||||
videoMuted
|
||||
},
|
||||
roomName,
|
||||
tenant
|
||||
);
|
||||
|
||||
if (browser.isElectron()) {
|
||||
// @ts-ignore
|
||||
|
||||
@@ -13,7 +13,9 @@ import {
|
||||
JitsiConferenceErrors,
|
||||
JitsiConnectionErrors
|
||||
} from '../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../base/media/constants';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import { isLocalTrackMuted } from '../base/tracks/functions.any';
|
||||
import { parseURIString } from '../base/util/uri';
|
||||
import { openLogoutDialog } from '../settings/actions';
|
||||
|
||||
@@ -257,6 +259,9 @@ function _handleLogin({ dispatch, getState }: IStore) {
|
||||
const room = state['features/base/conference'].room;
|
||||
const { locationURL = { href: '' } as URL } = state['features/base/connection'];
|
||||
const { tenant } = parseURIString(locationURL.href) || {};
|
||||
const { enabled: audioOnlyEnabled } = state['features/base/audio-only'];
|
||||
const audioMuted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.AUDIO);
|
||||
const videoMuted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (!room) {
|
||||
logger.warn('Cannot handle login, room is undefined!');
|
||||
@@ -270,7 +275,18 @@ function _handleLogin({ dispatch, getState }: IStore) {
|
||||
return;
|
||||
}
|
||||
|
||||
getTokenAuthUrl(config, room, tenant, true, locationURL)
|
||||
getTokenAuthUrl(
|
||||
config,
|
||||
locationURL,
|
||||
{
|
||||
audioMuted,
|
||||
audioOnlyEnabled,
|
||||
skipPrejoin: true,
|
||||
videoMuted
|
||||
},
|
||||
room,
|
||||
tenant
|
||||
)
|
||||
.then((tokenAuthServiceUrl: string | undefined) => {
|
||||
if (!tokenAuthServiceUrl) {
|
||||
logger.warn('Cannot handle login, token service URL is not set');
|
||||
|
||||
@@ -90,6 +90,12 @@ export const HELP_BUTTON_ENABLED = 'help.enabled';
|
||||
*/
|
||||
export const INVITE_ENABLED = 'invite.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if dial-in invite functionality should be enabled.
|
||||
* Default: enabled (true).
|
||||
*/
|
||||
export const INVITE_DIAL_IN_ENABLED = 'invite-dial-in.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if recording should be enabled in iOS.
|
||||
* Default: disabled (false).
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
StatusBar
|
||||
StatusBar,
|
||||
ViewStyle
|
||||
} from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
@@ -38,9 +39,9 @@ interface IProps {
|
||||
hasBottomTextInput: boolean;
|
||||
|
||||
/**
|
||||
* Is the screen rendering a tab navigator?
|
||||
* Is the screen header having an extra height?
|
||||
*/
|
||||
hasTabNavigator: boolean;
|
||||
hasExtraHeaderHeight?: boolean;
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the KeyboardAvoidingView.
|
||||
@@ -54,8 +55,8 @@ const JitsiKeyboardAvoidingView = (
|
||||
children,
|
||||
contentContainerStyle,
|
||||
disableForcedKeyboardDismiss,
|
||||
hasTabNavigator,
|
||||
hasBottomTextInput,
|
||||
hasExtraHeaderHeight,
|
||||
style
|
||||
}: IProps) => {
|
||||
const headerHeight = useHeaderHeight();
|
||||
@@ -69,13 +70,13 @@ const JitsiKeyboardAvoidingView = (
|
||||
}, [ insets.bottom ]);
|
||||
|
||||
|
||||
const tabNavigatorPadding
|
||||
= hasTabNavigator ? headerHeight : 0;
|
||||
const extraHeaderHeight
|
||||
= hasExtraHeaderHeight ? headerHeight : 0;
|
||||
const extraBottomPadding
|
||||
= addBottomPadding ? bottomPadding : 0;
|
||||
const noNotchDevicePadding = extraBottomPadding || 10;
|
||||
const iosVerticalOffset
|
||||
= headerHeight + noNotchDevicePadding + tabNavigatorPadding;
|
||||
= headerHeight + noNotchDevicePadding + extraHeaderHeight;
|
||||
const androidVerticalOffset = hasBottomTextInput
|
||||
? headerHeight + Number(StatusBar.currentHeight) : headerHeight;
|
||||
|
||||
@@ -86,7 +87,7 @@ const JitsiKeyboardAvoidingView = (
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior = { Platform.OS === 'ios' ? 'padding' : 'height' }
|
||||
contentContainerStyle = { contentContainerStyle }
|
||||
contentContainerStyle = { contentContainerStyle as ViewStyle }
|
||||
enabled = { true }
|
||||
keyboardVerticalOffset = {
|
||||
Platform.OS === 'ios'
|
||||
@@ -95,7 +96,7 @@ const JitsiKeyboardAvoidingView = (
|
||||
}
|
||||
onResponderRelease = { onRelease }
|
||||
onStartShouldSetResponder = { shouldSetResponse }
|
||||
style = { style }>
|
||||
style = { style as ViewStyle }>
|
||||
{ children }
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
|
||||
@@ -40,9 +40,9 @@ interface IProps {
|
||||
hasBottomTextInput?: boolean;
|
||||
|
||||
/**
|
||||
* Is the screen rendering a tab navigator?
|
||||
* Is the screen header having an extra height?
|
||||
*/
|
||||
hasTabNavigator?: boolean;
|
||||
hasExtraHeaderHeight?: boolean;
|
||||
|
||||
/**
|
||||
* Insets for the SafeAreaView.
|
||||
@@ -61,8 +61,8 @@ const JitsiScreen = ({
|
||||
children,
|
||||
disableForcedKeyboardDismiss = false,
|
||||
footerComponent,
|
||||
hasTabNavigator = false,
|
||||
hasBottomTextInput = false,
|
||||
hasExtraHeaderHeight = false,
|
||||
safeAreaInsets = [ 'left', 'right' ],
|
||||
style
|
||||
}: IProps) => {
|
||||
@@ -72,7 +72,7 @@ const JitsiScreen = ({
|
||||
contentContainerStyle = { contentContainerStyle }
|
||||
disableForcedKeyboardDismiss = { disableForcedKeyboardDismiss }
|
||||
hasBottomTextInput = { hasBottomTextInput }
|
||||
hasTabNavigator = { hasTabNavigator }
|
||||
hasExtraHeaderHeight = { hasExtraHeaderHeight }
|
||||
style = { style }>
|
||||
<SafeAreaView
|
||||
edges = { safeAreaInsets }
|
||||
|
||||
@@ -11,7 +11,6 @@ const button = {
|
||||
|
||||
const buttonLabel = {
|
||||
...BaseTheme.typography.bodyShortBold,
|
||||
lineHeight: 14,
|
||||
textTransform: 'capitalize'
|
||||
};
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class Chat extends Component<IProps> {
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
}
|
||||
hasBottomTextInput = { true }
|
||||
hasTabNavigator = { true }
|
||||
hasExtraHeaderHeight = { true }
|
||||
style = { styles.chatContainer }>
|
||||
{/* @ts-ignore */}
|
||||
<MessageContainer messages = { _messages } />
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { connect, useDispatch } from 'react-redux';
|
||||
|
||||
import { appNavigate } from '../../../app/actions';
|
||||
import { appNavigate } from '../../../app/actions.native';
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { CONFERENCE_BLURRED, CONFERENCE_FOCUSED } from '../../../base/conference/actionTypes';
|
||||
import { FULLSCREEN_ENABLED, PIP_ENABLED } from '../../../base/flags/constants';
|
||||
@@ -41,15 +41,15 @@ import { navigate } from '../../../mobile/navigation/components/conference/Confe
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import { setPictureInPictureEnabled } from '../../../mobile/picture-in-picture/functions';
|
||||
import Captions from '../../../subtitles/components/native/Captions';
|
||||
import { setToolboxVisible } from '../../../toolbox/actions';
|
||||
import { setToolboxVisible } from '../../../toolbox/actions.native';
|
||||
import Toolbox from '../../../toolbox/components/native/Toolbox';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions.native';
|
||||
import {
|
||||
AbstractConference,
|
||||
abstractMapStateToProps
|
||||
} from '../AbstractConference';
|
||||
import type { AbstractProps } from '../AbstractConference';
|
||||
import { isConnecting } from '../functions';
|
||||
import { isConnecting } from '../functions.native';
|
||||
|
||||
import AlwaysOnLabels from './AlwaysOnLabels';
|
||||
import ExpandedLabelPopup from './ExpandedLabelPopup';
|
||||
@@ -230,7 +230,9 @@ class Conference extends AbstractConference<IProps, State> {
|
||||
*/
|
||||
componentDidUpdate(prevProps: IProps) {
|
||||
const {
|
||||
_showLobby
|
||||
_audioOnlyEnabled,
|
||||
_showLobby,
|
||||
_startCarMode
|
||||
} = this.props;
|
||||
|
||||
if (!prevProps._showLobby && _showLobby) {
|
||||
@@ -238,6 +240,10 @@ class Conference extends AbstractConference<IProps, State> {
|
||||
}
|
||||
|
||||
if (prevProps._showLobby && !_showLobby) {
|
||||
if (_audioOnlyEnabled && _startCarMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(screen.conference.main);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
showSharedVideoMenu
|
||||
} from '../../../participants-pane/actions.native';
|
||||
import { toggleToolboxVisible } from '../../../toolbox/actions.native';
|
||||
import { shouldDisplayTileView } from '../../../video-layout/functions.native';
|
||||
import { SQUARE_TILE_ASPECT_RATIO } from '../../constants';
|
||||
|
||||
import AudioMutedIndicator from './AudioMutedIndicator';
|
||||
@@ -43,6 +44,7 @@ import RaisedHandIndicator from './RaisedHandIndicator';
|
||||
import ScreenShareIndicator from './ScreenShareIndicator';
|
||||
import styles, { AVATAR_SIZE } from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* Thumbnail component's property types.
|
||||
*/
|
||||
@@ -111,6 +113,8 @@ interface IProps {
|
||||
*/
|
||||
_renderModeratorIndicator: boolean;
|
||||
|
||||
_shouldDisplayTileView: boolean;
|
||||
|
||||
/**
|
||||
* The video track that will be displayed in the thumbnail.
|
||||
*/
|
||||
@@ -206,14 +210,25 @@ class Thumbnail extends PureComponent<IProps> {
|
||||
_fakeParticipant,
|
||||
_isScreenShare: isScreenShare,
|
||||
_isVirtualScreenshare,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
_participantId: participantId,
|
||||
_pinned,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
_shouldDisplayTileView,
|
||||
renderDisplayName,
|
||||
tileView
|
||||
} = this.props;
|
||||
const indicators = [];
|
||||
|
||||
let bottomIndicatorsContainerStyle;
|
||||
|
||||
if (_shouldDisplayTileView) {
|
||||
bottomIndicatorsContainerStyle = styles.bottomIndicatorsContainer;
|
||||
} else if (audioMuted || renderModeratorIndicator) {
|
||||
bottomIndicatorsContainerStyle = styles.bottomIndicatorsContainer;
|
||||
} else {
|
||||
bottomIndicatorsContainerStyle = null;
|
||||
}
|
||||
|
||||
if (!_fakeParticipant || _isVirtualScreenshare) {
|
||||
indicators.push(<View
|
||||
key = 'top-left-indicators'
|
||||
@@ -228,10 +243,9 @@ class Thumbnail extends PureComponent<IProps> {
|
||||
</View>);
|
||||
indicators.push(<Container
|
||||
key = 'bottom-indicators'
|
||||
style = { styles.thumbnailIndicatorContainer }>
|
||||
style = { styles.thumbnailIndicatorContainer as StyleType }>
|
||||
<Container
|
||||
style = { ((audioMuted || renderModeratorIndicator) && styles.bottomIndicatorsContainer
|
||||
) as StyleType }>
|
||||
style = { bottomIndicatorsContainerStyle as StyleType }>
|
||||
{ audioMuted && !_isVirtualScreenshare && <AudioMutedIndicator /> }
|
||||
{ !tileView && _pinned && <PinnedIndicator />}
|
||||
{ renderModeratorIndicator && !_isVirtualScreenshare && <ModeratorIndicator />}
|
||||
@@ -422,6 +436,7 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
_raisedHand: hasRaisedHand(participant),
|
||||
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
_shouldDisplayTileView: shouldDisplayTileView(state),
|
||||
_videoTrack: videoTrack
|
||||
};
|
||||
}
|
||||
|
||||
@@ -144,8 +144,8 @@ export default {
|
||||
},
|
||||
|
||||
bottomIndicatorsContainer: {
|
||||
padding: 2,
|
||||
flexDirection: 'row'
|
||||
flexDirection: 'row',
|
||||
padding: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
thumbnailTopLeftIndicatorContainer: {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { WithTranslation } from 'react-i18next';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
SafeAreaView,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewStyle
|
||||
@@ -199,6 +200,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
return (
|
||||
<JitsiScreen
|
||||
footerComponent = { this._renderShareMeetingButton }
|
||||
hasExtraHeaderHeight = { true }
|
||||
style = { styles.addPeopleContainer }>
|
||||
<Input
|
||||
autoFocus = { false }
|
||||
@@ -497,7 +499,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
*/
|
||||
_renderShareMeetingButton() {
|
||||
return (
|
||||
<View
|
||||
<SafeAreaView
|
||||
style = { [
|
||||
styles.bottomBar as ViewStyle,
|
||||
this.state.bottomPadding ? styles.extraBarPadding : null
|
||||
@@ -508,7 +510,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
src = { IconShare }
|
||||
style = { styles.shareIcon } />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ export default {
|
||||
bottomBar: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: BaseTheme.palette.ui01,
|
||||
height: BaseTheme.spacing[10]
|
||||
backgroundColor: BaseTheme.palette.ui01
|
||||
},
|
||||
|
||||
clearButton: {
|
||||
|
||||
@@ -563,12 +563,13 @@ export function searchDirectory( // eslint-disable-line max-params
|
||||
* @param {IReduxState} state - The current state.
|
||||
* @param {string} inviteUrl - The conference/location URL.
|
||||
* @param {boolean} useHtml - Whether to return html text.
|
||||
* @param {boolean} skipDialIn - Whether to skip dial-in options or not.
|
||||
* @returns {Promise<string>} A {@code Promise} resolving with a
|
||||
* descriptive text that can be used to invite participants to a meeting.
|
||||
*/
|
||||
export function getShareInfoText(state: IReduxState, inviteUrl: string, useHtml?: boolean): Promise<string> {
|
||||
export function getShareInfoText(
|
||||
state: IReduxState, inviteUrl: string, useHtml?: boolean, skipDialIn?: boolean): Promise<string> {
|
||||
let roomUrl = _decodeRoomURI(inviteUrl);
|
||||
const includeDialInfo = state['features/base/config'] !== undefined;
|
||||
|
||||
if (useHtml) {
|
||||
roomUrl = `<a href="${roomUrl}">${roomUrl}</a>`;
|
||||
@@ -576,85 +577,72 @@ export function getShareInfoText(state: IReduxState, inviteUrl: string, useHtml?
|
||||
|
||||
let infoText = i18next.t('share.mainText', { roomUrl });
|
||||
|
||||
if (includeDialInfo) {
|
||||
const { room } = parseURIString(inviteUrl);
|
||||
let numbersPromise;
|
||||
let hasPaymentError = false;
|
||||
const { room } = parseURIString(inviteUrl);
|
||||
const { dialInConfCodeUrl, dialInNumbersUrl, hosts } = state['features/base/config'];
|
||||
const { locationURL = {} } = state['features/base/connection'];
|
||||
const mucURL = hosts?.muc;
|
||||
|
||||
if (state['features/invite'].numbers
|
||||
&& state['features/invite'].conferenceID) {
|
||||
numbersPromise = Promise.resolve(state['features/invite']);
|
||||
} else {
|
||||
// we are requesting numbers and conferenceId directly
|
||||
// not using updateDialInNumbers, because custom room
|
||||
// is specified and we do not want to store the data
|
||||
// in the state
|
||||
const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
|
||||
= state['features/base/config'];
|
||||
const { locationURL = {} } = state['features/base/connection'];
|
||||
const mucURL = hosts?.muc;
|
||||
|
||||
if (!dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
|
||||
// URLs for fetching dial in numbers not defined
|
||||
return Promise.resolve(infoText);
|
||||
}
|
||||
|
||||
numbersPromise = Promise.all([
|
||||
getDialInNumbers(dialInNumbersUrl, room, mucURL), // @ts-ignore
|
||||
getDialInConferenceID(dialInConfCodeUrl, room, mucURL, locationURL)
|
||||
]).then(([ numbers, {
|
||||
conference, id, message } ]) => {
|
||||
|
||||
if (!conference || !id) {
|
||||
return Promise.reject(message);
|
||||
}
|
||||
|
||||
return {
|
||||
numbers,
|
||||
conferenceID: id
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return numbersPromise.then(
|
||||
({ conferenceID, numbers }) => {
|
||||
const phoneNumber = _getDefaultPhoneNumber(numbers) || '';
|
||||
|
||||
return `${
|
||||
i18next.t('info.dialInNumber')} ${
|
||||
phoneNumber} ${
|
||||
i18next.t('info.dialInConferenceID')} ${
|
||||
conferenceID}#\n\n`;
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error fetching numbers or conferenceID', error);
|
||||
hasPaymentError = error?.status === StatusCode.PaymentRequired;
|
||||
})
|
||||
.then(defaultDialInNumber => {
|
||||
if (hasPaymentError) {
|
||||
infoText += `${
|
||||
i18next.t('info.dialInNumber')} ${i18next.t('info.reachedLimit')} ${
|
||||
i18next.t('info.upgradeOptions')} ${UPGRADE_OPTIONS_TEXT}`;
|
||||
|
||||
return infoText;
|
||||
}
|
||||
|
||||
let dialInfoPageUrl = getDialInfoPageURL(state, room);
|
||||
|
||||
if (useHtml) {
|
||||
dialInfoPageUrl
|
||||
= `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`;
|
||||
}
|
||||
|
||||
infoText += i18next.t('share.dialInfoText', {
|
||||
defaultDialInNumber,
|
||||
dialInfoPageUrl });
|
||||
|
||||
return infoText;
|
||||
});
|
||||
if (skipDialIn || !dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
|
||||
// URLs for fetching dial in numbers not defined.
|
||||
return Promise.resolve(infoText);
|
||||
}
|
||||
|
||||
return Promise.resolve(infoText);
|
||||
let hasPaymentError = false;
|
||||
|
||||
// We are requesting numbers and conferenceId directly
|
||||
// not using updateDialInNumbers, because custom room
|
||||
// is specified and we do not want to store the data
|
||||
// in the state.
|
||||
const numbersPromise = Promise.all([
|
||||
getDialInNumbers(dialInNumbersUrl, room, mucURL), // @ts-ignore
|
||||
getDialInConferenceID(dialInConfCodeUrl, room, mucURL, locationURL)
|
||||
]).then(([ numbers, {
|
||||
conference, id, message } ]) => {
|
||||
|
||||
if (!conference || !id) {
|
||||
return Promise.reject(message);
|
||||
}
|
||||
|
||||
return {
|
||||
numbers,
|
||||
conferenceID: id
|
||||
};
|
||||
});
|
||||
|
||||
return numbersPromise.then(({ conferenceID, numbers }) => {
|
||||
const phoneNumber = _getDefaultPhoneNumber(numbers) || '';
|
||||
|
||||
return `${
|
||||
i18next.t('info.dialInNumber')} ${
|
||||
phoneNumber} ${
|
||||
i18next.t('info.dialInConferenceID')} ${
|
||||
conferenceID}#\n\n`;
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error fetching numbers or conferenceID', error);
|
||||
hasPaymentError = error?.status === StatusCode.PaymentRequired;
|
||||
})
|
||||
.then(defaultDialInNumber => {
|
||||
if (hasPaymentError) {
|
||||
infoText += `${
|
||||
i18next.t('info.dialInNumber')} ${i18next.t('info.reachedLimit')} ${
|
||||
i18next.t('info.upgradeOptions')} ${UPGRADE_OPTIONS_TEXT}`;
|
||||
|
||||
return infoText;
|
||||
}
|
||||
|
||||
let dialInfoPageUrl = getDialInfoPageURL(state, room);
|
||||
|
||||
if (useHtml) {
|
||||
dialInfoPageUrl = `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`;
|
||||
}
|
||||
|
||||
infoText += i18next.t('share.dialInfoText', {
|
||||
defaultDialInNumber,
|
||||
dialInfoPageUrl });
|
||||
|
||||
return infoText;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,10 @@ class LobbyChatScreen extends
|
||||
const { _lobbyChatMessages } = this.props;
|
||||
|
||||
return (
|
||||
<JitsiScreen style = { styles.lobbyChatWrapper }>
|
||||
<JitsiScreen
|
||||
hasBottomTextInput = { true }
|
||||
hasExtraHeaderHeight = { true }
|
||||
style = { styles.lobbyChatWrapper }>
|
||||
{/* @ts-ignore */}
|
||||
<MessageContainer messages = { _lobbyChatMessages } />
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
|
||||
@@ -4,10 +4,7 @@ export default {
|
||||
|
||||
lobbyChatWrapper: {
|
||||
backgroundColor: BaseTheme.palette.ui01,
|
||||
alignItems: 'stretch',
|
||||
flexDirection: 'column',
|
||||
justifyItems: 'center',
|
||||
height: '100%'
|
||||
flex: 1
|
||||
},
|
||||
|
||||
passwordJoinButtons: {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NativeModules } from 'react-native';
|
||||
|
||||
|
||||
/**
|
||||
* Determimes if the ExternalAPI native module is available.
|
||||
* Determines if the ExternalAPI native module is available.
|
||||
*
|
||||
* @returns {boolean} If yes {@code true} otherwise {@code false}.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
|
||||
import { getAppProp } from '../../base/app/functions';
|
||||
import {
|
||||
CONFERENCE_BLURRED,
|
||||
@@ -6,8 +8,10 @@ import {
|
||||
CONFERENCE_LEFT,
|
||||
CONFERENCE_WILL_JOIN
|
||||
} from '../../base/conference/actionTypes';
|
||||
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../../base/media/actionTypes';
|
||||
import { PARTICIPANT_JOINED } from '../../base/participants/actionTypes';
|
||||
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../../base/redux/StateListenerRegistry';
|
||||
import { READY_TO_CLOSE } from '../external-api/actionTypes';
|
||||
import { participantToParticipantInfo } from '../external-api/functions';
|
||||
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture/actionTypes';
|
||||
@@ -15,9 +19,12 @@ import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture/actionTypes';
|
||||
import { isExternalAPIAvailable } from './functions';
|
||||
|
||||
const externalAPIEnabled = isExternalAPIAvailable();
|
||||
const { JMOngoingConference } = NativeModules;
|
||||
|
||||
|
||||
/**
|
||||
* Check if native modules are being used or not. If not then the init of middleware doesn't happen.
|
||||
* Check if native modules are being used or not.
|
||||
* If not, then the init of middleware doesn't happen.
|
||||
*/
|
||||
!externalAPIEnabled && MiddlewareRegistry.register(store => next => action => {
|
||||
const result = next(action);
|
||||
@@ -25,6 +32,12 @@ const externalAPIEnabled = isExternalAPIAvailable();
|
||||
const rnSdkHandlers = getAppProp(store, 'rnSdkHandlers');
|
||||
|
||||
switch (type) {
|
||||
case SET_AUDIO_MUTED:
|
||||
rnSdkHandlers?.onAudioMutedChanged && rnSdkHandlers?.onAudioMutedChanged(action.muted);
|
||||
break;
|
||||
case SET_VIDEO_MUTED:
|
||||
rnSdkHandlers?.onVideoMutedChanged && rnSdkHandlers?.onVideoMutedChanged(Boolean(action.muted));
|
||||
break;
|
||||
case CONFERENCE_BLURRED:
|
||||
rnSdkHandlers?.onConferenceBlurred && rnSdkHandlers?.onConferenceBlurred();
|
||||
break;
|
||||
@@ -56,5 +69,22 @@ const externalAPIEnabled = isExternalAPIAvailable();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Before enabling media projection service control on Android,
|
||||
* we need to check if native modules are being used or not.
|
||||
*/
|
||||
Platform.OS === 'android' && !externalAPIEnabled && StateListenerRegistry.register(
|
||||
state => state['features/base/conference'].conference,
|
||||
(conference, previousConference) => {
|
||||
if (!conference) {
|
||||
JMOngoingConference.abort();
|
||||
} else if (conference && !previousConference) {
|
||||
JMOngoingConference.launch();
|
||||
} else if (conference !== previousConference) {
|
||||
JMOngoingConference.abort();
|
||||
JMOngoingConference.launch();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -288,6 +288,6 @@ export default {
|
||||
visitorsLabel: {
|
||||
...BaseTheme.typography.heading6,
|
||||
color: BaseTheme.palette.warning02,
|
||||
marginLeft: BaseTheme.spacing[3]
|
||||
marginLeft: BaseTheme.spacing[2]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { FlatList, Text, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import AbstractPollResults from '../AbstractPollResults';
|
||||
@@ -20,6 +18,7 @@ const PollResults = (props: AbstractProps) => {
|
||||
const {
|
||||
answers,
|
||||
changeVote,
|
||||
creatorName,
|
||||
haveVoted,
|
||||
question,
|
||||
showDetails,
|
||||
@@ -88,7 +87,6 @@ const PollResults = (props: AbstractProps) => {
|
||||
);
|
||||
|
||||
}, [ showDetails ]);
|
||||
const localParticipant = useSelector(getLocalParticipant);
|
||||
|
||||
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
@@ -96,7 +94,7 @@ const PollResults = (props: AbstractProps) => {
|
||||
<View>
|
||||
<Text style = { dialogStyles.questionText as TextStyle } >{ question }</Text>
|
||||
<Text style = { dialogStyles.questionOwnerText as TextStyle } >
|
||||
{ t('polls.by', { name: localParticipant?.name }) }
|
||||
{ t('polls.by', { name: creatorName }) }
|
||||
</Text>
|
||||
<FlatList
|
||||
data = { answers }
|
||||
|
||||
@@ -48,7 +48,7 @@ const PollsPane = (props: AbstractProps) => {
|
||||
<JitsiScreen
|
||||
contentContainerStyle = { chatStyles.pollPane as StyleType }
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
hasTabNavigator = { true }
|
||||
hasExtraHeaderHeight = { true }
|
||||
style = { chatStyles.pollPaneContainer as StyleType }>
|
||||
{
|
||||
createMode
|
||||
|
||||
@@ -83,7 +83,8 @@ function _appWillMount({ dispatch, getState }: IStore, next: Function, action: A
|
||||
* @returns {*} The result returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceWillLeave({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { doNotStoreRoom } = getState()['features/base/config'];
|
||||
const state = getState();
|
||||
const { doNotStoreRoom } = state['features/base/config'];
|
||||
|
||||
if (!doNotStoreRoom && !inIframe()) {
|
||||
let locationURL;
|
||||
@@ -100,13 +101,17 @@ function _conferenceWillLeave({ dispatch, getState }: IStore, next: Function, ac
|
||||
* JITSI_CONFERENCE_URL_KEY so we cannot call it and must use the other way.
|
||||
*/
|
||||
if (typeof APP === 'undefined') {
|
||||
locationURL = action.conference[JITSI_CONFERENCE_URL_KEY];
|
||||
const { conference } = action;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
||||
locationURL = conference && conference[JITSI_CONFERENCE_URL_KEY];
|
||||
} else {
|
||||
locationURL = getState()['features/base/connection'].locationURL;
|
||||
locationURL = state['features/base/connection'].locationURL;
|
||||
}
|
||||
dispatch(
|
||||
_updateConferenceDuration(
|
||||
locationURL));
|
||||
locationURL
|
||||
));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -17,6 +17,11 @@ interface IProps {
|
||||
*/
|
||||
_password?: string;
|
||||
|
||||
/**
|
||||
* Number of digits used in the room-lock password.
|
||||
*/
|
||||
_passwordNumberOfDigits?: number;
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} which requires a password.
|
||||
*
|
||||
@@ -88,6 +93,15 @@ class PasswordRequiredPrompt extends Component<IProps, IState> {
|
||||
*/
|
||||
render() {
|
||||
const { password } = this.state;
|
||||
const { _passwordNumberOfDigits } = this.props;
|
||||
const textInputProps: any = {
|
||||
secureTextEntry: true
|
||||
};
|
||||
|
||||
if (_passwordNumberOfDigits) {
|
||||
textInputProps.keyboardType = 'numeric';
|
||||
textInputProps.maxLength = _passwordNumberOfDigits;
|
||||
}
|
||||
|
||||
return (
|
||||
<InputDialog
|
||||
@@ -96,9 +110,7 @@ class PasswordRequiredPrompt extends Component<IProps, IState> {
|
||||
messageKey = { password ? 'dialog.incorrectRoomLockPassword' : undefined }
|
||||
onCancel = { this._onCancel }
|
||||
onSubmit = { this._onSubmit }
|
||||
textInputProps = {{
|
||||
secureTextEntry: true
|
||||
}}
|
||||
textInputProps = { textInputProps }
|
||||
titleKey = 'dialog.password' />
|
||||
);
|
||||
}
|
||||
@@ -142,8 +154,11 @@ class PasswordRequiredPrompt extends Component<IProps, IState> {
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { roomPasswordNumberOfDigits } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_password: state['features/base/conference'].password
|
||||
_password: state['features/base/conference'].password,
|
||||
_passwordNumberOfDigits: roomPasswordNumberOfDigits
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ interface IProps {
|
||||
_isModerator: boolean;
|
||||
|
||||
/**
|
||||
* State of the lobby mode.
|
||||
* Whether lobby mode is enabled or not.
|
||||
*/
|
||||
_lobbyEnabled: boolean;
|
||||
|
||||
@@ -107,6 +107,11 @@ interface IProps {
|
||||
*/
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* State of lobby mode.
|
||||
*/
|
||||
lobbyEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Password added by the participant for room lock.
|
||||
*/
|
||||
@@ -134,6 +139,7 @@ class SecurityDialog extends PureComponent<IProps, IState> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
lobbyEnabled: props._lobbyEnabled,
|
||||
passwordInputValue: '',
|
||||
showElement: props._locked === LOCKED_LOCALLY || false
|
||||
};
|
||||
@@ -168,7 +174,6 @@ class SecurityDialog extends PureComponent<IProps, IState> {
|
||||
*/
|
||||
_renderLobbyMode() {
|
||||
const {
|
||||
_lobbyEnabled,
|
||||
_lobbyModeSwitchVisible,
|
||||
t
|
||||
} = this.props;
|
||||
@@ -188,7 +193,7 @@ class SecurityDialog extends PureComponent<IProps, IState> {
|
||||
{ t('lobby.toggleLabel') }
|
||||
</Text>
|
||||
<Switch
|
||||
checked = { _lobbyEnabled }
|
||||
checked = { this.state.lobbyEnabled }
|
||||
onChange = { this._onToggleLobbyMode } />
|
||||
</View>
|
||||
</View>
|
||||
@@ -386,13 +391,14 @@ class SecurityDialog extends PureComponent<IProps, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleLobbyMode() {
|
||||
const { _lobbyEnabled, dispatch } = this.props;
|
||||
const { dispatch } = this.props;
|
||||
const { lobbyEnabled } = this.state;
|
||||
|
||||
if (_lobbyEnabled) {
|
||||
dispatch(toggleLobbyMode(false));
|
||||
} else {
|
||||
dispatch(toggleLobbyMode(true));
|
||||
}
|
||||
this.setState({
|
||||
lobbyEnabled: !lobbyEnabled
|
||||
});
|
||||
|
||||
dispatch(toggleLobbyMode(!lobbyEnabled));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IReduxState } from '../../../app/types';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions.any';
|
||||
import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions.native';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
@@ -17,7 +17,6 @@ import styles from './styles';
|
||||
const ConferenceSection = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const defaultServerURL = useSelector((state: IReduxState) => getDefaultURL(state));
|
||||
const {
|
||||
serverURL,
|
||||
startCarMode,
|
||||
@@ -25,7 +24,10 @@ const ConferenceSection = () => {
|
||||
startWithVideoMuted
|
||||
} = useSelector((state: IReduxState) => state['features/base/settings']);
|
||||
|
||||
const { serverURLChangeEnabled } = useSelector((state: IReduxState) => isServerURLChangeEnabled(state));
|
||||
const defaultServerURL = useSelector((state: IReduxState) => getDefaultURL(state));
|
||||
const [ newServerURL, setNewServerURL ] = useState(serverURL ?? '');
|
||||
|
||||
const serverURLChangeEnabled = useSelector((state: IReduxState) => isServerURLChangeEnabled(state));
|
||||
|
||||
const switches = useMemo(() => [
|
||||
{
|
||||
@@ -45,21 +47,27 @@ const ConferenceSection = () => {
|
||||
}
|
||||
], [ startCarMode, startWithAudioMuted, startWithVideoMuted ]);
|
||||
|
||||
const onChangeServerURL = useCallback(newServerURL => {
|
||||
dispatch(updateSettings({ serverURL: newServerURL }));
|
||||
}, [ updateSettings ]);
|
||||
const onChangeServerURL = useCallback(value => {
|
||||
|
||||
setNewServerURL(value);
|
||||
dispatch(updateSettings({
|
||||
serverURL: value
|
||||
}));
|
||||
}, [ dispatch, newServerURL ]);
|
||||
|
||||
const processServerURL = useCallback(() => {
|
||||
const normalizedURL = normalizeUserInputURL(serverURL ?? '');
|
||||
const normalizedURL = normalizeUserInputURL(newServerURL);
|
||||
|
||||
onChangeServerURL(normalizedURL);
|
||||
}, [ serverURL ]);
|
||||
}, [ newServerURL ]);
|
||||
|
||||
useEffect(() => () => processServerURL(), []);
|
||||
|
||||
const onSwitchToggled = useCallback((name: string) => (enabled?: boolean) => {
|
||||
|
||||
// @ts-ignore
|
||||
dispatch(updateSettings({ [name]: enabled }));
|
||||
}, [ dispatch, updateSettings ]);
|
||||
}, [ dispatch ]);
|
||||
|
||||
return (
|
||||
<FormSection
|
||||
@@ -74,7 +82,7 @@ const ConferenceSection = () => {
|
||||
onChange = { onChangeServerURL }
|
||||
placeholder = { defaultServerURL }
|
||||
textContentType = { 'URL' } // iOS only
|
||||
value = { serverURL ?? '' } />
|
||||
value = { newServerURL } />
|
||||
{
|
||||
switches.map(({ label, state, name }) => (
|
||||
<FormRow
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ScrollView, Text, View, ViewStyle } from 'react-native';
|
||||
import { Edge } from 'react-native-safe-area-context';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
@@ -39,6 +40,7 @@ const ProfileView = ({ isInWelcomePage }: {
|
||||
(state: IReduxState) => state['features/base/settings']
|
||||
);
|
||||
const participant = useSelector((state: IReduxState) => getLocalParticipant(state));
|
||||
const { locationURL } = useSelector((state: IReduxState) => state['features/base/connection']);
|
||||
|
||||
const [ displayName, setDisplayName ] = useState(reduxDisplayName);
|
||||
const [ email, setEmail ] = useState(reduxEmail);
|
||||
@@ -103,7 +105,9 @@ const ProfileView = ({ isInWelcomePage }: {
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft,
|
||||
headerRight: !isInWelcomePage && headerRight
|
||||
headerRight: !isInWelcomePage
|
||||
&& !locationURL?.hostname?.includes('8x8.vc')
|
||||
&& headerRight
|
||||
});
|
||||
}, [ navigation ]);
|
||||
|
||||
@@ -112,7 +116,7 @@ const ProfileView = ({ isInWelcomePage }: {
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
|
||||
// @ts-ignore
|
||||
safeAreaInsets = { [ !isInWelcomePage && 'bottom', 'left', 'right' ].filter(Boolean) }
|
||||
safeAreaInsets = { [ !isInWelcomePage && 'bottom', 'left', 'right' ].filter(Boolean) as Edge[] }
|
||||
style = { styles.settingsViewContainer }>
|
||||
<ScrollView
|
||||
bounces = { isInWelcomePage }
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Share } from 'react-native';
|
||||
|
||||
import { getName } from '../app/functions.native';
|
||||
import { IStore } from '../app/types';
|
||||
import { INVITE_DIAL_IN_ENABLED } from '../base/flags/constants';
|
||||
import { getFeatureFlag } from '../base/flags/functions';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import { getShareInfoText } from '../invite/functions';
|
||||
|
||||
@@ -35,7 +37,9 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @returns {void}
|
||||
*/
|
||||
function _shareRoom(roomURL: string, { dispatch, getState }: IStore) {
|
||||
getShareInfoText(getState(), roomURL)
|
||||
const dialInEnabled = getFeatureFlag(getState(), INVITE_DIAL_IN_ENABLED, true);
|
||||
|
||||
getShareInfoText(getState(), roomURL, false /* useHtml */, !dialInEnabled /* skipDialIn */)
|
||||
.then(message => {
|
||||
const title = `${getName()} Conference`;
|
||||
const onFulfilled
|
||||
@@ -53,14 +57,15 @@ function _shareRoom(roomURL: string, { dispatch, getState }: IStore) {
|
||||
.then(
|
||||
/* onFulfilled */ value => {
|
||||
onFulfilled(value.action === Share.sharedAction);
|
||||
dispatch(toggleShareDialog(false));
|
||||
},
|
||||
/* onRejected */ reason => {
|
||||
dispatch(toggleShareDialog(false));
|
||||
logger.error(
|
||||
`Failed to share conference/room URL ${roomURL}:`,
|
||||
reason);
|
||||
onFulfilled(false);
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch(toggleShareDialog(false));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user