mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-02 23:47:45 +00:00
Compare commits
56 Commits
vtesting1.
...
drop-avata
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b5e9c0b50 | ||
|
|
ec22c1fdda | ||
|
|
3441954f8b | ||
|
|
b0a87041da | ||
|
|
42586be533 | ||
|
|
65e94bd173 | ||
|
|
73c836fafb | ||
|
|
d6fa066e4d | ||
|
|
22bbf4939e | ||
|
|
ca195fd708 | ||
|
|
c3c0166731 | ||
|
|
fc94854b72 | ||
|
|
c55eb68cf2 | ||
|
|
80e08a112a | ||
|
|
896fc29af7 | ||
|
|
c88ffab370 | ||
|
|
445515da93 | ||
|
|
f5dbd6780b | ||
|
|
ba06121464 | ||
|
|
7115919206 | ||
|
|
1d95c30893 | ||
|
|
a5cd5e2733 | ||
|
|
8732675162 | ||
|
|
a074437d99 | ||
|
|
d2eb11fa5c | ||
|
|
b35200648c | ||
|
|
a8958019a5 | ||
|
|
200228339b | ||
|
|
ddc64ad687 | ||
|
|
2d2bae6ec1 | ||
|
|
fa6dc292c1 | ||
|
|
b5ac40b32a | ||
|
|
8299aa498b | ||
|
|
ce22adfe64 | ||
|
|
01bcccdd99 | ||
|
|
673a54adb1 | ||
|
|
f48c03e314 | ||
|
|
7641ddad68 | ||
|
|
26021b2dd0 | ||
|
|
a6457db819 | ||
|
|
8bfa65987d | ||
|
|
b1c0cc5322 | ||
|
|
67cbef0d7a | ||
|
|
b2e06c1c94 | ||
|
|
70a05e487e | ||
|
|
c2a446a79b | ||
|
|
56df23af35 | ||
|
|
1c12f5cf2a | ||
|
|
06b727ae1a | ||
|
|
a2aca30d57 | ||
|
|
68106feb57 | ||
|
|
3066fc1d31 | ||
|
|
4834fb7b6f | ||
|
|
1e101afe5a | ||
|
|
4d79bbb5d8 | ||
|
|
e31aff5afd |
4
Makefile
4
Makefile
@@ -24,9 +24,9 @@ else
|
||||
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
|
||||
endif
|
||||
|
||||
all: compile deploy clean
|
||||
all: compile deploy
|
||||
|
||||
compile:
|
||||
compile: clean
|
||||
NODE_OPTIONS=--max-old-space-size=8192 \
|
||||
$(WEBPACK)
|
||||
|
||||
|
||||
@@ -211,11 +211,6 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
|
||||
|
||||
Log.d(TAG, "Is in picture-in-picture mode: " + isInPictureInPictureMode);
|
||||
|
||||
if (!isInPictureInPictureMode) {
|
||||
this.startActivity(new Intent(this, getClass())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
@@ -10,7 +10,6 @@ MVN_HTTP=0
|
||||
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
|
||||
if [[ $THE_MVN_REPO == http* ]]; then
|
||||
MVN_HTTP=1
|
||||
@@ -67,15 +66,12 @@ pushd ${THIS_DIR}/../
|
||||
./gradlew publish
|
||||
popd
|
||||
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
# The artifacts are now on the Maven repo, commit them
|
||||
# The artifacts are now on the Maven repo, commit them
|
||||
if [[ $MVN_HTTP == 0 ]]; then
|
||||
pushd ${MVN_REPO_PATH}
|
||||
git add -A .
|
||||
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
|
||||
popd
|
||||
|
||||
# Tag the release
|
||||
git tag android-sdk-${SDK_VERSION}
|
||||
fi
|
||||
|
||||
# Done!
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
|
||||
exec ${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command --reset-cache
|
||||
exec ${THIS_DIR}/../../node_modules/react-native/scripts/packager.sh --reset-cache
|
||||
|
||||
@@ -54,6 +54,8 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
private static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
|
||||
private static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
|
||||
|
||||
private boolean isReadyToClose;
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -124,6 +126,8 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
JitsiMeetLogger.i("onDestroy()");
|
||||
|
||||
// Here we are trying to handle the following corner case: an application using the SDK
|
||||
// is using this Activity for displaying meetings, but there is another "main" Activity
|
||||
// with other content. If this Activity is "swiped out" from the recent list we will get
|
||||
@@ -131,7 +135,10 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
// current meeting, but when our view is detached from React the JS <-> Native bridge won't
|
||||
// be operational so the external API won't be able to notify the native side that the
|
||||
// conference terminated. Thus, try our best to clean up.
|
||||
leave();
|
||||
if (!isReadyToClose) {
|
||||
JitsiMeetLogger.i("onDestroy(): leaving...");
|
||||
leave();
|
||||
}
|
||||
|
||||
this.jitsiView = null;
|
||||
|
||||
@@ -149,8 +156,12 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
leave();
|
||||
if (!isReadyToClose) {
|
||||
JitsiMeetLogger.i("finish(): leaving...");
|
||||
leave();
|
||||
}
|
||||
|
||||
JitsiMeetLogger.i("finish(): finishing...");
|
||||
super.finish();
|
||||
}
|
||||
|
||||
@@ -170,8 +181,8 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
public void join(JitsiMeetConferenceOptions options) {
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView .join(options);
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView.join(options);
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot join, view is null");
|
||||
}
|
||||
@@ -252,6 +263,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
|
||||
protected void onReadyToClose() {
|
||||
JitsiMeetLogger.i("SDK is ready to close");
|
||||
isReadyToClose = true;
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -270,11 +270,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
Bundle asProps() {
|
||||
Bundle props = new Bundle();
|
||||
|
||||
// Android always has the PiP flag set by default.
|
||||
if (!featureFlags.containsKey("pip.enabled")) {
|
||||
featureFlags.putBoolean("pip.enabled", true);
|
||||
}
|
||||
|
||||
props.putBundle("flags", featureFlags);
|
||||
|
||||
Bundle urlProps = new Bundle();
|
||||
|
||||
@@ -51,30 +51,34 @@ import java.util.Random;
|
||||
*
|
||||
* See: https://developer.android.com/guide/components/services
|
||||
*/
|
||||
public class JitsiMeetOngoingConferenceService extends Service
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
public class JitsiMeetOngoingConferenceService extends Service implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
||||
private static final String ACTIVITY_DATA_KEY = "activityDataKey";
|
||||
private static final String EXTRA_DATA_KEY = "extraDataKey";
|
||||
private static final String EXTRA_DATA_BUNDLE_KEY = "extraDataBundleKey";
|
||||
private static final String IS_AUDIO_MUTED_KEY = "isAudioMuted";
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
||||
|
||||
private static final int PERMISSIONS_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
||||
|
||||
private boolean isAudioMuted;
|
||||
private Class tapBackActivity;
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
|
||||
private static void doLaunch(Context context, HashMap<String, Object> extraData) {
|
||||
Activity activity = (Activity) context;
|
||||
|
||||
OngoingNotification.createNotificationChannel((Activity) context);
|
||||
OngoingNotification.createNotificationChannel(activity);
|
||||
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
|
||||
Bundle extraDataBundle = new Bundle();
|
||||
extraDataBundle.putSerializable(EXTRA_DATA_KEY, extraData);
|
||||
|
||||
intent.putExtra(EXTRA_DATA_BUNDLE_KEY, extraDataBundle);
|
||||
intent.putExtra(ACTIVITY_DATA_KEY, activity.getClass().getCanonicalName());
|
||||
|
||||
ComponentName componentName;
|
||||
|
||||
@@ -154,7 +158,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this);
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this, tapBackActivity);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
@@ -190,13 +194,28 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final String actionName = intent.getAction();
|
||||
final Action action = Action.fromName(actionName);
|
||||
|
||||
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
|
||||
if (action != Action.HANGUP) {
|
||||
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
|
||||
|
||||
if (isAudioMuted != null) {
|
||||
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
||||
if (isAudioMuted != null) {
|
||||
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
||||
}
|
||||
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this);
|
||||
if (tapBackActivity == null) {
|
||||
String targetActivityName = intent.getExtras().getString(ACTIVITY_DATA_KEY);
|
||||
Class<? extends Activity> targetActivity = null;
|
||||
try {
|
||||
targetActivity = Class.forName(targetActivityName).asSubclass(Activity.class);
|
||||
tapBackActivity = targetActivity;
|
||||
} catch (ClassNotFoundException e) {
|
||||
JitsiMeetLogger.w(TAG + " Could not find target Activity: " + targetActivityName);
|
||||
}
|
||||
}
|
||||
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this, tapBackActivity);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
@@ -206,9 +225,6 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
final String actionName = intent.getAction();
|
||||
final Action action = Action.fromName(actionName);
|
||||
|
||||
// When starting the service, there is no action passed in the intent
|
||||
if (action != null) {
|
||||
switch (action) {
|
||||
@@ -281,8 +297,9 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Class tapBackActivity = JitsiMeetOngoingConferenceService.this.tapBackActivity;
|
||||
isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, context);
|
||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, context, tapBackActivity);
|
||||
if (notification == null) {
|
||||
stopSelf();
|
||||
JitsiMeetLogger.w(TAG + " Couldn't update service, notification is null");
|
||||
|
||||
@@ -73,14 +73,13 @@ class OngoingNotification {
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context) {
|
||||
|
||||
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context, Class tapBackActivity) {
|
||||
if (context == null) {
|
||||
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
|
||||
return null;
|
||||
}
|
||||
|
||||
Intent notificationIntent = new Intent(context, context.getClass());
|
||||
Intent notificationIntent = new Intent(context, tapBackActivity == null ? context.getClass() : tapBackActivity);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
|
||||
|
||||
@@ -205,18 +205,6 @@ class ReactInstanceManagerHolder {
|
||||
? reactContext.getNativeModule(nativeModuleClass) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current {@link Activity} linked to React Native.
|
||||
*
|
||||
* @return An activity attached to React Native.
|
||||
*/
|
||||
static Activity getCurrentActivity() {
|
||||
ReactContext reactContext
|
||||
= reactInstanceManager != null
|
||||
? reactInstanceManager.getCurrentReactContext() : null;
|
||||
return reactContext != null ? reactContext.getCurrentActivity() : null;
|
||||
}
|
||||
|
||||
static ReactInstanceManager getReactInstanceManager() {
|
||||
return reactInstanceManager;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import {
|
||||
p2pStatusChanged
|
||||
} from './react/features/base/conference/actions';
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
CONFERENCE_LEAVE_REASONS,
|
||||
EMAIL_COMMAND
|
||||
} from './react/features/base/conference/constants';
|
||||
@@ -182,7 +181,6 @@ window.JitsiMeetScreenObtainer = {
|
||||
* Known custom conference commands.
|
||||
*/
|
||||
const commands = {
|
||||
AVATAR_URL: AVATAR_URL_COMMAND,
|
||||
CUSTOM_ROLE: 'custom-role',
|
||||
EMAIL: EMAIL_COMMAND,
|
||||
ETHERPAD: 'etherpad'
|
||||
@@ -1772,17 +1770,6 @@ export default {
|
||||
}));
|
||||
});
|
||||
|
||||
room.addCommandListener(
|
||||
this.commands.defaults.AVATAR_URL,
|
||||
(data, from) => {
|
||||
APP.store.dispatch(
|
||||
participantUpdated({
|
||||
conference: room,
|
||||
id: from,
|
||||
avatarURL: data.value
|
||||
}));
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
|
||||
({ audio, video }) => {
|
||||
|
||||
32
config.js
32
config.js
@@ -232,9 +232,26 @@ var config = {
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 720.
|
||||
// resolution: 720,
|
||||
|
||||
// DEPRECATED. Please use raisedHands.disableRemoveRaisedHandOnFocus instead.
|
||||
// Specifies whether the raised hand will hide when someone becomes a dominant speaker or not
|
||||
// disableRemoveRaisedHandOnFocus: false,
|
||||
|
||||
// Specifies which raised hand related config should be set.
|
||||
// raisedHands: {
|
||||
// // Specifies whether the raised hand can be lowered by moderator.
|
||||
// disableLowerHandByModerator: false,
|
||||
|
||||
// // Specifies whether there is a notification before hiding the raised hand
|
||||
// // when someone becomes the dominant speaker.
|
||||
// disableLowerHandNotification: true,
|
||||
|
||||
// // Specifies whether there is a notification when you are the next speaker in line.
|
||||
// disableNextSpeakerNotification: false,
|
||||
|
||||
// // Specifies whether the raised hand will hide when someone becomes a dominant speaker or not.
|
||||
// disableRemoveRaisedHandOnFocus: false,
|
||||
// },
|
||||
|
||||
// speakerStats: {
|
||||
// // Specifies whether the speaker stats is enable or not.
|
||||
// disabled: false,
|
||||
@@ -1549,6 +1566,17 @@ var config = {
|
||||
// and will automatically redirect to the token service to get the token for the meeting.
|
||||
// tokenAuthUrlAutoRedirect: false
|
||||
|
||||
// You can put an array of values to target different entity types in the invite dialog.
|
||||
// Valid values are "phone", "room", "sip", "user", "videosipgw" and "email"
|
||||
// peopleSearchQueryTypes: ["user", "email"],
|
||||
// Directory endpoint which is called for invite dialog autocomplete
|
||||
// peopleSearchUrl: "https://myservice.com/api/people",
|
||||
// Endpoint which is called to send invitation requests
|
||||
// inviteServiceUrl: "https://myservice.com/api/invite",
|
||||
|
||||
// For external entities (e. g. email), the localStorage key holding the token value for directory authentication
|
||||
// peopleSearchTokenLocation: "mytoken",
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
@@ -1565,8 +1593,6 @@ var config = {
|
||||
iAmRecorder
|
||||
iAmSipGateway
|
||||
microsoftApiApplicationClientID
|
||||
peopleSearchQueryTypes
|
||||
peopleSearchUrl
|
||||
requireDisplayName
|
||||
*/
|
||||
|
||||
@@ -1747,8 +1773,6 @@ var config = {
|
||||
// tileTime: 5000,
|
||||
// // Limit results by rating: g, pg, pg-13, r. Default value: g.
|
||||
// rating: 'pg',
|
||||
// // The proxy server url for giphy requests in the web app.
|
||||
// proxyUrl: 'https://giphy-proxy.example.com',
|
||||
// },
|
||||
|
||||
// Logging
|
||||
|
||||
@@ -69,6 +69,11 @@
|
||||
|
||||
window.indexLoadedTime = window.performance.now();
|
||||
console.log("(TIME) index.html loaded:\t", indexLoadedTime);
|
||||
window.addEventListener('load', function() {
|
||||
window.loadedEventTime = window.performance.now();
|
||||
console.log("(TIME) window loaded event:\t", loadedEventTime);
|
||||
});
|
||||
|
||||
// XXX the code below listeners for errors and displays an error message
|
||||
// in the document body when any of the required files fails to load.
|
||||
// The intention is to prevent from displaying broken page.
|
||||
|
||||
@@ -1022,7 +1022,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-performance (5.0.0):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (4.7.1):
|
||||
- react-native-safe-area-context (4.10.8):
|
||||
- React-Core
|
||||
- react-native-slider (4.4.3):
|
||||
- React-Core
|
||||
@@ -1034,7 +1034,7 @@ PODS:
|
||||
- react-native-video/Video (6.0.0-alpha.11):
|
||||
- PromisesSwift
|
||||
- React-Core
|
||||
- react-native-webrtc (124.0.3):
|
||||
- react-native-webrtc (124.0.4):
|
||||
- JitsiWebRTC (~> 124.0.0)
|
||||
- React-Core
|
||||
- react-native-webview (13.8.7):
|
||||
@@ -1214,7 +1214,7 @@ PODS:
|
||||
- React-Core
|
||||
- RNDeviceInfo (10.9.0):
|
||||
- React-Core
|
||||
- RNGestureHandler (2.17.1):
|
||||
- RNGestureHandler (2.18.1):
|
||||
- glog
|
||||
- RCT-Folly (= 2022.05.16.00)
|
||||
- React-Core
|
||||
@@ -1558,11 +1558,11 @@ SPEC CHECKSUMS:
|
||||
react-native-orientation-locker: 4409c5b12b65f942e75449872b4f078b6f27af81
|
||||
react-native-pager-view: 0ccb8bf60e2ebd38b1f3669fa3650ecce81db2df
|
||||
react-native-performance: 47ac22ebf2aa24f324a96a5825581f6ce18c09e8
|
||||
react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2
|
||||
react-native-safe-area-context: b7daa1a8df36095a032dff095a1ea8963cb48371
|
||||
react-native-slider: 1cdd6ba29675df21f30544253bf7351d3c2d68c4
|
||||
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
|
||||
react-native-video: 472b7c366eaaaa0207e546d9a50410df89790bcf
|
||||
react-native-webrtc: 41f6602490c618e3ec0e2108e46b84e2c731c127
|
||||
react-native-webrtc: 48295e7228279470c4f5acb38570e170723bd3b2
|
||||
react-native-webview: a9663e81c7acc865b88632e43a164838f00745f6
|
||||
React-nativeconfig: 2e44d0d2dd222b12a5183f4bcaa4a91881497acb
|
||||
React-NativeModulesApple: 464a9590389efd364d45d726a35ef7ade6b5c59a
|
||||
@@ -1589,7 +1589,7 @@ SPEC CHECKSUMS:
|
||||
RNCClipboard: 0a720adef5ec193aa0e3de24c3977222c7e52a37
|
||||
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
|
||||
RNDeviceInfo: 02ea8b23e2280fa18e00a06d7e62804d74028579
|
||||
RNGestureHandler: 67d3f1f69d4d0c98d6e83f4229e3bbf997d1dc72
|
||||
RNGestureHandler: 1155b1898ceddefeebf77792927360d44fe11e77
|
||||
RNGoogleSignin: a6a612cce56a45ab701c5c5c6e36f5390522d100
|
||||
RNScreens: e842cdccb23c0a084bd6307f6fa83fd1c1738029
|
||||
RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -u
|
||||
set -e -u -x
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
PROJECT_REPO=$(realpath ${THIS_DIR}/../..)
|
||||
RELEASE_REPO=$(realpath ${THIS_DIR}/../../../jitsi-meet-ios-sdk-releases)
|
||||
DEFAULT_SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Lite-Info.plist)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
|
||||
|
||||
echo "Releasing Jitsi Meet SDK Lite ${SDK_VERSION}"
|
||||
|
||||
@@ -50,9 +48,6 @@ xcodebuild -create-xcframework \
|
||||
-framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
|
||||
-framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
|
||||
-output ios/sdk/out/JitsiMeetSDK.xcframework
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git tag ios-sdk-lite-${SDK_VERSION}
|
||||
fi
|
||||
popd
|
||||
|
||||
pushd ${RELEASE_REPO}
|
||||
@@ -61,11 +56,9 @@ pushd ${RELEASE_REPO}
|
||||
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeetSDK.xcframework lite/Frameworks/
|
||||
|
||||
# Add all files to git
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git add -A .
|
||||
git commit -m "${SDK_VERSION} lite"
|
||||
git tag "${SDK_VERSION}-lite"
|
||||
fi
|
||||
git add -A .
|
||||
git commit --allow-empty -m "${SDK_VERSION} lite"
|
||||
git tag "${SDK_VERSION}-lite"
|
||||
|
||||
popd
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -u
|
||||
set -e -u -x
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
PROJECT_REPO=$(realpath ${THIS_DIR}/../..)
|
||||
RELEASE_REPO=$(realpath ${THIS_DIR}/../../../jitsi-meet-ios-sdk-releases)
|
||||
DEFAULT_SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Info.plist)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
|
||||
|
||||
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
|
||||
|
||||
@@ -50,9 +48,6 @@ xcodebuild -create-xcframework \
|
||||
-framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
|
||||
-framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
|
||||
-output ios/sdk/out/JitsiMeetSDK.xcframework
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git tag ios-sdk-${SDK_VERSION}
|
||||
fi
|
||||
popd
|
||||
|
||||
pushd ${RELEASE_REPO}
|
||||
@@ -61,11 +56,9 @@ pushd ${RELEASE_REPO}
|
||||
cp -a ${PROJECT_REPO}/ios/sdk/out/JitsiMeetSDK.xcframework Frameworks/
|
||||
|
||||
# Add all files to git
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git add -A .
|
||||
git commit -m "${SDK_VERSION}"
|
||||
git tag ${SDK_VERSION}
|
||||
fi
|
||||
git add -A .
|
||||
git commit --allow-empty -m "${SDK_VERSION}"
|
||||
git tag "${SDK_VERSION}"
|
||||
|
||||
popd
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
|
||||
exec ${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command --reset-cache
|
||||
exec ${THIS_DIR}/../../node_modules/react-native/scripts/packager.sh --reset-cache
|
||||
|
||||
@@ -820,8 +820,11 @@
|
||||
"videoUnmuteBlockedDescription": "Die Kamera und Bildschirmfreigabe kann aus Überlastungsschutzgründen temporär nicht eingeschaltet werden.",
|
||||
"videoUnmuteBlockedTitle": "Kamera und Bildschirmfreigabe kann nicht aktiviert werden!",
|
||||
"viewLobby": "Lobby ansehen",
|
||||
"viewParticipants": "Personen anzeigen",
|
||||
"viewVisitors": "Gäste anzeigen",
|
||||
"waitingParticipants": "{{waitingParticipants}} Personen",
|
||||
"waitingVisitors": "In der Lobby wartende Gäste: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "Die Konferenz wurde noch nicht gestartet!",
|
||||
"whiteboardLimitDescription": "Bitte speichern Sie Ihre Inhalte, da das Nutzungslimit bald erreicht wird und dann Ihr Whiteboard geschlossen wird.",
|
||||
"whiteboardLimitTitle": "Whiteboard-Nutzung"
|
||||
},
|
||||
@@ -835,7 +838,10 @@
|
||||
"audioModeration": "Für sich selbst die Stummschaltung aufzuheben",
|
||||
"blockEveryoneMicCamera": "Kamera und Mikrofon von allen sperren",
|
||||
"breakoutRooms": "Breakout-Räume",
|
||||
"goLive": "Live gehen",
|
||||
"invite": "Person einladen",
|
||||
"lowerAllHands": "Alle Hände senken",
|
||||
"lowerHand": "Hand senken",
|
||||
"moreModerationActions": "Weitere Moderationsoptionen",
|
||||
"moreModerationControls": "Weitere Moderationsoptionen",
|
||||
"moreParticipantOptions": "Mehr Optionen für Anwesende",
|
||||
@@ -852,6 +858,7 @@
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "Anwesende ({{count}})",
|
||||
"visitorInQueue": " (Wartende Gäste {{count}})",
|
||||
"visitorRequests": " (Anfragen {{count}})",
|
||||
"visitors": "Gäste ({{count}})",
|
||||
"waitingLobby": "In der Lobby ({{count}})"
|
||||
@@ -865,10 +872,13 @@
|
||||
"pinnedParticipant": "Die Person ist angeheftet",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"edit": "Bearbeiten",
|
||||
"send": "Senden",
|
||||
"skip": "Überspringen",
|
||||
"submit": "Speichern"
|
||||
},
|
||||
"by": "Von {{ name }}",
|
||||
"closeButton": "Umfrage schließen",
|
||||
"create": {
|
||||
"addOption": "Antwort hinzufügen",
|
||||
"answerPlaceholder": "Antwort {{index}}",
|
||||
@@ -878,7 +888,8 @@
|
||||
"pollQuestion": "Frage",
|
||||
"questionPlaceholder": "Eine Frage stellen",
|
||||
"removeOption": "Antwort entfernen",
|
||||
"send": "Erstellen"
|
||||
"save": "Erstellen",
|
||||
"send": "Senden"
|
||||
},
|
||||
"errors": {
|
||||
"notUniqueOption": "Optionen müssen einzigartig sein"
|
||||
@@ -1483,16 +1494,22 @@
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(Gast)",
|
||||
"joinMeeting": {
|
||||
"description": "Sie beobachten derzeit diese Konferenz.",
|
||||
"raiseHand": "Hand heben",
|
||||
"title": "Konferenz wird beigetreten",
|
||||
"wishToSpeak": "Wenn Sie sprechen möchten, heben Sie bitte unten Ihre Hand und warten Sie auf die Zustimmung der Moderation"
|
||||
},
|
||||
"labelTooltip": "Anzahl Gäste: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Hierhin verschoben von {{actor}}, bitte melden Sie sich um teilzunehmen",
|
||||
"description": "Bitte melden Sie sich um teilzunehmen",
|
||||
"noMainParticipantsDescription": "Eine Person muss die Konferenz starten. Bitte versuchen Sie es gleich noch einmal.",
|
||||
"noMainParticipantsTitle": "Diese Konferenz wurde noch nicht gestartet.",
|
||||
"noVisitorLobby": "Sie können nicht teilnehmen, solange die Lobby für diese Konferenz aktiviert ist.",
|
||||
"notAllowedPromotion": "Eine Person muss Ihre Anfrage erst erlauben.",
|
||||
"title": "Sie sind Gast in der Konferenz"
|
||||
}
|
||||
},
|
||||
"waitingMessage": "Sie werden der Konferenz beitreten, sobald sie gestartet ist!"
|
||||
},
|
||||
"volumeSlider": "Lautstärkeregler",
|
||||
"welcomepage": {
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Nav iespējams, kamēr ir aktīva ierakstīšana",
|
||||
"localUserControls": "Lokālo lietotāju kontroles",
|
||||
"lockMessage": "Neizdevās aizslēgt sapulci.",
|
||||
"lockRoom": "Pievienot sapulci $t(lockRoomPasswordUppercase)",
|
||||
"lockRoom": "Iestatīt sapulces $t(lockRoomPasswordUppercase)",
|
||||
"lockTitle": "Aizslēgšāna neizdevās",
|
||||
"login": "Pierakstīties",
|
||||
"loginQuestion": "Vai tiešām vēlaties pierakstīties un pamest sapulci?",
|
||||
@@ -382,9 +382,9 @@
|
||||
"noDropboxToken": "Nav derīga Dropbox tokena",
|
||||
"password": "Parole",
|
||||
"passwordLabel": "Dalībnieks ir aizslēdzis sapulci. Lūdzu, ievadiet $t(lockRoomPassword), lai pievienotos.",
|
||||
"passwordNotSupported": "Sapulces $t(lockRoomPassword) iestatīšana netiek atbalstīta.",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) netiek atbalstīts",
|
||||
"passwordRequired": "Nepieciešams $t(lockRoomPasswordUppercase)",
|
||||
"passwordNotSupported": "Sapulces slēgšana ar $t(lockRoomPassword) netiek atbalstīta.",
|
||||
"passwordNotSupportedTitle": "Slēgšana ar $t(lockRoomPasswordUppercase) netiek atbalstīta",
|
||||
"passwordRequired": "Nepieciešams ievadīt $t(lockRoomPasswordUppercase)",
|
||||
"permissionCameraRequiredError": "Lai piedalītos konferencēs ar video, ir nepieciešama kameras atļauja. Lūdzu, piešķiriet to Iestatījumos",
|
||||
"permissionErrorTitle": "Nepieciešama atļauja",
|
||||
"permissionMicRequiredError": "Lai piedalītos konferencēs ar audio, nepieciešama mikrofona atļauja. Lūdzu, piešķiriet to Iestatījumos",
|
||||
@@ -567,7 +567,7 @@
|
||||
"noRoom": "Iezvana numuram nav piesaistīta neviena sapulces telpa.",
|
||||
"noWhiteboard": "Nevarēja ielādēt tāfeli.",
|
||||
"numbers": "Iezvana numuri",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"password": "Ievadiet $t(lockRoomPasswordUppercase):",
|
||||
"reachedLimit": "Jūs esat sasniedzis sava plāna limitu.",
|
||||
"sip": "SIP adrese",
|
||||
"sipAudioOnly": "Tikai SIP audio adrese",
|
||||
@@ -604,7 +604,7 @@
|
||||
"showSpeakerStats": "Rādīt prezentētāja statistiku",
|
||||
"toggleChat": "Tērzētava (čats) (atvērt/aizvērt)",
|
||||
"toggleFilmstrip": "Kinolente (rādīt/nerādīt)",
|
||||
"toggleParticipantsPane": "Rādīt vai paslēpt dalībnieku paneli",
|
||||
"toggleParticipantsPane": "Rādīt/paslēpt dalībnieku paneli",
|
||||
"toggleScreensharing": "Pārslēgties starp kameru un ekrāna rādīšanu",
|
||||
"toggleShortcuts": "Atrās piekļuves taustiņi (rādīt/nerādīt)",
|
||||
"videoMute": "Kamera (iesl./izsl.)"
|
||||
@@ -659,7 +659,7 @@
|
||||
"emailField": "Ievadiet savu e-pasta adresi",
|
||||
"enableDialogPasswordField": "Iestatīt paroli (neobligāti)",
|
||||
"enableDialogSubmit": "Iespējot",
|
||||
"enableDialogText": "Vestibila režīms ļauj aizsargāt sapulci, ļaujot cilvēkiem tajā iekļūt tikai pēc oficiāla moderatora apstiprinājuma.",
|
||||
"enableDialogText": "Vestibila režīms ļauj aizsargāt sapulci, ļaujot cilvēkiem tajā iekļūt tikai pēc moderatora apstiprinājuma.",
|
||||
"enterPasswordButton": "Ievadiet sapulces paroli",
|
||||
"enterPasswordTitle": "Ievadiet paroli, lai pievienotos sapulcei",
|
||||
"errorMissingPassword": "Lūdzu, ievadiet sapulces paroli",
|
||||
@@ -719,8 +719,8 @@
|
||||
"wait": "Lūdzu, uzgaidiet, kamēr jūsu ieraksts tiek saglabāts",
|
||||
"yes": "Jā"
|
||||
},
|
||||
"lockRoomPassword": "parole",
|
||||
"lockRoomPasswordUppercase": "Parole",
|
||||
"lockRoomPassword": "paroli",
|
||||
"lockRoomPasswordUppercase": "Paroli",
|
||||
"lonelyMeetingExperience": {
|
||||
"button": "Uzaiciniet citus",
|
||||
"youAreAlone": "Jūs esat vienīgais sapulcē"
|
||||
@@ -786,6 +786,7 @@
|
||||
"newDeviceAction": "Izmantot",
|
||||
"newDeviceAudioTitle": "Atrasta jauna audio ierīce",
|
||||
"newDeviceCameraTitle": "Atrasta jauna kamera",
|
||||
"nextToSpeak": "Jūs esat nākamais runātājs rindā",
|
||||
"noiseSuppressionDesktopAudioDescription": "Darbvirsmas audio koplietošanas laikā nevar iespējot trokšņu slāpēšanu. Lūdzu, atspējojiet to un mēģiniet vēlreiz.",
|
||||
"noiseSuppressionFailedTitle": "Neizdevās sākt trokšņu slāpēšanu",
|
||||
"noiseSuppressionStereoDescription": "Stereo audio trokšņu slāpēšana pašlaik netiek atbalstīta.",
|
||||
@@ -820,8 +821,11 @@
|
||||
"videoUnmuteBlockedDescription": "Kameras ieslēgšanas un darbvirsmas koplietošanas darbība ir īslaicīgi bloķēta sistēmas ierobežojumu dēļ.",
|
||||
"videoUnmuteBlockedTitle": "Kameras ieslēgšana un darbvirsmas koplietošana ir bloķēta!",
|
||||
"viewLobby": "Skatīt vestibilu",
|
||||
"viewParticipants": "Skatīt dalībniekus",
|
||||
"viewVisitors": "Skatīt apmeklētājus",
|
||||
"waitingParticipants": "{{waitingParticipants}} personas",
|
||||
"waitingVisitors": "Apmeklētāji gaida rindā: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "Sanāksme vēl nav sākusies!",
|
||||
"whiteboardLimitDescription": "Lūdzu, saglabājiet savu progresu, jo drīz tiks sasniegts lietotāju limits un tāfele tiks aizvērta.",
|
||||
"whiteboardLimitTitle": "Tāfeles lietošana"
|
||||
},
|
||||
@@ -835,7 +839,10 @@
|
||||
"audioModeration": "Ieslēgt savu skaņu",
|
||||
"blockEveryoneMicCamera": "Bloķēt visiem mikrofonu un kameru",
|
||||
"breakoutRooms": "Grupu istabas",
|
||||
"goLive": "Sākt",
|
||||
"invite": "Uzaicināt",
|
||||
"lowerAllHands": "Nolaist visas paceltās rokas",
|
||||
"lowerHand": "Nolaist roku",
|
||||
"moreModerationActions": "Vairāk moderēšanas iespēju",
|
||||
"moreModerationControls": "Vairāk moderēšanas iespēju",
|
||||
"moreParticipantOptions": "Vairāk dalībnieku iespēju",
|
||||
@@ -852,6 +859,7 @@
|
||||
"headings": {
|
||||
"lobby": "Vestibils ({{count}})",
|
||||
"participantsList": "Sapulces dalībnieki ({{count}})",
|
||||
"visitorInQueue": " (gaida {{count}})",
|
||||
"visitorRequests": " (pieprasījumi {{count}})",
|
||||
"visitors": "Apmeklētāji ({{count}})",
|
||||
"waitingLobby": "Gaida vestibilā ({{count}})"
|
||||
@@ -871,6 +879,7 @@
|
||||
"submit": "Iesniegt"
|
||||
},
|
||||
"by": "Pēc {{ name }} iniciatīvas",
|
||||
"closeButton": "Slēgt aptauju",
|
||||
"create": {
|
||||
"addOption": "Pievienot opciju",
|
||||
"answerPlaceholder": "Opcija {{index}}",
|
||||
@@ -1026,7 +1035,7 @@
|
||||
"localRecordingStartWarningTitle": "Apturiet ierakstīšanu, lai to saglabātu",
|
||||
"localRecordingVideoStop": "Apturot vide, tiks apturēta arī lokālā ierakstīšana. Vai tiešām vēlaties turpināt?",
|
||||
"localRecordingVideoWarning": "Lai ierakstītu video, tas ir jāieslēdz, uzsākot ierakstīšanu",
|
||||
"localRecordingWarning": "Noteikti atlasiet pašreizējo cilni, lai izmantotu pareizo video un audio. Ieraksts pašlaik ir ierobežots līdz 1 GB, kas ir aptuveni 100 minūtes.",
|
||||
"localRecordingWarning": "Noteikti izvēlieties pašreizējo cilni, lai izmantotu pareizo video un audio. Ieraksts pašlaik ir ierobežots līdz 1 GB, kas ir aptuveni 100 minūtes.",
|
||||
"loggedIn": "Pierakstījies kā {{userName}}",
|
||||
"noMicPermission": "Mikrofona ierakstu nevarēja izveidot. Lūdzu, piešķiriet atļauju lietot mikrofonu.",
|
||||
"noStreams": "Nav konstatēta audio vai video straume.",
|
||||
@@ -1058,8 +1067,8 @@
|
||||
"pullToRefresh": "Pavilkt, lai atsvaidzinātu"
|
||||
},
|
||||
"security": {
|
||||
"about": "Savai sapulcei pievienojiet $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
|
||||
"aboutReadOnly": "Moderatora dalībnieki sapulcei var pievienot $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
|
||||
"about": "Iestatiet sapulcei $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
|
||||
"aboutReadOnly": "Moderatora dalībnieki sapulcei var iestatīt $t(lockRoomPassword). Dalībniekiem būs jānorāda $t(lockRoomPassword), lai viņi varētu pievienoties sapulcei.",
|
||||
"insecureRoomNameWarningNative": "Istabas nosaukums nav drošs. Nevēlami dalībnieki var pievienoties jūsu sapulcei. {{recommendAction}} Uzziniet vairāk par tikšanās nodrošināšanu",
|
||||
"insecureRoomNameWarningWeb": "Istabas nosaukums nav drošs. Nevēlami dalībnieki var pievienoties jūsu sapulcei. {{recommendAction}} Uzziniet vairāk par to, kā nodrošināt atbilstību prasībām <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">šeit</a>.",
|
||||
"title": "Drošības iespējas",
|
||||
@@ -1083,7 +1092,7 @@
|
||||
"desktopShareHighFpsWarning": "Lielāks kadru nomaiņas ātrums darbvirsmas koplietošanai var ietekmēt joslas platumu. Lai jaunie iestatījumi stātos spēkā, ir jārestartē ekrāna kopīgošana.",
|
||||
"desktopShareWarning": "Lai jaunie iestatījumi stātos spēkā, ir jārestartē ekrāna kopīgošana.",
|
||||
"devices": "Ierīces",
|
||||
"followMe": "Visi man seko",
|
||||
"followMe": "Visi seko man",
|
||||
"framesPerSecond": "kadri sekundē",
|
||||
"incomingMessage": "Ienākošā ziņa",
|
||||
"language": "Valoda",
|
||||
@@ -1108,7 +1117,7 @@
|
||||
"selfView": "Pašskats",
|
||||
"shortcuts": "Īsceļi",
|
||||
"speakers": "Skaļruņi",
|
||||
"startAudioMuted": "Dalībnieki pievienojas ar izslēgtu skaņu",
|
||||
"startAudioMuted": "Dalībnieki pievienojas ar izslēgtu mikrofonu",
|
||||
"startReactionsMuted": "Izslēgt reakcijas skaņas visiem",
|
||||
"startVideoMuted": "Dalībnieki pievienojas ar izslēgtu kameru",
|
||||
"talkWhileMuted": "Runā, kad izslēgta skaņa",
|
||||
@@ -1486,16 +1495,22 @@
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(apmeklētājs)",
|
||||
"joinMeeting": {
|
||||
"description": "Jūs pašlaik esat novērotājs šajā konferencē.",
|
||||
"raiseHand": "Pacelt roku",
|
||||
"title": "Pievienošanās sapulcei",
|
||||
"wishToSpeak": "Ja vēlaties runāt, lūdzu, paceliet roku zemāk un gaidiet moderatora apstiprinājumu."
|
||||
},
|
||||
"labelTooltip": "Apmeklētāju skaits: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "{{actor}} pārveidoja par apmeklētāju, paceliet roku, lai piedalītos",
|
||||
"description": "Paceliet roku, lai piedalītos",
|
||||
"noMainParticipantsDescription": "Dalībniekam ir jāsāk sapulce. Lūdzu, pēc brīža mēģiniet vēlreiz.",
|
||||
"noMainParticipantsTitle": "Šī sapulce vēl nav sākusies.",
|
||||
"noVisitorLobby": "Jūs nevarat pievienoties, kamēr sapulcei ir iespējots vestibils.",
|
||||
"notAllowedPromotion": "Dalībniekam vispirms ir jāatļauj jūsu pieprasījums.",
|
||||
"title": "Jūs esat sapulces apmeklētājs"
|
||||
}
|
||||
},
|
||||
"waitingMessage": "Jūs pievienosities sapulcei, tiklīdz tā sāksies!"
|
||||
},
|
||||
"volumeSlider": "Skaļuma slīdnis",
|
||||
"welcomepage": {
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
"privateNotice": "Mensagem privada para {{recipient}}",
|
||||
"sendButton": "Enviar",
|
||||
"smileysPanel": "Painel de Emojis",
|
||||
"systemDisplayName": "Sistema",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"polls": "Sondagens"
|
||||
@@ -263,6 +264,7 @@
|
||||
"Share": "Partilhar",
|
||||
"Submit": "Submeter",
|
||||
"WaitForHostMsg": "A conferência ainda não começou porque ainda não chegaram moderadores. Se quiser ser um moderador, inicie a sessão. Caso contrário, aguarde.",
|
||||
"WaitForHostNoAuthMsg": "A conferência ainda não começou porque ainda não chegaram os moderadores. Por favor, aguarde.",
|
||||
"WaitingForHostButton": "Esperar pelo moderador",
|
||||
"WaitingForHostTitle": "À espera de um moderador...",
|
||||
"Yes": "Sim",
|
||||
@@ -318,6 +320,7 @@
|
||||
"embedMeeting": "Embutir reunião",
|
||||
"enterDisplayName": "Digite o seu nome",
|
||||
"error": "Erro",
|
||||
"errorRoomCreationRestriction": "Tentou juntar-se demasiado depressa, por favor volte mais tarde.",
|
||||
"gracefulShutdown": "O nosso serviço está atualmente em manutenção. Por favor, tente novamente mais tarde.",
|
||||
"grantModeratorDialog": "Tem a certeza que quer conceder direitos de moderador a {{participantName}}?",
|
||||
"grantModeratorTitle": "Conceder direitos de moderador",
|
||||
@@ -733,8 +736,10 @@
|
||||
"connectedOneMember": "{{name}} entrou na reunião",
|
||||
"connectedThreePlusMembers": "{{name}} e muitos outros entraram na reunião",
|
||||
"connectedTwoMembers": "{{first}} e {{second}} entraram na reunião",
|
||||
"dataChannelClosed": "Deficiência na qualidade do vídeo",
|
||||
"dataChannelClosedDescription": "O canal de ponte foi desconectado e, portanto, a qualidade do vídeo está limitada à sua configuração mais baixa.",
|
||||
"dataChannelClosed": "A qualidade do vídeo pode ser afetada",
|
||||
"dataChannelClosedDescription": "O canal de ponte está em baixo e, por isso, a qualidade de vídeo pode estar limitada à sua definição mais baixa.",
|
||||
"dataChannelClosedDescriptionWithAudio": "O canal de ponte está em baixo, pelo que podem ocorrer interrupções no áudio e no vídeo.",
|
||||
"dataChannelClosedWithAudio": "A qualidade do áudio e do vídeo pode ser afetada",
|
||||
"disabledIframe": "A incorporação destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos.",
|
||||
"disabledIframeSecondary": "A incorporação de {{domain}} destina-se apenas a fins de demonstração, pelo que esta chamada será desligada em {{timeout}} minutos. Por favor, use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> para incorporação em produção!",
|
||||
"disconnected": "desconectado",
|
||||
@@ -781,6 +786,7 @@
|
||||
"newDeviceAction": "Usar",
|
||||
"newDeviceAudioTitle": "Novo dispositivo de áudio detetado",
|
||||
"newDeviceCameraTitle": "Nova câmara detetada",
|
||||
"nextToSpeak": "É o próximo na fila para falar",
|
||||
"noiseSuppressionDesktopAudioDescription": "A supressão de ruído não pode ser ativada enquanto se partilha o áudio do ambiente de trabalho, por favor desative-o e tente novamente.",
|
||||
"noiseSuppressionFailedTitle": "Falha ao iniciar a supressão de ruído",
|
||||
"noiseSuppressionStereoDescription": "A supressão do ruído de áudio estéreo não é atualmente suportada.",
|
||||
@@ -815,8 +821,11 @@
|
||||
"videoUnmuteBlockedDescription": "A operação de ligar a câmara e partilhar o ambiente de trabalho foi temporariamente bloqueada devido aos limites do sistema.",
|
||||
"videoUnmuteBlockedTitle": "Está bloqueado ligar a câmara e partilhar o ambiente de trabalho!",
|
||||
"viewLobby": "Ver sala de espera",
|
||||
"viewParticipants": "Ver participantes",
|
||||
"viewVisitors": "Ver visitantes",
|
||||
"waitingParticipants": "{{waitingParticipants}} pessoas",
|
||||
"waitingVisitors": "Visitantes em fila de espera: {{waitingVisitors}}",
|
||||
"waitingVisitorsTitle": "A reunião ainda não está em direto!",
|
||||
"whiteboardLimitDescription": "Guarde o seu progresso, pois o limite de utilizadores será atingido em breve e o quadro branco será encerrado.",
|
||||
"whiteboardLimitTitle": "Utilização do quadro branco"
|
||||
},
|
||||
@@ -830,7 +839,10 @@
|
||||
"audioModeration": "Ligar o microfone deles",
|
||||
"blockEveryoneMicCamera": "Bloquear o microfone e a câmara de todos",
|
||||
"breakoutRooms": "Salas simultâneas",
|
||||
"goLive": "Aceder ao vivo",
|
||||
"invite": "Convidar alguém",
|
||||
"lowerAllHands": "Baixar todas as mãos",
|
||||
"lowerHand": "Baixar a mão",
|
||||
"moreModerationActions": "Mais opções de moderação",
|
||||
"moreModerationControls": "Mais controlos de moderação",
|
||||
"moreParticipantOptions": "Mais opções de participantes",
|
||||
@@ -847,6 +859,7 @@
|
||||
"headings": {
|
||||
"lobby": "Sala de espera ({{count}})",
|
||||
"participantsList": "Participantes da reunião ({{count}})",
|
||||
"visitorInQueue": " (à espera {{count}})",
|
||||
"visitorRequests": " (pedidos {{count}})",
|
||||
"visitors": "Visitantes ({{count}})",
|
||||
"waitingLobby": "Aguardam na sala de espera ({{count}})"
|
||||
@@ -860,10 +873,13 @@
|
||||
"pinnedParticipant": "O participante está afixado",
|
||||
"polls": {
|
||||
"answer": {
|
||||
"edit": "Editar",
|
||||
"send": "Enviar",
|
||||
"skip": "Ignorar",
|
||||
"submit": "Submeter"
|
||||
},
|
||||
"by": "Por {{ name }}",
|
||||
"closeButton": "Fechar sondagem",
|
||||
"create": {
|
||||
"addOption": "Adicionar opção",
|
||||
"answerPlaceholder": "Opção {{index}}",
|
||||
@@ -873,6 +889,7 @@
|
||||
"pollQuestion": "Pergunta de Sondagem",
|
||||
"questionPlaceholder": "Faça uma pergunta",
|
||||
"removeOption": "Remover opção",
|
||||
"save": "Guardar",
|
||||
"send": "Enviar"
|
||||
},
|
||||
"errors": {
|
||||
@@ -1478,12 +1495,22 @@
|
||||
},
|
||||
"visitors": {
|
||||
"chatIndicator": "(visitante)",
|
||||
"joinMeeting": {
|
||||
"description": "Atualmente, é um observador nesta conferência.",
|
||||
"raiseHand": "Levantar a mão",
|
||||
"title": "Participar na reunião",
|
||||
"wishToSpeak": "Se deseja intervir, levante a mão e aguarde a aprovação do moderador."
|
||||
},
|
||||
"labelTooltip": "Número de visitantes: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Enviado aqui pelo {{actor}}, levante a mão para participar",
|
||||
"description": "Para participar levante a sua mão",
|
||||
"noMainParticipantsDescription": "Um participante precisa de iniciar a reunião. Tente novamente daqui a pouco.",
|
||||
"noMainParticipantsTitle": "Esta reunião ainda não começou.",
|
||||
"noVisitorLobby": "Não é possível aderir enquanto houver uma sala de espera activada para a reunião.",
|
||||
"notAllowedPromotion": "É necessário que um participante autorize primeiro o seu pedido.",
|
||||
"title": "É um visitante na reunião"
|
||||
}
|
||||
},
|
||||
"waitingMessage": "Participará na reunião assim que esta estiver em direto!"
|
||||
},
|
||||
"volumeSlider": "Controlo de volume",
|
||||
"welcomepage": {
|
||||
|
||||
@@ -443,7 +443,7 @@
|
||||
"shareVideoTitle": "Share video",
|
||||
"shareYourScreen": "Share your screen",
|
||||
"shareYourScreenDisabled": "Screen sharing disabled.",
|
||||
"sharedVideoDialogError": "Error: Invalid URL",
|
||||
"sharedVideoDialogError": "Error: Invalid or forbidden URL",
|
||||
"sharedVideoLinkPlaceholder": "YouTube link or direct video link",
|
||||
"show": "Show",
|
||||
"start": "Start ",
|
||||
@@ -821,6 +821,7 @@
|
||||
"videoUnmuteBlockedDescription": "Camera unmute and desktop sharing operation have been temporarily blocked because of system limits.",
|
||||
"videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!",
|
||||
"viewLobby": "View lobby",
|
||||
"viewParticipants": "View participants",
|
||||
"viewVisitors": "View visitors",
|
||||
"waitingParticipants": "{{waitingParticipants}} people",
|
||||
"waitingVisitors": "Visitors waiting in queue: {{waitingVisitors}}",
|
||||
@@ -1503,7 +1504,6 @@
|
||||
"labelTooltip": "Number of visitors: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
|
||||
"description": "To participate raise your hand",
|
||||
"noMainParticipantsDescription": "A participant needs to start the meeting. Please try again in a bit.",
|
||||
"noMainParticipantsTitle": "This meeting hasn’t started yet.",
|
||||
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
|
||||
|
||||
@@ -489,9 +489,8 @@ function initCommands() {
|
||||
sendAnalytics(createApiEvent('email.changed'));
|
||||
APP.conference.changeLocalEmail(email);
|
||||
},
|
||||
'avatar-url': avatarUrl => {
|
||||
sendAnalytics(createApiEvent('avatar.url.changed'));
|
||||
APP.conference.changeLocalAvatarUrl(avatarUrl);
|
||||
'avatar-url': () => {
|
||||
console.warn('Setting avatar url is available when using jwt via context.user.avatar');
|
||||
},
|
||||
'send-chat-message': (message, to, ignorePrivacy = false) => {
|
||||
if (to) {
|
||||
@@ -1126,12 +1125,7 @@ class API {
|
||||
this.notifyBrowserSupport(isSupportedBrowser());
|
||||
|
||||
// Let the embedder know we are ready.
|
||||
this._sendEvent({
|
||||
name: 'ready',
|
||||
|
||||
// XXX: Here we are using window.config since this is fired really early.
|
||||
info: window.config.deploymentInfo
|
||||
});
|
||||
this._sendEvent({ name: 'ready' });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1343,14 +1337,14 @@ class API {
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyReceivedChatMessage(
|
||||
{ body, id, nick, privateMessage, ts } = {}) {
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
{ body, from, nick, privateMessage, ts } = {}) {
|
||||
if (APP.conference.isLocalId(from)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendEvent({
|
||||
name: 'incoming-message',
|
||||
from: id,
|
||||
from,
|
||||
message: body,
|
||||
nick,
|
||||
privateMessage,
|
||||
|
||||
181
package-lock.json
generated
181
package-lock.json
generated
@@ -61,8 +61,8 @@
|
||||
"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/v1845.0.0+515a927c/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1852.0.0+526ec25d/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"null-loader": "4.0.1",
|
||||
@@ -82,15 +82,15 @@
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "10.9.0",
|
||||
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
|
||||
"react-native-gesture-handler": "2.17.1",
|
||||
"react-native-gesture-handler": "2.18.1",
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-immersive-mode": "2.0.2",
|
||||
"react-native-keep-awake": "4.0.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",
|
||||
"react-native-safe-area-context": "4.7.1",
|
||||
"react-native-safe-area-context": "4.10.8",
|
||||
"react-native-screens": "3.32.0",
|
||||
"react-native-sound": "0.11.2",
|
||||
"react-native-splash-screen": "3.3.0",
|
||||
@@ -100,7 +100,7 @@
|
||||
"react-native-url-polyfill": "2.0.0",
|
||||
"react-native-video": "6.0.0-alpha.11",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "124.0.3",
|
||||
"react-native-webrtc": "124.0.4",
|
||||
"react-native-webview": "13.8.7",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
@@ -132,7 +132,7 @@
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/dom-screen-wake-lock": "1.0.1",
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/lodash": "4.14.182",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/moment-duration-format": "2.2.6",
|
||||
"@types/offscreencanvas": "2019.7.2",
|
||||
"@types/pixelmatch": "5.2.5",
|
||||
@@ -6254,6 +6254,15 @@
|
||||
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/long": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
|
||||
@@ -10278,9 +10287,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-loops": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz",
|
||||
"integrity": "sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g=="
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.4.tgz",
|
||||
"integrity": "sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg=="
|
||||
},
|
||||
"node_modules/fast-shallow-equal": {
|
||||
"version": "1.0.0",
|
||||
@@ -12489,8 +12498,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1845.0.0+515a927c/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-UzT6skfD8wqs2695oMi3B8SxcImhG2SIYwc1xM+z9pVlJ4MAOWsLc7flCBXSnpnUTK3b5AjCOot3cRqeMibNcQ==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1852.0.0+526ec25d/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-zFt1dzJfSbLejjZUR0FAsGxQIzF/RnLqFmxnHDACaiEIbZhxmyw/Y4rQjLO7FRrmJNSP37U4cuiTi5m/AwP2PA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -12824,7 +12833,13 @@
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
@@ -15076,9 +15091,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-devtools-core/node_modules/ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
@@ -15307,9 +15322,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-gesture-handler": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.17.1.tgz",
|
||||
"integrity": "sha512-pWfniN6NuVKUq40KACuD3NCMe+bWNQCpD3cmxL6aLSCTwPKYmf7l4Lp0/E/almpjvxhybJZtFLU0w4tmxnIKaA==",
|
||||
"version": "2.18.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.18.1.tgz",
|
||||
"integrity": "sha512-WF2fxQ5kTaxHghlkBM4YxO86SyGWVwrSNgJ1E8z/ZtL2xD5B3bg5agvuVFfOzvceC114yq71s6E9vKPz94ZxRw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@egjs/hammerjs": "^2.0.17",
|
||||
@@ -15334,9 +15349,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-immersive-mode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-immersive-mode/-/react-native-immersive-mode-2.0.1.tgz",
|
||||
"integrity": "sha512-2wlL7VIHl4rr4gwgnUp9K1UvsN7J5VCGqoAvBWQXvB4xn7XaoDEl6z9vqaqOiEdC6aAh2d/7zqcJz+dfcR2ELw==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-immersive-mode/-/react-native-immersive-mode-2.0.2.tgz",
|
||||
"integrity": "sha512-W4YBpf/seURr6pdt91bKWtlQhwoZYgQN1nllj/y+EkwKs5xbu/IiRxlCs/8ML7dyA8wbOFaXR24AC4HOrvxapw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.60.5"
|
||||
}
|
||||
@@ -15350,6 +15366,7 @@
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1",
|
||||
"react-native": ">=0.63.2",
|
||||
@@ -15365,6 +15382,7 @@
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.2.0.tgz",
|
||||
"integrity": "sha512-pf9OnL/Tkr+5s4Gjmsn7xh91PtJLDa6qxYa/bmtUhd/+s4cQdWQ8DIFoOFghwZIHHHwVdWtoXkp6HtpjN+r20g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
@@ -15395,9 +15413,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-safe-area-context": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.7.1.tgz",
|
||||
"integrity": "sha512-X2pJG2ttmAbiGlItWedvDkZg1T1ikmEDiz+7HsiIwAIm2UbFqlhqn+B1JF53mSxPzdNaDcCQVHRNPvj8oFu6Yg==",
|
||||
"version": "4.10.8",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.8.tgz",
|
||||
"integrity": "sha512-Jx1lovhvIdYygg0UsMCBUJN0Wvj9GlA5bbcBLzjZf93uJpNHzaiHC4hR280+sNVK1+/pMHEyEkXVHDZE5JWn0w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
@@ -15990,10 +16009,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-webrtc": {
|
||||
"version": "124.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.3.tgz",
|
||||
"integrity": "sha512-uU+FxcAlBjLQ1EZnw3q+nje8xw+7hwZ7yDpSK4f1OJ25LrLA6fAjgxHCtMn9D20D9tR4+pRkBugBzkfqmboIFg==",
|
||||
"license": "MIT",
|
||||
"version": "124.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.4.tgz",
|
||||
"integrity": "sha512-ZbhSz1f+kc1v5VE0B84+v6ujIWTHa2fIuocrYzGUIFab7E5izmct7PNHb9dzzs0xhBGqh4c2rUa49jbL+P/e2w==",
|
||||
"dependencies": {
|
||||
"base64-js": "1.5.1",
|
||||
"debug": "4.3.4",
|
||||
@@ -17579,9 +17597,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -18712,9 +18730,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/ws": {
|
||||
"version": "7.5.7",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
|
||||
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
@@ -18903,9 +18921,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -19174,9 +19192,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
|
||||
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
|
||||
"dependencies": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
@@ -23400,6 +23418,15 @@
|
||||
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/long": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
|
||||
@@ -26411,9 +26438,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fast-loops": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz",
|
||||
"integrity": "sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g=="
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.4.tgz",
|
||||
"integrity": "sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg=="
|
||||
},
|
||||
"fast-shallow-equal": {
|
||||
"version": "1.0.0",
|
||||
@@ -27986,8 +28013,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1845.0.0+515a927c/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-UzT6skfD8wqs2695oMi3B8SxcImhG2SIYwc1xM+z9pVlJ4MAOWsLc7flCBXSnpnUTK3b5AjCOot3cRqeMibNcQ==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1852.0.0+526ec25d/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-zFt1dzJfSbLejjZUR0FAsGxQIzF/RnLqFmxnHDACaiEIbZhxmyw/Y4rQjLO7FRrmJNSP37U4cuiTi5m/AwP2PA==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
@@ -28250,7 +28277,13 @@
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
@@ -29855,9 +29888,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q=="
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -30105,9 +30138,9 @@
|
||||
"integrity": "sha512-MKbuBbovO8eGiAM9i6o0nrdBXivhRpzPQ+aVBXGJEPMH7RrCSNUKaCoEpkjfGHlTxjZimi6WjDCjjzCRSHlV1A=="
|
||||
},
|
||||
"react-native-gesture-handler": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.17.1.tgz",
|
||||
"integrity": "sha512-pWfniN6NuVKUq40KACuD3NCMe+bWNQCpD3cmxL6aLSCTwPKYmf7l4Lp0/E/almpjvxhybJZtFLU0w4tmxnIKaA==",
|
||||
"version": "2.18.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.18.1.tgz",
|
||||
"integrity": "sha512-WF2fxQ5kTaxHghlkBM4YxO86SyGWVwrSNgJ1E8z/ZtL2xD5B3bg5agvuVFfOzvceC114yq71s6E9vKPz94ZxRw==",
|
||||
"requires": {
|
||||
"@egjs/hammerjs": "^2.0.17",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
@@ -30124,9 +30157,9 @@
|
||||
}
|
||||
},
|
||||
"react-native-immersive-mode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-immersive-mode/-/react-native-immersive-mode-2.0.1.tgz",
|
||||
"integrity": "sha512-2wlL7VIHl4rr4gwgnUp9K1UvsN7J5VCGqoAvBWQXvB4xn7XaoDEl6z9vqaqOiEdC6aAh2d/7zqcJz+dfcR2ELw=="
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-immersive-mode/-/react-native-immersive-mode-2.0.2.tgz",
|
||||
"integrity": "sha512-W4YBpf/seURr6pdt91bKWtlQhwoZYgQN1nllj/y+EkwKs5xbu/IiRxlCs/8ML7dyA8wbOFaXR24AC4HOrvxapw=="
|
||||
},
|
||||
"react-native-keep-awake": {
|
||||
"version": "4.0.0",
|
||||
@@ -30159,9 +30192,9 @@
|
||||
"integrity": "sha512-nZJdliNFaA7vmu9PbYGSlwguRQVsuWX8CtchUUxZvjd/i9B3OYYveajIcuic3oQjWyTydLeDlhXRwpOUlj2Npw=="
|
||||
},
|
||||
"react-native-safe-area-context": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.7.1.tgz",
|
||||
"integrity": "sha512-X2pJG2ttmAbiGlItWedvDkZg1T1ikmEDiz+7HsiIwAIm2UbFqlhqn+B1JF53mSxPzdNaDcCQVHRNPvj8oFu6Yg=="
|
||||
"version": "4.10.8",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.8.tgz",
|
||||
"integrity": "sha512-Jx1lovhvIdYygg0UsMCBUJN0Wvj9GlA5bbcBLzjZf93uJpNHzaiHC4hR280+sNVK1+/pMHEyEkXVHDZE5JWn0w=="
|
||||
},
|
||||
"react-native-screens": {
|
||||
"version": "3.32.0",
|
||||
@@ -30518,9 +30551,9 @@
|
||||
}
|
||||
},
|
||||
"react-native-webrtc": {
|
||||
"version": "124.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.3.tgz",
|
||||
"integrity": "sha512-uU+FxcAlBjLQ1EZnw3q+nje8xw+7hwZ7yDpSK4f1OJ25LrLA6fAjgxHCtMn9D20D9tR4+pRkBugBzkfqmboIFg==",
|
||||
"version": "124.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.4.tgz",
|
||||
"integrity": "sha512-ZbhSz1f+kc1v5VE0B84+v6ujIWTHa2fIuocrYzGUIFab7E5izmct7PNHb9dzzs0xhBGqh4c2rUa49jbL+P/e2w==",
|
||||
"requires": {
|
||||
"base64-js": "1.5.1",
|
||||
"debug": "4.3.4",
|
||||
@@ -31603,9 +31636,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
@@ -32381,9 +32414,9 @@
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.5.7",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
|
||||
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -32500,9 +32533,9 @@
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -32683,9 +32716,9 @@
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
|
||||
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
|
||||
14
package.json
14
package.json
@@ -67,8 +67,8 @@
|
||||
"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/v1845.0.0+515a927c/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1852.0.0+526ec25d/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"null-loader": "4.0.1",
|
||||
@@ -88,15 +88,15 @@
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "10.9.0",
|
||||
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
|
||||
"react-native-gesture-handler": "2.17.1",
|
||||
"react-native-gesture-handler": "2.18.1",
|
||||
"react-native-get-random-values": "1.9.0",
|
||||
"react-native-immersive-mode": "2.0.1",
|
||||
"react-native-immersive-mode": "2.0.2",
|
||||
"react-native-keep-awake": "4.0.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",
|
||||
"react-native-safe-area-context": "4.7.1",
|
||||
"react-native-safe-area-context": "4.10.8",
|
||||
"react-native-screens": "3.32.0",
|
||||
"react-native-sound": "0.11.2",
|
||||
"react-native-splash-screen": "3.3.0",
|
||||
@@ -106,7 +106,7 @@
|
||||
"react-native-url-polyfill": "2.0.0",
|
||||
"react-native-video": "6.0.0-alpha.11",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "124.0.3",
|
||||
"react-native-webrtc": "124.0.4",
|
||||
"react-native-webview": "13.8.7",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
@@ -138,7 +138,7 @@
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/dom-screen-wake-lock": "1.0.1",
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/lodash": "4.14.182",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/moment-duration-format": "2.2.6",
|
||||
"@types/offscreencanvas": "2019.7.2",
|
||||
"@types/pixelmatch": "5.2.5",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"url": "git+https://github.com/jitsi/jitsi-meet.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "0.0.0",
|
||||
"@jitsi/js-utils": "0.0.0",
|
||||
"@jitsi/logger": "0.0.0",
|
||||
"@jitsi/rtcstats": "0.0.0",
|
||||
@@ -19,6 +20,7 @@
|
||||
"@react-navigation/material-top-tabs": "0.0.0",
|
||||
"@react-navigation/native": "0.0.0",
|
||||
"@react-navigation/stack": "0.0.0",
|
||||
"@stomp/stompjs": "0.0.0",
|
||||
"@xmldom/xmldom": "0.0.0",
|
||||
"base64-js": "0.0.0",
|
||||
"grapheme-splitter": "0.0.0",
|
||||
@@ -29,7 +31,7 @@
|
||||
"js-sha512": "0.0.0",
|
||||
"jwt-decode": "0.0.0",
|
||||
"lib-jitsi-meet": "0.0.0",
|
||||
"lodash": "0.0.0",
|
||||
"lodash-es": "0.0.0",
|
||||
"moment": "0.0.0",
|
||||
"moment-duration-format": "0.0.0",
|
||||
"optional-require": "0.0.0",
|
||||
@@ -39,6 +41,7 @@
|
||||
"react-i18next": "0.0.0",
|
||||
"react-linkify": "0.0.0",
|
||||
"react-native-dialog": "0.0.0",
|
||||
"react-native-paper": "0.0.0",
|
||||
"react-native-svg-transformer": "0.0.0",
|
||||
"react-native-tab-view": "0.0.0",
|
||||
"react-native-url-polyfill": "0.0.0",
|
||||
@@ -46,6 +49,7 @@
|
||||
"react-redux": "0.0.0",
|
||||
"redux": "0.0.0",
|
||||
"redux-thunk": "0.0.0",
|
||||
"text-encoding": "0.0.0",
|
||||
"unorm": "0.0.0",
|
||||
"util": "0.0.0",
|
||||
"uuid": "0.0.0",
|
||||
@@ -53,7 +57,6 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@amplitude/react-native": "0.0.0",
|
||||
"@braintree/sanitize-url": "0.0.0",
|
||||
"@giphy/react-native-sdk": "0.0.0",
|
||||
"@react-native/metro-config": "*",
|
||||
"@react-native-async-storage/async-storage": "0.0.0",
|
||||
@@ -61,7 +64,6 @@
|
||||
"@react-native-community/netinfo": "0.0.0",
|
||||
"@react-native-community/slider": "0.0.0",
|
||||
"@react-native-google-signin/google-signin": "0.0.0",
|
||||
"@stomp/stompjs": "0.0.0",
|
||||
"react-native": "*",
|
||||
"react": "*",
|
||||
"react-native-background-timer": "0.0.0",
|
||||
@@ -73,7 +75,6 @@
|
||||
"react-native-immersive-mode": "0.0.0",
|
||||
"react-native-keep-awake": "0.0.0",
|
||||
"react-native-pager-view": "0.0.0",
|
||||
"react-native-paper": "0.0.0",
|
||||
"react-native-performance": "0.0.0",
|
||||
"react-native-orientation-locker": "0.0.0",
|
||||
"react-native-safe-area-context": "0.0.0",
|
||||
@@ -84,8 +85,7 @@
|
||||
"react-native-video": "0.0.0",
|
||||
"react-native-watch-connectivity": "0.0.0",
|
||||
"react-native-webrtc": "0.0.0",
|
||||
"react-native-webview": "0.0.0",
|
||||
"text-encoding": "0.0.0"
|
||||
"react-native-webview": "0.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@xmldom/xmldom": "0.0.0"
|
||||
|
||||
42
react-native-sdk/prepare_sdk.js
vendored
42
react-native-sdk/prepare_sdk.js
vendored
@@ -1,10 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
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';
|
||||
@@ -56,44 +52,6 @@ function copyFolderRecursiveSync(source, target) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the dependency versions from the root package.json with the dependencies of the SDK package.json.
|
||||
*/
|
||||
function mergeDependencyVersions() {
|
||||
|
||||
// Updates SDK dependencies to match project dependencies.
|
||||
for (const key in SDKPackageJSON.dependencies) {
|
||||
if (SDKPackageJSON.dependencies.hasOwnProperty(key)) {
|
||||
SDKPackageJSON.dependencies[key] = packageJSON.dependencies[key] || packageJSON.devDependencies[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Updates SDK peer dependencies.
|
||||
for (const key in packageJSON.dependencies) {
|
||||
if (SDKPackageJSON.peerDependencies.hasOwnProperty(key)) {
|
||||
|
||||
// Updates all peer dependencies except react and react-native.
|
||||
if (key !== 'react' && key !== 'react-native') {
|
||||
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates SDK overrides dependencies.
|
||||
for (const key in packageJSON.overrides) {
|
||||
if (SDKPackageJSON.overrides.hasOwnProperty(key)) {
|
||||
SDKPackageJSON.overrides[key] = packageJSON.overrides[key];
|
||||
}
|
||||
}
|
||||
|
||||
const data = JSON.stringify(SDKPackageJSON, null, 4);
|
||||
|
||||
fs.writeFileSync('package.json', data);
|
||||
}
|
||||
|
||||
// TODO: put this in a seperate step
|
||||
mergeDependencyVersions();
|
||||
|
||||
copyFolderRecursiveSync(
|
||||
'../images',
|
||||
'.'
|
||||
|
||||
42
react-native-sdk/update_sdk_dependencies.js
vendored
Normal file
42
react-native-sdk/update_sdk_dependencies.js
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const packageJSON = require('../package.json');
|
||||
|
||||
const SDKPackageJSON = require('./package.json');
|
||||
|
||||
/**
|
||||
* Merges the dependency versions from the root package.json with the dependencies of the SDK package.json.
|
||||
*/
|
||||
function mergeDependencyVersions() {
|
||||
|
||||
// Updates SDK dependencies to match project dependencies.
|
||||
for (const key in SDKPackageJSON.dependencies) {
|
||||
if (SDKPackageJSON.dependencies.hasOwnProperty(key)) {
|
||||
SDKPackageJSON.dependencies[key] = packageJSON.dependencies[key] || packageJSON.devDependencies[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Updates SDK peer dependencies.
|
||||
for (const key in packageJSON.dependencies) {
|
||||
if (SDKPackageJSON.peerDependencies.hasOwnProperty(key)) {
|
||||
|
||||
// Updates all peer dependencies except react and react-native.
|
||||
if (key !== 'react' && key !== 'react-native') {
|
||||
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates SDK overrides dependencies.
|
||||
for (const key in packageJSON.overrides) {
|
||||
if (SDKPackageJSON.overrides.hasOwnProperty(key)) {
|
||||
SDKPackageJSON.overrides[key] = packageJSON.overrides[key];
|
||||
}
|
||||
}
|
||||
|
||||
const data = JSON.stringify(SDKPackageJSON, null, 4);
|
||||
|
||||
fs.writeFileSync('package.json', data);
|
||||
}
|
||||
|
||||
mergeDependencyVersions();
|
||||
@@ -76,7 +76,7 @@ export const _getTokenAuthState = (
|
||||
|
||||
for (const key of Object.keys(params)) {
|
||||
// we allow only config and interfaceConfig overrides in the state
|
||||
if (key.startsWith('config.') || key.startsWith('interfaceConfig.')) {
|
||||
if (key.startsWith('config.') || key.startsWith('interfaceConfig.') || key.startsWith('iceServers.')) {
|
||||
// @ts-ignore
|
||||
state[key] = params[key];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import _ from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import React, { Component, ComponentType, Fragment } from 'react';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { Provider } from 'react-redux';
|
||||
@@ -254,7 +254,7 @@ export default class BaseApp<P> extends Component<P, IState> {
|
||||
href?: string;
|
||||
props?: Object;
|
||||
}): Promise<any> {
|
||||
if (_.isEqual(route, this.state.route)) {
|
||||
if (isEqual(route, this.state.route)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ export interface IProps {
|
||||
*/
|
||||
colorBase?: string;
|
||||
|
||||
/**
|
||||
* Indicates the default icon for the avatar.
|
||||
*/
|
||||
defaultIcon?: string;
|
||||
|
||||
/**
|
||||
* Display name of the entity to render an avatar for (if any). This is handy when we need
|
||||
* an avatar for a non-participant entity (e.g. A recent list item).
|
||||
@@ -112,6 +117,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
|
||||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
defaultIcon: IconUser,
|
||||
dynamicColor: true
|
||||
};
|
||||
|
||||
@@ -172,6 +178,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
|
||||
_loadableAvatarUrlUseCORS,
|
||||
className,
|
||||
colorBase,
|
||||
defaultIcon,
|
||||
dynamicColor,
|
||||
id,
|
||||
size,
|
||||
@@ -229,7 +236,7 @@ class Avatar<P extends IProps> extends PureComponent<P, IState> {
|
||||
}
|
||||
|
||||
if (navigator.product !== 'ReactNative') {
|
||||
avatarProps.iconUser = IconUser;
|
||||
avatarProps.iconUser = defaultIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import GraphemeSplitter from 'grapheme-splitter';
|
||||
import _ from 'lodash';
|
||||
import { split } from 'lodash-es';
|
||||
|
||||
const AVATAR_COLORS = [
|
||||
'#6A50D3',
|
||||
@@ -63,7 +63,7 @@ function getFirstGraphemeUpper(word: string) {
|
||||
*/
|
||||
export function getInitials(s?: string) {
|
||||
// We don't want to use the domain part of an email address, if it is one
|
||||
const initialsBasis = _.split(s, '@')[0];
|
||||
const initialsBasis = split(s, '@')[0];
|
||||
const [ firstWord, secondWord ] = initialsBasis.split(wordSplitRegex).filter(Boolean);
|
||||
|
||||
return getFirstGraphemeUpper(firstWord) + getFirstGraphemeUpper(secondWord);
|
||||
|
||||
@@ -72,7 +72,6 @@ import {
|
||||
} from './actionTypes';
|
||||
import { setupVisitorStartupMedia } from './actions';
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
JITSI_CONFERENCE_URL_KEY
|
||||
} from './constants';
|
||||
@@ -275,13 +274,6 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[
|
||||
botType
|
||||
})));
|
||||
|
||||
conference.addCommandListener(
|
||||
AVATAR_URL_COMMAND,
|
||||
(data: { value: string; }, id: string) => dispatch(participantUpdated({
|
||||
conference,
|
||||
id,
|
||||
avatarURL: data.value
|
||||
})));
|
||||
conference.addCommandListener(
|
||||
EMAIL_COMMAND,
|
||||
(data: { value: string; }, id: string) => dispatch(participantUpdated({
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/**
|
||||
* The command type for updating a participant's avatar URL.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const AVATAR_URL_COMMAND = 'avatar-url';
|
||||
|
||||
/**
|
||||
* The command type for updating a participant's email address.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { sha512_256 as sha512 } from 'js-sha512';
|
||||
import _ from 'lodash';
|
||||
import { upperFirst, words } from 'lodash-es';
|
||||
|
||||
import { getName } from '../../app/functions';
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
|
||||
import { setObfuscatedRoom } from './actions';
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
JITSI_CONFERENCE_URL_KEY
|
||||
} from './constants';
|
||||
@@ -90,7 +89,9 @@ export function commonUserJoinedHandling(
|
||||
} else {
|
||||
const isReplacing = user?.isReplacing();
|
||||
|
||||
// the identity and avatar come from jwt and never change in the presence
|
||||
dispatch(participantJoined({
|
||||
avatarURL: user.getIdentity()?.user?.avatar,
|
||||
botType: user.getBotType(),
|
||||
conference,
|
||||
id,
|
||||
@@ -540,15 +541,11 @@ export function sendLocalParticipant(
|
||||
stateful: IStateful,
|
||||
conference?: IJitsiConference) {
|
||||
const {
|
||||
avatarURL,
|
||||
email,
|
||||
features,
|
||||
name
|
||||
} = getLocalParticipant(stateful) ?? {};
|
||||
|
||||
avatarURL && conference?.sendCommand(AVATAR_URL_COMMAND, {
|
||||
value: avatarURL
|
||||
});
|
||||
email && conference?.sendCommand(EMAIL_COMMAND, {
|
||||
value: email
|
||||
});
|
||||
@@ -571,7 +568,7 @@ export function sendLocalParticipant(
|
||||
* @returns {string}
|
||||
*/
|
||||
function safeStartCase(s = '') {
|
||||
return _.words(`${s}`.replace(/['\u2019]/g, '')).reduce(
|
||||
(result, word, index) => result + (index ? ' ' : '') + _.upperFirst(word)
|
||||
return words(`${s}`.replace(/['\u2019]/g, '')).reduce(
|
||||
(result, word, index) => result + (index ? ' ' : '') + upperFirst(word)
|
||||
, '');
|
||||
}
|
||||
|
||||
@@ -375,7 +375,6 @@ export interface IConfig {
|
||||
giphy?: {
|
||||
displayMode?: 'all' | 'tile' | 'chat';
|
||||
enabled?: boolean;
|
||||
proxyUrl?: string;
|
||||
rating?: 'g' | 'pg' | 'pg-13' | 'r';
|
||||
sdkKey?: string;
|
||||
tileTime?: number;
|
||||
@@ -477,6 +476,7 @@ export interface IConfig {
|
||||
};
|
||||
pcStatsInterval?: number;
|
||||
peopleSearchQueryTypes?: string[];
|
||||
peopleSearchTokenLocation?: string;
|
||||
peopleSearchUrl?: string;
|
||||
preferBosh?: boolean;
|
||||
preferVisitor?: boolean;
|
||||
@@ -487,6 +487,12 @@ export interface IConfig {
|
||||
hideExtraJoinButtons?: Array<string>;
|
||||
};
|
||||
prejoinPageEnabled?: boolean;
|
||||
raisedHands?: {
|
||||
disableLowerHandByModerator?: boolean;
|
||||
disableLowerHandNotification?: boolean;
|
||||
disableNextSpeakerNotification?: boolean;
|
||||
disableRemoveRaisedHandOnFocus?: boolean;
|
||||
};
|
||||
readOnlyName?: boolean;
|
||||
recordingLimit?: {
|
||||
appName?: string;
|
||||
|
||||
@@ -201,6 +201,7 @@ export default [
|
||||
'preferVisitor',
|
||||
'prejoinConfig',
|
||||
'prejoinPageEnabled',
|
||||
'raisedHands',
|
||||
'recordingService',
|
||||
'requireDisplayName',
|
||||
'remoteVideoMenu',
|
||||
|
||||
@@ -3,7 +3,7 @@ import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { safeJsonParse } from '@jitsi/js-utils/json';
|
||||
import _ from 'lodash';
|
||||
import { isEmpty, mergeWith, pick } from 'lodash-es';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { getLocalParticipant } from '../participants/functions';
|
||||
@@ -89,7 +89,37 @@ export function getFeatureFlag(state: IReduxState, featureFlag: string) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getDisableRemoveRaisedHandOnFocus(state: IReduxState) {
|
||||
return state['features/base/config']?.disableRemoveRaisedHandOnFocus || false;
|
||||
return state['features/base/config']?.raisedHands?.disableRemoveRaisedHandOnFocus || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector used to get the disableLowerHandByModerator.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getDisableLowerHandByModerator(state: IReduxState) {
|
||||
return state['features/base/config']?.raisedHands?.disableLowerHandByModerator || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector used to get the disableLowerHandNotification.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getDisableLowerHandNotification(state: IReduxState) {
|
||||
return state['features/base/config']?.raisedHands?.disableLowerHandNotification || true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector used to get the disableNextSpeakerNotification.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getDisableNextSpeakerNotification(state: IReduxState) {
|
||||
return state['features/base/config']?.raisedHands?.disableNextSpeakerNotification || false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,13 +166,11 @@ export function overrideConfigJSON(config: IConfig, interfaceConfig: any, json:
|
||||
const configJSON
|
||||
= getWhitelistedJSON(configName as 'interfaceConfig' | 'config', json[configName]);
|
||||
|
||||
if (!_.isEmpty(configJSON)) {
|
||||
logger.info(
|
||||
`Extending ${configName} with: ${
|
||||
JSON.stringify(configJSON)}`);
|
||||
if (!isEmpty(configJSON)) {
|
||||
logger.info(`Extending ${configName} with: ${JSON.stringify(configJSON)}`);
|
||||
|
||||
// eslint-disable-next-line arrow-body-style
|
||||
_.mergeWith(configObj, configJSON, (oldValue, newValue) => {
|
||||
mergeWith(configObj, configJSON, (oldValue, newValue) => {
|
||||
|
||||
// XXX We don't want to merge the arrays, we want to
|
||||
// overwrite them.
|
||||
@@ -166,9 +194,9 @@ export function overrideConfigJSON(config: IConfig, interfaceConfig: any, json:
|
||||
*/
|
||||
export function getWhitelistedJSON(configName: 'interfaceConfig' | 'config', configJSON: any): Object {
|
||||
if (configName === 'interfaceConfig') {
|
||||
return _.pick(configJSON, INTERFACE_CONFIG_WHITELIST);
|
||||
return pick(configJSON, INTERFACE_CONFIG_WHITELIST);
|
||||
} else if (configName === 'config') {
|
||||
return _.pick(configJSON, CONFIG_WHITELIST);
|
||||
return pick(configJSON, CONFIG_WHITELIST);
|
||||
}
|
||||
|
||||
return configJSON;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { merge, union } from 'lodash-es';
|
||||
|
||||
import { CONFERENCE_INFO } from '../../conference/components/constants';
|
||||
import { TOOLBAR_BUTTONS } from '../../toolbox/constants';
|
||||
@@ -194,7 +194,7 @@ function _setConfig(state: IConfig, { config }: { config: IConfig; }) {
|
||||
});
|
||||
}
|
||||
|
||||
const newState = _.merge(
|
||||
const newState = merge(
|
||||
{},
|
||||
config,
|
||||
hdAudioOptions,
|
||||
@@ -397,7 +397,7 @@ function _translateLegacyConfig(oldValue: IConfig) {
|
||||
= (newValue.conferenceInfo?.alwaysVisible ?? [])
|
||||
.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= _.union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
|
||||
= union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
|
||||
} else {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= (newValue.conferenceInfo.alwaysVisible ?? [])
|
||||
@@ -442,6 +442,12 @@ function _translateLegacyConfig(oldValue: IConfig) {
|
||||
newValue.disabledSounds.unshift('INCOMING_MSG_SOUND');
|
||||
}
|
||||
|
||||
newValue.raisedHands = newValue.raisedHands || {};
|
||||
|
||||
if (oldValue.disableRemoveRaisedHandOnFocus) {
|
||||
newValue.raisedHands.disableRemoveRaisedHandOnFocus = oldValue.disableRemoveRaisedHandOnFocus;
|
||||
}
|
||||
|
||||
if (oldValue.stereo || oldValue.opusMaxAverageBitrate) {
|
||||
newValue.audioQuality = {
|
||||
opusMaxAverageBitrate: oldValue.audioQuality?.opusMaxAverageBitrate ?? oldValue.opusMaxAverageBitrate,
|
||||
@@ -593,7 +599,7 @@ function _translateLegacyConfig(oldValue: IConfig) {
|
||||
* @returns {Object} The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _updateConfig(state: IConfig, { config }: { config: IConfig; }) {
|
||||
const newState = _.merge({}, state, config);
|
||||
const newState = merge({}, state, config);
|
||||
|
||||
_cleanupConfig(newState);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { conferenceLeft, conferenceWillLeave, redirect } from '../conference/actions';
|
||||
@@ -113,7 +113,7 @@ export function connectionFailed(
|
||||
export function constructOptions(state: IReduxState) {
|
||||
// Deep clone the options to make sure we don't modify the object in the
|
||||
// redux store.
|
||||
const options: IOptions = _.cloneDeep(state['features/base/config']);
|
||||
const options: IOptions = cloneDeep(state['features/base/config']);
|
||||
|
||||
const { locationURL, preferVisitor } = state['features/base/connection'];
|
||||
const params = parseURLParams(locationURL || '');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { isEqual, merge } from 'lodash-es';
|
||||
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
@@ -25,9 +25,9 @@ export interface IFlagsState {
|
||||
ReducerRegistry.register<IFlagsState>('features/base/flags', (state = DEFAULT_STATE, action): IFlagsState => {
|
||||
switch (action.type) {
|
||||
case UPDATE_FLAGS: {
|
||||
const newState = _.merge({}, state, action.flags);
|
||||
const newState = merge({}, state, action.flags);
|
||||
|
||||
return _.isEqual(state, newState) ? state : newState;
|
||||
return isEqual(state, newState) ? state : newState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export default {
|
||||
function normalizeLanguage(language: string) {
|
||||
const [ lang, variant ] = language.replace('_', '-').split('-');
|
||||
|
||||
if (!variant || lang === variant) {
|
||||
if (!variant || lang.toUpperCase() === variant.toUpperCase()) {
|
||||
return lang;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import COUNTRIES_RESOURCES from 'i18n-iso-countries/langs/en.json';
|
||||
import i18next from 'i18next';
|
||||
import I18nextXHRBackend, { HttpBackendOptions } from 'i18next-http-backend';
|
||||
import _ from 'lodash';
|
||||
import { merge } from 'lodash-es';
|
||||
|
||||
import LANGUAGES_RESOURCES from '../../../../lang/languages.json';
|
||||
import MAIN_RESOURCES from '../../../../lang/main.json';
|
||||
@@ -22,7 +22,7 @@ const COUNTRIES_RESOURCES_OVERRIDES = {
|
||||
/**
|
||||
* Merged country names.
|
||||
*/
|
||||
const COUNTRIES = _.merge({}, COUNTRIES_RESOURCES, COUNTRIES_RESOURCES_OVERRIDES);
|
||||
const COUNTRIES = merge({}, COUNTRIES_RESOURCES, COUNTRIES_RESOURCES_OVERRIDES);
|
||||
|
||||
/**
|
||||
* The available/supported languages.
|
||||
|
||||
@@ -46,7 +46,7 @@ export function getJwtName(state: IReduxState) {
|
||||
* @param {string} feature - The feature we want to check.
|
||||
* @param {boolean} ifNoToken - Default value if there is no token.
|
||||
* @param {boolean} ifNotInFeatures - Default value if features prop exists but does not have the {@code feature}.
|
||||
* @returns {bolean}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isJwtFeatureEnabled(state: IReduxState, feature: string, ifNoToken = false, ifNotInFeatures = false) {
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import debounce from 'lodash/debounce';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { SET_FILMSTRIP_ENABLED } from '../../filmstrip/actionTypes';
|
||||
@@ -11,7 +11,6 @@ import { SET_AUDIO_ONLY } from '../audio-only/actionTypes';
|
||||
import { CONFERENCE_JOINED } from '../conference/actionTypes';
|
||||
import { getParticipantById } from '../participants/functions';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import { isLocalVideoTrackDesktop } from '../tracks/functions';
|
||||
|
||||
import { setLastN } from './actions';
|
||||
import logger from './logger';
|
||||
@@ -45,9 +44,7 @@ const _updateLastN = debounce(({ dispatch, getState }: IStore) => {
|
||||
// 3. -1 as the default value.
|
||||
let lastNSelected = config.startLastN ?? (config.channelLastN ?? -1);
|
||||
|
||||
if (typeof appState !== 'undefined' && appState !== 'active') {
|
||||
lastNSelected = isLocalVideoTrackDesktop(state) ? 1 : 0;
|
||||
} else if (carMode) {
|
||||
if (appState === 'background' || carMode) {
|
||||
lastNSelected = 0;
|
||||
} else if (audioOnly) {
|
||||
const { remoteScreenShares, tileViewEnabled } = state['features/video-layout'];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-expect-error
|
||||
import Logger, { getLogger as _getLogger } from '@jitsi/logger';
|
||||
import _ from 'lodash';
|
||||
import { once } from 'lodash-es';
|
||||
|
||||
import LogTransport from './LogTransport';
|
||||
|
||||
@@ -26,7 +26,7 @@ export function getLogger(id: string) {
|
||||
/**
|
||||
* Initializes native logging. This operations must be done as early as possible.
|
||||
*/
|
||||
export const _initLogging = _.once(() => {
|
||||
export const _initLogging = once(() => {
|
||||
if (navigator.product !== 'ReactNative') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { merge } from 'lodash-es';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
@@ -95,7 +95,7 @@ ReducerRegistry.register<ILoggingState>(
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setLoggingConfig(state: ILoggingState, action: AnyAction) {
|
||||
const newConfig = _.merge({}, DEFAULT_STATE.config, action.config);
|
||||
const newConfig = merge({}, DEFAULT_STATE.config, action.config);
|
||||
|
||||
if (equals(state.config, newConfig)) {
|
||||
return state;
|
||||
|
||||
@@ -547,7 +547,7 @@ export function participantKicked(kicker: any, kicked: any) {
|
||||
const state = getState();
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const kickedId = kicked.getId();
|
||||
const kickerId = kicker.getId();
|
||||
const kickerId = kicker?.getId();
|
||||
|
||||
dispatch({
|
||||
type: PARTICIPANT_KICKED,
|
||||
@@ -555,7 +555,7 @@ export function participantKicked(kicker: any, kicked: any) {
|
||||
kicker: kickerId
|
||||
});
|
||||
|
||||
if (kicked.isReplaced?.() || kickerId === localParticipant?.id) {
|
||||
if (kicked.isReplaced?.() || !kickerId || kickerId === localParticipant?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
isTrackStreamingStatusInactive
|
||||
} from '../../../connection-indicator/functions';
|
||||
import SharedVideo from '../../../shared-video/components/native/SharedVideo';
|
||||
import { isSharedVideoEnabled } from '../../../shared-video/functions';
|
||||
import { IStateful } from '../../app/types';
|
||||
import Avatar from '../../avatar/components/Avatar';
|
||||
import { translate } from '../../i18n/functions';
|
||||
@@ -236,7 +237,8 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
_isConnectionInactive: isTrackStreamingStatusInactive(videoTrack),
|
||||
_isSharedVideoParticipant: isSharedVideoParticipant(participant),
|
||||
_participantName: getParticipantDisplayName(state, participantId),
|
||||
_renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
|
||||
_renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo
|
||||
&& isSharedVideoEnabled(state),
|
||||
_videoTrack: videoTrack
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
NOTIFICATION_TIMEOUT_TYPE,
|
||||
RAISE_HAND_NOTIFICATION_ID
|
||||
} from '../../notifications/constants';
|
||||
import { open as openParticipantsPane } from '../../participants-pane/actions';
|
||||
import { isForceMuted } from '../../participants-pane/functions';
|
||||
import { CALLING, INVITED } from '../../presence-status/constants';
|
||||
import { RAISE_HAND_SOUND_ID } from '../../reactions/constants';
|
||||
@@ -783,10 +784,19 @@ function _raiseHandUpdated({ dispatch, getState }: IStore, conference: IJitsiCon
|
||||
|| isForceMuted(participant, MEDIA_TYPE.VIDEO, state);
|
||||
}
|
||||
|
||||
const action = shouldDisplayAllowAction ? {
|
||||
customActionNameKey: [ 'notify.allowAction' ],
|
||||
customActionHandler: [ () => dispatch(approveParticipant(participantId)) ]
|
||||
} : {};
|
||||
let action;
|
||||
|
||||
if (shouldDisplayAllowAction) {
|
||||
action = {
|
||||
customActionNameKey: [ 'notify.allowAction' ],
|
||||
customActionHandler: [ () => dispatch(approveParticipant(participantId)) ]
|
||||
};
|
||||
} else {
|
||||
action = {
|
||||
customActionNameKey: [ 'notify.viewParticipants' ],
|
||||
customActionHandler: [ () => dispatch(openParticipantsPane()) ]
|
||||
};
|
||||
}
|
||||
|
||||
if (raisedHandTimestamp) {
|
||||
let notificationTitle;
|
||||
@@ -810,7 +820,7 @@ function _raiseHandUpdated({ dispatch, getState }: IStore, conference: IJitsiCon
|
||||
concatText: true,
|
||||
uid: RAISE_HAND_NOTIFICATION_ID,
|
||||
...action
|
||||
}, shouldDisplayAllowAction ? NOTIFICATION_TIMEOUT_TYPE.MEDIUM : NOTIFICATION_TIMEOUT_TYPE.SHORT));
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
||||
dispatch(playSound(RAISE_HAND_SOUND_ID));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,17 @@ ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
case NOTIFIED_TO_SPEAK: {
|
||||
return {
|
||||
...state,
|
||||
raisedHandsQueue: [
|
||||
{
|
||||
...state.raisedHandsQueue[0],
|
||||
hasBeenNotified: true
|
||||
},
|
||||
...state.raisedHandsQueue.slice(1)
|
||||
]
|
||||
raisedHandsQueue: state.raisedHandsQueue.map((item, index) => {
|
||||
if (index === 0) {
|
||||
|
||||
return {
|
||||
...item,
|
||||
hasBeenNotified: true
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import { difference } from 'lodash-es';
|
||||
import { batch } from 'react-redux';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { showNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
import { hideNotification, showNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE, RAISE_HAND_NOTIFICATION_ID } from '../../notifications/constants';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { getSsrcRewritingFeatureFlag, hasBeenNotified, isNextToSpeak } from '../config/functions.any';
|
||||
import {
|
||||
getDisableNextSpeakerNotification,
|
||||
getSsrcRewritingFeatureFlag,
|
||||
hasBeenNotified,
|
||||
isNextToSpeak } from '../config/functions.any';
|
||||
import { VIDEO_TYPE } from '../media/constants';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
|
||||
@@ -33,9 +37,16 @@ StateListenerRegistry.register(
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].raisedHandsQueue,
|
||||
/* listener */ (raisedHandsQueue, store) => {
|
||||
if (isNextToSpeak(store.getState()) && !hasBeenNotified(store.getState())) {
|
||||
if (raisedHandsQueue.length
|
||||
&& isNextToSpeak(store.getState())
|
||||
&& !hasBeenNotified(store.getState())
|
||||
&& !getDisableNextSpeakerNotification(store.getState())
|
||||
&& !store.getState()['features/visitors'].iAmVisitor) { // visitors raise hand to be promoted
|
||||
_notifyNextSpeakerInRaisedHandQueue(store);
|
||||
}
|
||||
if (!raisedHandsQueue[0]) {
|
||||
store.dispatch(hideNotification(RAISE_HAND_NOTIFICATION_ID));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -54,8 +65,8 @@ function _createOrRemoveVirtualParticipants(
|
||||
store: IStore): void {
|
||||
const { dispatch, getState } = store;
|
||||
const conference = getCurrentConference(getState());
|
||||
const removedScreenshareSourceNames = _.difference(oldScreenshareSourceNames, newScreenshareSourceNames);
|
||||
const addedScreenshareSourceNames = _.difference(newScreenshareSourceNames, oldScreenshareSourceNames);
|
||||
const removedScreenshareSourceNames = difference(oldScreenshareSourceNames, newScreenshareSourceNames);
|
||||
const addedScreenshareSourceNames = difference(newScreenshareSourceNames, oldScreenshareSourceNames);
|
||||
|
||||
if (removedScreenshareSourceNames.length) {
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import { toArray } from 'react-emoji-render';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import GifMessage from '../../../../chat/components/web/GifMessage';
|
||||
import { GIF_PREFIX } from '../../../../gifs/constants';
|
||||
import { isGifMessage } from '../../../../gifs/functions.web';
|
||||
import { isGifEnabled, isGifMessage } from '../../../../gifs/functions.web';
|
||||
|
||||
import Linkify from './Linkify';
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Whether the gifs are enabled or not.
|
||||
*/
|
||||
gifEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The body of the message.
|
||||
*/
|
||||
@@ -43,11 +50,11 @@ class Message extends Component<IProps> {
|
||||
|
||||
// Tokenize the text in order to avoid emoji substitution for URLs
|
||||
const tokens = text ? text.split(' ') : [];
|
||||
|
||||
const content = [];
|
||||
const { gifEnabled } = this.props;
|
||||
|
||||
// check if the message is a GIF
|
||||
if (isGifMessage(text)) {
|
||||
if (gifEnabled && isGifMessage(text)) {
|
||||
const url = text.substring(GIF_PREFIX.length, text.length - 1);
|
||||
|
||||
content.push(<GifMessage
|
||||
@@ -93,4 +100,16 @@ class Message extends Component<IProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export default Message;
|
||||
/**
|
||||
* Maps part of the redux state to the props of this component.
|
||||
*
|
||||
* @param {IReduxState} state - The Redux state.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
gifEnabled: isGifEnabled(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(Message);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _debounce from 'lodash/debounce';
|
||||
import { debounce } from 'lodash-es';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { MultiSelectItem } from '../../../ui/components/types';
|
||||
@@ -145,7 +145,7 @@ class MultiSelectAutocomplete extends Component<IProps, IState> {
|
||||
this._onFilterChange = this._onFilterChange.bind(this);
|
||||
this._onRetry = this._onRetry.bind(this);
|
||||
this._onSelectionChange = this._onSelectionChange.bind(this);
|
||||
this._sendQuery = _debounce(this._sendQuery.bind(this), 200);
|
||||
this._sendQuery = debounce(this._sendQuery.bind(this), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface IIconButtonProps {
|
||||
accessibilityLabel?: string;
|
||||
color?: string;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
onPress?: (e?: GestureResponderEvent) => void;
|
||||
size?: number | string;
|
||||
src: Function;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { IStateful } from '../app/types';
|
||||
@@ -36,7 +36,7 @@ export function assign<T extends Object>(target: T, source: Partial<T>): T {
|
||||
* comparison); false, otherwise.
|
||||
*/
|
||||
export function equals(a: any, b: any) {
|
||||
return _.isEqual(a, b);
|
||||
return isEqual(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { throttle } from 'lodash-es';
|
||||
|
||||
import MiddlewareRegistry from './MiddlewareRegistry';
|
||||
import PersistenceRegistry from './PersistenceRegistry';
|
||||
@@ -13,10 +13,7 @@ const PERSIST_STATE_DELAY = 2000;
|
||||
/**
|
||||
* A throttled function to avoid repetitive state persisting.
|
||||
*/
|
||||
const throttledPersistState
|
||||
= _.throttle(
|
||||
state => PersistenceRegistry.persistState(state),
|
||||
PERSIST_STATE_DELAY);
|
||||
const throttledPersistState = throttle(state => PersistenceRegistry.persistState(state), PERSIST_STATE_DELAY);
|
||||
|
||||
// Web only code.
|
||||
// We need the <tt>if</tt> because it appears that on mobile the polyfill is not
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { escape } from 'lodash-es';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
@@ -100,8 +100,8 @@ function _updateLocalParticipantFromUrl({ dispatch, getState }: IStore) {
|
||||
const localParticipant = getLocalParticipant(getState());
|
||||
|
||||
if (localParticipant) {
|
||||
const displayName = _.escape(urlDisplayName);
|
||||
const email = _.escape(urlEmail);
|
||||
const displayName = escape(urlDisplayName);
|
||||
const email = escape(urlEmail);
|
||||
|
||||
dispatch(participantUpdated({
|
||||
...localParticipant,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import _ from 'lodash';
|
||||
import { escape } from 'lodash-es';
|
||||
|
||||
import { APP_WILL_MOUNT } from '../app/actionTypes';
|
||||
import PersistenceRegistry from '../redux/PersistenceRegistry';
|
||||
@@ -154,8 +154,8 @@ function _initSettings(featureState: ISettingsState) {
|
||||
// is a defined value, it will override any value found in local storage.
|
||||
// The workaround is sidestepping _.escape when the value is not set in
|
||||
// local storage.
|
||||
const displayName = savedDisplayName === null ? undefined : _.escape(savedDisplayName);
|
||||
const email = savedEmail === null ? undefined : _.escape(savedEmail);
|
||||
const displayName = savedDisplayName === null ? undefined : escape(savedDisplayName);
|
||||
const email = savedEmail === null ? undefined : escape(savedEmail);
|
||||
|
||||
settings = assignIfDefined({
|
||||
displayName,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { isEqual, sortBy } from 'lodash-es';
|
||||
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import { getScreenshareParticipantIds } from '../participants/functions';
|
||||
@@ -16,7 +16,7 @@ StateListenerRegistry.register(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_.isEqual(_.sortBy(participantIDs), _.sortBy(previousParticipantIDs))) {
|
||||
if (!isEqual(sortBy(participantIDs), sortBy(previousParticipantIDs))) {
|
||||
APP.API.notifySharingParticipantsChanged(participantIDs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TouchableHighlight } from 'react-native';
|
||||
import { StyleProp, TouchableHighlight } from 'react-native';
|
||||
import { Button as NativePaperButton, Text } from 'react-native-paper';
|
||||
import { IconSource } from 'react-native-paper/lib/typescript/components/Icon';
|
||||
|
||||
import { BUTTON_MODES, BUTTON_TYPES } from '../../constants.native';
|
||||
import BaseTheme from '../BaseTheme.native';
|
||||
@@ -13,6 +14,7 @@ import styles from './buttonStyles';
|
||||
export interface IProps extends IButtonProps {
|
||||
color?: string | undefined;
|
||||
contentStyle?: Object | undefined;
|
||||
id?: string;
|
||||
labelStyle?: Object | undefined;
|
||||
mode?: any;
|
||||
style?: Object | undefined;
|
||||
@@ -24,6 +26,7 @@ const Button: React.FC<IProps> = ({
|
||||
contentStyle,
|
||||
disabled,
|
||||
icon,
|
||||
id,
|
||||
labelKey,
|
||||
labelStyle,
|
||||
mode = BUTTON_MODES.CONTAINED,
|
||||
@@ -74,16 +77,17 @@ const Button: React.FC<IProps> = ({
|
||||
<TouchableHighlight
|
||||
accessibilityLabel = { accessibilityLabel }
|
||||
disabled = { disabled }
|
||||
id = { id }
|
||||
onPress = { onPress }
|
||||
style = { [
|
||||
buttonStyles,
|
||||
style
|
||||
] }>
|
||||
] as StyleProp<object> }>
|
||||
<Text
|
||||
style = { [
|
||||
buttonLabelStyles,
|
||||
labelStyle
|
||||
] }>{ t(labelKey ?? '') }</Text>
|
||||
] as StyleProp<object> }>{ t(labelKey ?? '') }</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
@@ -96,21 +100,20 @@ const Button: React.FC<IProps> = ({
|
||||
contentStyle = { [
|
||||
styles.buttonContent,
|
||||
contentStyle
|
||||
] }
|
||||
] as StyleProp<object> }
|
||||
disabled = { disabled }
|
||||
|
||||
// @ts-ignore
|
||||
icon = { icon }
|
||||
icon = { icon as IconSource | undefined }
|
||||
id = { id }
|
||||
labelStyle = { [
|
||||
buttonLabelStyles,
|
||||
labelStyle
|
||||
] }
|
||||
] as StyleProp<object> }
|
||||
mode = { mode }
|
||||
onPress = { onPress }
|
||||
style = { [
|
||||
buttonStyles,
|
||||
style
|
||||
] } />
|
||||
] as StyleProp<object> } />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const IconButton: React.FC<IIconButtonProps> = ({
|
||||
accessibilityLabel,
|
||||
color: iconColor,
|
||||
disabled,
|
||||
id,
|
||||
onPress,
|
||||
size,
|
||||
src,
|
||||
@@ -52,6 +53,7 @@ const IconButton: React.FC<IIconButtonProps> = ({
|
||||
<TouchableHighlight
|
||||
accessibilityLabel = { accessibilityLabel }
|
||||
disabled = { disabled }
|
||||
id = { id }
|
||||
onPress = { onPress }
|
||||
style = { [
|
||||
iconButtonContainerStyles,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { ColorValue } from 'react-native';
|
||||
import { ColorValue, StyleProp } from 'react-native';
|
||||
import { Switch as NativeSwitch } from 'react-native-paper';
|
||||
|
||||
import { ISwitchProps } from '../types';
|
||||
@@ -12,6 +12,12 @@ import {
|
||||
|
||||
interface IProps extends ISwitchProps {
|
||||
|
||||
/**
|
||||
* Id for the switch.
|
||||
*/
|
||||
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* Custom styles for the switch.
|
||||
*/
|
||||
@@ -31,6 +37,7 @@ interface IProps extends ISwitchProps {
|
||||
const Switch = ({
|
||||
checked,
|
||||
disabled,
|
||||
id,
|
||||
onChange,
|
||||
thumbColor = THUMB_COLOR,
|
||||
trackColor = {
|
||||
@@ -41,9 +48,10 @@ const Switch = ({
|
||||
}: IProps) => (
|
||||
<NativeSwitch
|
||||
disabled = { disabled }
|
||||
id = { id }
|
||||
ios_backgroundColor = { DISABLED_TRACK_COLOR }
|
||||
onValueChange = { onChange }
|
||||
style = { style }
|
||||
style = { style as StyleProp<object> }
|
||||
thumbColor = { thumbColor }
|
||||
trackColor = { trackColor }
|
||||
value = { checked } />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { NIL, parse as parseUUID } from 'uuid';
|
||||
import zxcvbn from 'zxcvbn';
|
||||
|
||||
@@ -22,7 +22,7 @@ function isValidUUID(str: string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !_.isEqual(uuid, NIL_UUID);
|
||||
return !isEqual(uuid, NIL_UUID);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import i18next from 'i18next';
|
||||
import _ from 'lodash';
|
||||
import { chunk, filter, shuffle } from 'lodash-es';
|
||||
|
||||
import { createBreakoutRoomsEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
@@ -129,14 +129,14 @@ export function removeBreakoutRoom(breakoutRoomJid: string) {
|
||||
export function autoAssignToBreakoutRooms() {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const rooms = getBreakoutRooms(getState);
|
||||
const breakoutRooms = _.filter(rooms, room => !room.isMainRoom);
|
||||
const breakoutRooms = filter(rooms, room => !room.isMainRoom);
|
||||
|
||||
if (breakoutRooms) {
|
||||
sendAnalytics(createBreakoutRoomsEvent('auto.assign'));
|
||||
const participantIds = Array.from(getRemoteParticipants(getState).keys());
|
||||
const length = Math.ceil(participantIds.length / breakoutRooms.length);
|
||||
|
||||
_.chunk(_.shuffle(participantIds), length).forEach((group, index) =>
|
||||
chunk(shuffle(participantIds), length).forEach((group, index) =>
|
||||
group.forEach(participantId => {
|
||||
dispatch(sendParticipantToRoom(participantId, breakoutRooms[index].id));
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { find } from 'lodash-es';
|
||||
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { getCurrentConference } from '../base/conference/functions';
|
||||
@@ -33,7 +33,7 @@ export const getBreakoutRooms = (stateful: IStateful): IRooms => toState(statefu
|
||||
export const getMainRoom = (stateful: IStateful) => {
|
||||
const rooms = getBreakoutRooms(stateful);
|
||||
|
||||
return _.find(rooms, room => Boolean(room.isMainRoom));
|
||||
return find(rooms, room => Boolean(room.isMainRoom));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -135,7 +135,7 @@ export const getRoomsInfo = (stateful: IStateful) => {
|
||||
export const getRoomByJid = (stateful: IStateful, roomJid: string) => {
|
||||
const rooms = getBreakoutRooms(stateful);
|
||||
|
||||
return _.find(rooms, (room: IRoom) => room.jid === roomJid);
|
||||
return find(rooms, (room: IRoom) => room.jid === roomJid);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,12 +83,12 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
const { messages } = getState()['features/chat'];
|
||||
|
||||
messages?.forEach(m => {
|
||||
if (m.messageType === MESSAGE_TYPE_REMOTE && !getParticipantById(getState(), m.id)) {
|
||||
if (m.messageType === MESSAGE_TYPE_REMOTE && !getParticipantById(getState(), m.participantId)) {
|
||||
const rooms: IRooms = action.rooms;
|
||||
|
||||
for (const room of Object.values(rooms)) {
|
||||
const participants = room.participants || {};
|
||||
const matchedJid = Object.keys(participants).find(jid => jid.endsWith(m.id));
|
||||
const matchedJid = Object.keys(participants).find(jid => jid.endsWith(m.participantId));
|
||||
|
||||
if (matchedJid) {
|
||||
m.displayName = participants[matchedJid].displayName;
|
||||
|
||||
@@ -36,13 +36,13 @@ export default class AbstractMessageContainer<P extends IProps, S> extends Compo
|
||||
for (let i = 0; i < messagesCount; i++) {
|
||||
const message = this.props.messages[i];
|
||||
|
||||
if (message.id === currentGroupParticipantId) {
|
||||
if (message.participantId === currentGroupParticipantId) {
|
||||
currentGrouping.push(message);
|
||||
} else {
|
||||
currentGrouping.length && groups.push(currentGrouping);
|
||||
|
||||
currentGrouping = [ message ];
|
||||
currentGroupParticipantId = message.id;
|
||||
currentGroupParticipantId = message.participantId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ class ChatInputBar extends Component<IProps, IState> {
|
||||
|
||||
return (
|
||||
<View
|
||||
id = 'chat-input'
|
||||
style = { [
|
||||
inputBarStyles,
|
||||
this.state.addPadding ? styles.extraBarPadding : null
|
||||
@@ -91,6 +92,7 @@ class ChatInputBar extends Component<IProps, IState> {
|
||||
<Input
|
||||
blurOnSubmit = { false }
|
||||
customStyles = {{ container: styles.customInputContainer }}
|
||||
id = 'chat-input-messagebox'
|
||||
multiline = { false }
|
||||
onBlur = { this._onFocused(false) }
|
||||
onChange = { this._onChangeText }
|
||||
@@ -101,6 +103,7 @@ class ChatInputBar extends Component<IProps, IState> {
|
||||
value = { this.state.message } />
|
||||
<IconButton
|
||||
disabled = { !this.state.message }
|
||||
id = { this.props.t('chat.sendButton') }
|
||||
onPress = { this._onSubmit }
|
||||
src = { IconSend }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
|
||||
@@ -6,8 +6,8 @@ import { IReduxState } from '../../../app/types';
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Linkify from '../../../base/react/components/native/Linkify';
|
||||
import { isGifMessage } from '../../../gifs/functions.native';
|
||||
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import { isGifEnabled, isGifMessage } from '../../../gifs/functions.native';
|
||||
import { CHAR_LIMIT, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import {
|
||||
getCanReplyToMessage,
|
||||
getFormattedTimestamp,
|
||||
@@ -32,7 +32,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { message, knocking } = this.props;
|
||||
const { gifEnabled, message, knocking } = this.props;
|
||||
const localMessage = message.messageType === MESSAGE_TYPE_LOCAL;
|
||||
const { privateMessage, lobbyChat } = message;
|
||||
|
||||
@@ -73,24 +73,20 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
messageBubbleStyle.push(styles.lobbyMessageBubble);
|
||||
}
|
||||
|
||||
const messageText = replaceNonUnicodeEmojis(getMessageText(this.props.message));
|
||||
const messageText = getMessageText(this.props.message);
|
||||
|
||||
return (
|
||||
<View style = { styles.messageWrapper as ViewStyle } >
|
||||
<View
|
||||
id = { message.messageId }
|
||||
style = { styles.messageWrapper as ViewStyle } >
|
||||
{ this._renderAvatar() }
|
||||
<View style = { detailsWrapperStyle }>
|
||||
<View style = { messageBubbleStyle }>
|
||||
<View style = { styles.textWrapper as ViewStyle } >
|
||||
{ this._renderDisplayName() }
|
||||
{ isGifMessage(messageText)
|
||||
{ gifEnabled && isGifMessage(messageText)
|
||||
? <GifMessage message = { messageText } />
|
||||
: (
|
||||
<Linkify
|
||||
linkStyle = { styles.chatLink }
|
||||
style = { styles.chatMessage }>
|
||||
{ messageText }
|
||||
</Linkify>
|
||||
)}
|
||||
: this._renderMessageTextComponent(messageText) }
|
||||
{ this._renderPrivateNotice() }
|
||||
</View>
|
||||
{ this._renderPrivateReplyButton() }
|
||||
@@ -104,7 +100,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
/**
|
||||
* Renders the avatar of the sender.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
* @returns {React.ReactElement<*>}
|
||||
*/
|
||||
_renderAvatar() {
|
||||
const { message } = this.props;
|
||||
@@ -113,7 +109,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
<View style = { styles.avatarWrapper }>
|
||||
{ this.props.showAvatar && <Avatar
|
||||
displayName = { message.displayName }
|
||||
participantId = { message.id }
|
||||
participantId = { message.participantId }
|
||||
size = { styles.avatarWrapper.width } />
|
||||
}
|
||||
</View>
|
||||
@@ -123,7 +119,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
/**
|
||||
* Renders the display name of the sender if necessary.
|
||||
*
|
||||
* @returns {React$Element<*> | null}
|
||||
* @returns {React.ReactElement<*> | null}
|
||||
*/
|
||||
_renderDisplayName() {
|
||||
const { message, showDisplayName } = this.props;
|
||||
@@ -139,10 +135,37 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the message text based on number of characters.
|
||||
*
|
||||
* @param {string} messageText - The message text.
|
||||
* @returns {React.ReactElement<*>}
|
||||
*/
|
||||
_renderMessageTextComponent(messageText: string) {
|
||||
|
||||
if (messageText.length >= CHAR_LIMIT) {
|
||||
return (
|
||||
<Text
|
||||
selectable = { true }
|
||||
style = { styles.chatMessage }>
|
||||
{ messageText }
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Linkify
|
||||
linkStyle = { styles.chatLink }
|
||||
style = { styles.chatMessage }>
|
||||
{ replaceNonUnicodeEmojis(messageText) }
|
||||
</Linkify>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the message privacy notice, if necessary.
|
||||
*
|
||||
* @returns {React$Element<*> | null}
|
||||
* @returns {React.ReactElement<*> | null}
|
||||
*/
|
||||
_renderPrivateNotice() {
|
||||
const { message, knocking } = this.props;
|
||||
@@ -161,7 +184,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
/**
|
||||
* Renders the private reply button, if necessary.
|
||||
*
|
||||
* @returns {React$Element<*> | null}
|
||||
* @returns {React.ReactElement<*> | null}
|
||||
*/
|
||||
_renderPrivateReplyButton() {
|
||||
const { message, canReply } = this.props;
|
||||
@@ -175,7 +198,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
<View style = { styles.replyContainer as ViewStyle }>
|
||||
<PrivateMessageButton
|
||||
isLobbyMessage = { lobbyChat }
|
||||
participantID = { message.id }
|
||||
participantID = { message.participantId }
|
||||
reply = { true }
|
||||
showLabel = { false }
|
||||
toggledStyles = { styles.replyStyles } />
|
||||
@@ -186,7 +209,7 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
/**
|
||||
* Renders the time at which the message was sent, if necessary.
|
||||
*
|
||||
* @returns {React$Element<*> | null}
|
||||
* @returns {React.ReactElement<*> | null}
|
||||
*/
|
||||
_renderTimestamp() {
|
||||
if (!this.props.showTimestamp) {
|
||||
@@ -205,11 +228,13 @@ class ChatMessage extends Component<IChatMessageProps> {
|
||||
* Maps part of the redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {IChatMessageProps} message - Message object.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState, { message }: IChatMessageProps) {
|
||||
return {
|
||||
canReply: getCanReplyToMessage(state, message),
|
||||
gifEnabled: isGifEnabled(state),
|
||||
knocking: state['features/lobby'].knocking
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ const GifMessage = ({ message }: IProps) => {
|
||||
const url = message.substring(GIF_PREFIX.length, message.length - 1);
|
||||
|
||||
return (<View
|
||||
id = 'gif-message'
|
||||
style = { styles.gifContainer }>
|
||||
<Image
|
||||
source = {{ uri: url }}
|
||||
|
||||
@@ -78,7 +78,9 @@ class MessageContainer extends AbstractMessageContainer<IProps, any> {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<View style = { styles.emptyComponentWrapper as ViewStyle }>
|
||||
<View
|
||||
id = 'no-messages-message'
|
||||
style = { styles.emptyComponentWrapper as ViewStyle }>
|
||||
<Text style = { styles.emptyComponentText as TextStyle }>
|
||||
{ t('chat.noMessagesMessage') }
|
||||
</Text>
|
||||
|
||||
@@ -102,7 +102,9 @@ class MessageRecipient extends AbstractMessageRecipient<IProps> {
|
||||
|
||||
if (isLobbyChatActive) {
|
||||
return (
|
||||
<View style = { styles.lobbyMessageRecipientContainer as ViewStyle }>
|
||||
<View
|
||||
id = 'chat-recipient'
|
||||
style = { styles.lobbyMessageRecipientContainer as ViewStyle }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.lobbyChatMessageTo', {
|
||||
recipient: lobbyMessageRecipient?.name
|
||||
@@ -123,13 +125,16 @@ class MessageRecipient extends AbstractMessageRecipient<IProps> {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.messageRecipientContainer as ViewStyle }>
|
||||
<View
|
||||
id = 'message-recipient'
|
||||
style = { styles.messageRecipientContainer as ViewStyle }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.messageTo', {
|
||||
recipient: privateMessageRecipient.name
|
||||
}) }
|
||||
</Text>
|
||||
<TouchableHighlight
|
||||
id = 'message-recipient-cancel-button'
|
||||
onPress = { this._onResetPrivateMessageRecipient }
|
||||
underlayColor = { 'transparent' }>
|
||||
<Icon
|
||||
|
||||
@@ -197,7 +197,7 @@ const ChatMessage = ({
|
||||
className = { classes.replyButtonContainer }>
|
||||
<PrivateMessageButton
|
||||
isLobbyMessage = { message.lobbyChat }
|
||||
participantID = { message.id } />
|
||||
participantID = { message.participantId } />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -66,7 +66,7 @@ const ChatMessageGroup = ({ className = '', messages }: IProps) => {
|
||||
<div className = { clsx(classes.groupContainer, className) }>
|
||||
<Avatar
|
||||
className = { clsx(classes.avatar, 'avatar') }
|
||||
participantId = { messages[0].id }
|
||||
participantId = { messages[0].participantId }
|
||||
size = { 32 } />
|
||||
<div className = { `${classes.messageGroup} chat-message-group ${className}` }>
|
||||
{messages.map((message, i) => (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import throttle from 'lodash/throttle';
|
||||
import { throttle } from 'lodash-es';
|
||||
import React, { RefObject } from 'react';
|
||||
import { scrollIntoView } from 'seamless-scroll-polyfill';
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Maximum number of characters allowed.
|
||||
*/
|
||||
export const CHAR_LIMIT = 500;
|
||||
|
||||
/**
|
||||
* The size of the chat. Equal to $sidebarWidth SCSS variable.
|
||||
*/
|
||||
|
||||
@@ -172,7 +172,7 @@ export function getMessageText(message: IMessage) {
|
||||
*/
|
||||
export function getCanReplyToMessage(state: IReduxState, message: IMessage) {
|
||||
const { knocking } = state['features/lobby'];
|
||||
const participant = getParticipantById(state, message.id);
|
||||
const participant = getParticipantById(state, message.participantId);
|
||||
|
||||
return Boolean(participant)
|
||||
&& (message.privateMessage || (message.lobbyChat && !knocking))
|
||||
|
||||
@@ -25,7 +25,7 @@ import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
|
||||
import { addGif } from '../gifs/actions';
|
||||
import { GIF_PREFIX } from '../gifs/constants';
|
||||
import { getGifDisplayMode, isGifMessage } from '../gifs/function.any';
|
||||
import { getGifDisplayMode, isGifEnabled, isGifMessage } from '../gifs/function.any';
|
||||
import { showMessageNotification } from '../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants';
|
||||
import { resetNbUnreadPollsMessages } from '../polls/actions';
|
||||
@@ -125,7 +125,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
store.dispatch(pushReactions(data.reactions));
|
||||
|
||||
_handleReceivedMessage(store, {
|
||||
id: participant.getId(),
|
||||
participantId: participant.getId(),
|
||||
message: getReactionMessageFromBuffer(data.reactions),
|
||||
privateMessage: false,
|
||||
lobbyChat: false,
|
||||
@@ -137,12 +137,12 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
|
||||
case NON_PARTICIPANT_MESSAGE_RECEIVED: {
|
||||
const { id, json: data } = action;
|
||||
const { participantId, json: data } = action;
|
||||
|
||||
if (data?.type === MESSAGE_TYPE_SYSTEM && data.message) {
|
||||
_handleReceivedMessage(store, {
|
||||
displayName: data.displayName ?? i18next.t('chat.systemDisplayName'),
|
||||
id,
|
||||
participantId,
|
||||
lobbyChat: false,
|
||||
message: data.message,
|
||||
privateMessage: true,
|
||||
@@ -213,7 +213,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
case ADD_REACTION_MESSAGE: {
|
||||
if (localParticipant?.id) {
|
||||
_handleReceivedMessage(store, {
|
||||
id: localParticipant.id,
|
||||
participantId: localParticipant.id,
|
||||
message: action.message,
|
||||
privateMessage: false,
|
||||
timestamp: Date.now(),
|
||||
@@ -274,25 +274,30 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.MESSAGE_RECEIVED,
|
||||
// eslint-disable-next-line max-params
|
||||
(id: string, message: string, timestamp: number, displayName: string, isGuest?: boolean) => {
|
||||
/* eslint-disable max-params */
|
||||
(participantId: string, message: string, timestamp: number,
|
||||
displayName: string, isGuest: boolean, messageId: string) => {
|
||||
/* eslint-enable max-params */
|
||||
_onConferenceMessageReceived(store, {
|
||||
id: id || displayName, // in case of messages coming from visitors we can have unknown id
|
||||
// in case of messages coming from visitors we can have unknown id
|
||||
participantId: participantId || displayName,
|
||||
message,
|
||||
timestamp,
|
||||
displayName,
|
||||
isGuest,
|
||||
messageId,
|
||||
privateMessage: false });
|
||||
}
|
||||
);
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.PRIVATE_MESSAGE_RECEIVED,
|
||||
(id: string, message: string, timestamp: number) => {
|
||||
(participantId: string, message: string, timestamp: number, messageId: string) => {
|
||||
_onConferenceMessageReceived(store, {
|
||||
id,
|
||||
participantId,
|
||||
message,
|
||||
timestamp,
|
||||
messageId,
|
||||
privateMessage: true
|
||||
});
|
||||
}
|
||||
@@ -311,25 +316,29 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
|
||||
* @param {Object} message - The message object.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onConferenceMessageReceived(store: IStore, { displayName, id, isGuest, message, timestamp, privateMessage }: {
|
||||
displayName?: string; id: string; isGuest?: boolean;
|
||||
message: string; privateMessage: boolean; timestamp: number; }) {
|
||||
const isGif = isGifMessage(message);
|
||||
function _onConferenceMessageReceived(store: IStore,
|
||||
{ displayName, isGuest, message, messageId, participantId, privateMessage, timestamp }: {
|
||||
displayName?: string; isGuest?: boolean; message: string; messageId?: string;
|
||||
participantId: string; privateMessage: boolean; timestamp: number; }
|
||||
) {
|
||||
|
||||
const isGif = isGifEnabled(store.getState()) && isGifMessage(message);
|
||||
|
||||
if (isGif) {
|
||||
_handleGifMessageReceived(store, id, message);
|
||||
_handleGifMessageReceived(store, participantId, message);
|
||||
if (getGifDisplayMode(store.getState()) === 'tile') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_handleReceivedMessage(store, {
|
||||
displayName,
|
||||
id,
|
||||
isGuest,
|
||||
participantId,
|
||||
message,
|
||||
privateMessage,
|
||||
lobbyChat: false,
|
||||
timestamp
|
||||
timestamp,
|
||||
messageId
|
||||
}, true, isGif);
|
||||
}
|
||||
|
||||
@@ -337,14 +346,14 @@ function _onConferenceMessageReceived(store: IStore, { displayName, id, isGuest,
|
||||
* Handles a received gif message.
|
||||
*
|
||||
* @param {Object} store - Redux store.
|
||||
* @param {string} id - Id of the participant that sent the message.
|
||||
* @param {string} participantId - Id of the participant that sent the message.
|
||||
* @param {string} message - The message sent.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _handleGifMessageReceived(store: IStore, id: string, message: string) {
|
||||
function _handleGifMessageReceived(store: IStore, participantId: string, message: string) {
|
||||
const url = message.substring(GIF_PREFIX.length, message.length - 1);
|
||||
|
||||
store.dispatch(addGif(id, url));
|
||||
store.dispatch(addGif(participantId, url));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +383,7 @@ function _handleChatError({ dispatch }: IStore, error: Error) {
|
||||
export function handleLobbyMessageReceived(message: string, participantId: string) {
|
||||
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
_handleReceivedMessage({ dispatch,
|
||||
getState }, { id: participantId,
|
||||
getState }, { participantId,
|
||||
message,
|
||||
privateMessage: false,
|
||||
lobbyChat: true,
|
||||
@@ -387,18 +396,18 @@ export function handleLobbyMessageReceived(message: string, participantId: strin
|
||||
* Function to get lobby chat user display name.
|
||||
*
|
||||
* @param {Store} state - The Redux store.
|
||||
* @param {string} id - The knocking participant id.
|
||||
* @param {string} participantId - The knocking participant id.
|
||||
* @returns {string}
|
||||
*/
|
||||
function getLobbyChatDisplayName(state: IReduxState, id: string) {
|
||||
function getLobbyChatDisplayName(state: IReduxState, participantId: string) {
|
||||
const { knockingParticipants } = state['features/lobby'];
|
||||
const { lobbyMessageRecipient } = state['features/chat'];
|
||||
|
||||
if (id === lobbyMessageRecipient?.id) {
|
||||
if (participantId === lobbyMessageRecipient?.id) {
|
||||
return lobbyMessageRecipient.name;
|
||||
}
|
||||
|
||||
const knockingParticipant = knockingParticipants.find(p => p.id === id);
|
||||
const knockingParticipant = knockingParticipants.find(p => p.id === participantId);
|
||||
|
||||
if (knockingParticipant) {
|
||||
return knockingParticipant.name;
|
||||
@@ -417,9 +426,9 @@ function getLobbyChatDisplayName(state: IReduxState, id: string) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
{ displayName, id, isGuest, message, privateMessage, timestamp, lobbyChat }: {
|
||||
displayName?: string; id: string; isGuest?: boolean; lobbyChat: boolean;
|
||||
message: string; privateMessage: boolean; timestamp: number; },
|
||||
{ displayName, isGuest, lobbyChat, message, messageId, participantId, privateMessage, timestamp }: {
|
||||
displayName?: string; isGuest?: boolean; lobbyChat: boolean; message: string;
|
||||
messageId?: string; participantId: string; privateMessage: boolean; timestamp: number; },
|
||||
shouldPlaySound = true,
|
||||
isReaction = false
|
||||
) {
|
||||
@@ -434,12 +443,12 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
|
||||
// Provide a default for the case when a message is being
|
||||
// backfilled for a participant that has left the conference.
|
||||
const participant = getParticipantById(state, id) || { local: undefined };
|
||||
const participant = getParticipantById(state, participantId) || { local: undefined };
|
||||
|
||||
const localParticipant = getLocalParticipant(getState);
|
||||
let displayNameToShow = lobbyChat
|
||||
? getLobbyChatDisplayName(state, id)
|
||||
: displayName || getParticipantDisplayName(state, id);
|
||||
? getLobbyChatDisplayName(state, participantId)
|
||||
: displayName || getParticipantDisplayName(state, participantId);
|
||||
const hasRead = participant.local || isChatOpen;
|
||||
const timestampToDate = timestamp ? new Date(timestamp) : new Date();
|
||||
const millisecondsTimestamp = timestampToDate.getTime();
|
||||
@@ -455,13 +464,14 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
dispatch(addMessage({
|
||||
displayName: displayNameToShow,
|
||||
hasRead,
|
||||
id,
|
||||
participantId,
|
||||
messageType: participant.local ? MESSAGE_TYPE_LOCAL : MESSAGE_TYPE_REMOTE,
|
||||
message,
|
||||
privateMessage,
|
||||
lobbyChat,
|
||||
recipient: getParticipantDisplayName(state, localParticipant?.id ?? ''),
|
||||
timestamp: millisecondsTimestamp,
|
||||
messageId,
|
||||
isReaction
|
||||
}));
|
||||
|
||||
@@ -477,7 +487,7 @@ function _handleReceivedMessage({ dispatch, getState }: IStore,
|
||||
|
||||
APP.API.notifyReceivedChatMessage({
|
||||
body: message,
|
||||
id,
|
||||
from: participantId,
|
||||
nick: displayNameToShow,
|
||||
privateMessage,
|
||||
ts: timestamp
|
||||
@@ -512,7 +522,7 @@ function _persistSentPrivateMessage({ dispatch, getState }: IStore, recipientID:
|
||||
dispatch(addMessage({
|
||||
displayName,
|
||||
hasRead: true,
|
||||
id: localParticipant.id,
|
||||
participantId: localParticipant.id,
|
||||
messageType: MESSAGE_TYPE_LOCAL,
|
||||
message,
|
||||
privateMessage: !isLobbyPrivateMessage,
|
||||
@@ -562,7 +572,7 @@ function _shouldSendPrivateMessageTo(state: IReduxState, action: AnyAction) {
|
||||
|
||||
if (lastMessage.privateMessage) {
|
||||
// We show the notice if the last received message was private.
|
||||
return lastMessage.id;
|
||||
return lastMessage.participantId;
|
||||
}
|
||||
|
||||
// But messages may come rapidly, we want to protect our users from mis-sending a message
|
||||
@@ -577,7 +587,7 @@ function _shouldSendPrivateMessageTo(state: IReduxState, action: AnyAction) {
|
||||
? recentPrivateMessages[0] : recentPrivateMessages[recentPrivateMessages.length - 1];
|
||||
|
||||
if (recentPrivateMessage) {
|
||||
return recentPrivateMessage.id;
|
||||
return recentPrivateMessage.participantId;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { ILocalParticipant, IParticipant } from '../base/participants/types';
|
||||
import ReducerRegistry from '../base/redux/ReducerRegistry';
|
||||
|
||||
@@ -48,9 +46,9 @@ ReducerRegistry.register<IChatState>('features/chat', (state = DEFAULT_STATE, ac
|
||||
const newMessage: IMessage = {
|
||||
displayName: action.displayName,
|
||||
error: action.error,
|
||||
id: action.id,
|
||||
participantId: action.participantId,
|
||||
isReaction: action.isReaction,
|
||||
messageId: uuidv4(),
|
||||
messageId: action.messageId,
|
||||
messageType: action.messageType,
|
||||
message: action.message,
|
||||
privateMessage: action.privateMessage,
|
||||
|
||||
@@ -5,12 +5,12 @@ import { IStore } from '../app/types';
|
||||
export interface IMessage {
|
||||
displayName: string;
|
||||
error?: Object;
|
||||
id: string;
|
||||
isReaction: boolean;
|
||||
lobbyChat: boolean;
|
||||
message: string;
|
||||
messageId: string;
|
||||
messageType: string;
|
||||
participantId: string;
|
||||
privateMessage: boolean;
|
||||
recipient: string;
|
||||
timestamp: number;
|
||||
@@ -44,6 +44,11 @@ export interface IChatMessageProps extends WithTranslation {
|
||||
*/
|
||||
canReply?: boolean;
|
||||
|
||||
/**
|
||||
* Whether gifs are enabled or not.
|
||||
*/
|
||||
gifEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* Whether current participant is currently knocking in the lobby room.
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ import { connect, useDispatch } from 'react-redux';
|
||||
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';
|
||||
import { FULLSCREEN_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import Container from '../../../base/react/components/native/Container';
|
||||
import LoadingIndicator from '../../../base/react/components/native/LoadingIndicator';
|
||||
@@ -38,7 +38,7 @@ import LargeVideo from '../../../large-video/components/LargeVideo.native';
|
||||
import { getIsLobbyVisible } from '../../../lobby/functions';
|
||||
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import { setPictureInPictureEnabled } from '../../../mobile/picture-in-picture/functions';
|
||||
import { isPipEnabled, setPictureInPictureEnabled } from '../../../mobile/picture-in-picture/functions';
|
||||
import Captions from '../../../subtitles/components/native/Captions';
|
||||
import { setToolboxVisible } from '../../../toolbox/actions.native';
|
||||
import Toolbox from '../../../toolbox/components/native/Toolbox';
|
||||
@@ -579,7 +579,7 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
_fullscreenEnabled: getFeatureFlag(state, FULLSCREEN_ENABLED, true),
|
||||
_isParticipantsPaneOpen: isOpen,
|
||||
_largeVideoParticipantId: state['features/large-video'].participantId,
|
||||
_pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED),
|
||||
_pictureInPictureEnabled: isPipEnabled(state),
|
||||
_reducedUI: reducedUI,
|
||||
_showLobby: getIsLobbyVisible(state),
|
||||
_startCarMode: startCarMode,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { throttle } from 'lodash-es';
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect as reactReduxConnect } from 'react-redux';
|
||||
@@ -133,7 +133,7 @@ class Conference extends AbstractConference<IProps, any> {
|
||||
this._originalOnShowToolbar = this._onShowToolbar;
|
||||
this._originalOnMouseMove = this._onMouseMove;
|
||||
|
||||
this._onShowToolbar = _.throttle(
|
||||
this._onShowToolbar = throttle(
|
||||
() => this._originalOnShowToolbar(),
|
||||
100,
|
||||
{
|
||||
@@ -141,7 +141,7 @@ class Conference extends AbstractConference<IProps, any> {
|
||||
trailing: false
|
||||
});
|
||||
|
||||
this._onMouseMove = _.throttle(
|
||||
this._onMouseMove = throttle(
|
||||
event => this._originalOnMouseMove(event),
|
||||
_mouseMoveCallbackInterval,
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ENDPOINT_MESSAGE_RECEIVED
|
||||
} from '../base/conference/actionTypes';
|
||||
import { getCurrentConference } from '../base/conference/functions';
|
||||
import { getDisableLowerHandByModerator } from '../base/config/functions.any';
|
||||
import { getURLWithoutParamsNormalized } from '../base/connection/utils';
|
||||
import { hideDialog } from '../base/dialog/actions';
|
||||
import { isDialogOpen } from '../base/dialog/functions';
|
||||
@@ -75,9 +76,11 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
case ENDPOINT_MESSAGE_RECEIVED: {
|
||||
const { participant, data } = action;
|
||||
const { dispatch } = store;
|
||||
const { dispatch, getState } = store;
|
||||
|
||||
if (data.name === LOWER_HAND_MESSAGE && participant.isModerator()) {
|
||||
if (data.name === LOWER_HAND_MESSAGE
|
||||
&& participant.isModerator()
|
||||
&& !getDisableLowerHandByModerator(getState())) {
|
||||
dispatch(raiseHand(false));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { union } from 'lodash-es';
|
||||
|
||||
import { IJitsiConference } from '../base/conference/reducer';
|
||||
import {
|
||||
@@ -143,7 +143,7 @@ const statsEmitter = {
|
||||
const resolutionUserIds = Object.keys(allUserResolutions);
|
||||
const codecUserIds = Object.keys(allUserCodecs);
|
||||
|
||||
_.union(framerateUserIds, resolutionUserIds, codecUserIds)
|
||||
union(framerateUserIds, resolutionUserIds, codecUserIds)
|
||||
.filter(id => id !== localUserId)
|
||||
.forEach(id => {
|
||||
const remoteUserStats: IStats = {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import { throttle } from 'lodash-es';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -286,7 +286,7 @@ class Filmstrip extends PureComponent <IProps, IState> {
|
||||
this._onDragMouseUp = this._onDragMouseUp.bind(this);
|
||||
this._onFilmstripResize = this._onFilmstripResize.bind(this);
|
||||
|
||||
this._throttledResize = _.throttle(
|
||||
this._throttledResize = throttle(
|
||||
this._onFilmstripResize,
|
||||
50,
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Theme } from '@mui/material';
|
||||
import clsx from 'clsx';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { debounce } from 'lodash-es';
|
||||
import React, { Component, KeyboardEvent, RefObject, createRef } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { differenceWith, isEqual } from 'lodash-es';
|
||||
|
||||
import { IStore } from '../app/types';
|
||||
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
|
||||
@@ -184,9 +184,9 @@ function _onFollowMeCommand(attributes: any = {}, id: string, store: IStore) {
|
||||
oldStageParticipants = JSON.parse(oldState.pinnedStageParticipants);
|
||||
}
|
||||
|
||||
if (!_.isEqual(stageParticipants, oldStageParticipants)) {
|
||||
const toRemove = _.differenceWith(oldStageParticipants, stageParticipants, _.isEqual);
|
||||
const toAdd = _.differenceWith(stageParticipants, oldStageParticipants, _.isEqual);
|
||||
if (!isEqual(stageParticipants, oldStageParticipants)) {
|
||||
const toRemove = differenceWith(oldStageParticipants, stageParticipants, isEqual);
|
||||
const toAdd = differenceWith(stageParticipants, oldStageParticipants, isEqual);
|
||||
|
||||
toRemove.forEach((p: { participantId: string; }) =>
|
||||
store.dispatch(removeStageParticipant(p.participantId)));
|
||||
|
||||
@@ -9,7 +9,7 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import { sendMessage } from '../../../chat/actions.any';
|
||||
import { goBack } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { formatGifUrlMessage, getGifRating, getGifUrl, getGiphyProxyUrl } from '../../functions.native';
|
||||
import { formatGifUrlMessage, getGifRating, getGifUrl } from '../../functions.native';
|
||||
|
||||
import GifsMenuFooter from './GifsMenuFooter';
|
||||
import styles from './styles';
|
||||
@@ -19,8 +19,6 @@ const GifsMenu = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const rating = useSelector(getGifRating) as GiphyRating;
|
||||
const proxyUrl = useSelector(getGiphyProxyUrl);
|
||||
|
||||
const options = {
|
||||
mediaType: GiphyMediaType.Gif,
|
||||
limit: 20,
|
||||
@@ -35,7 +33,7 @@ const GifsMenu = () => {
|
||||
});
|
||||
|
||||
const sendGif = useCallback(e => {
|
||||
const url = getGifUrl(e.nativeEvent.media, proxyUrl);
|
||||
const url = getGifUrl(e.nativeEvent.media);
|
||||
|
||||
sendAnalytics(createGifSentEvent());
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GiphyFetch, TrendingOptions, setServerUrl } from '@giphy/js-fetch-api';
|
||||
import { GiphyFetch, TrendingOptions } from '@giphy/js-fetch-api';
|
||||
import { Grid } from '@giphy/react-components';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -20,8 +20,7 @@ import {
|
||||
formatGifUrlMessage,
|
||||
getGifAPIKey,
|
||||
getGifRating,
|
||||
getGifUrl,
|
||||
getGiphyProxyUrl
|
||||
getGifUrl
|
||||
} from '../../function.any';
|
||||
|
||||
const OVERFLOW_DRAWER_PADDING = 16;
|
||||
@@ -104,7 +103,6 @@ function GifsMenu({ columns = 2, parent }: IProps) {
|
||||
= parent === IReactionsMenuParent.OverflowDrawer || parent === IReactionsMenuParent.OverflowMenu;
|
||||
const { clientWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
|
||||
const rating = useSelector(getGifRating);
|
||||
const proxyUrl = useSelector(getGiphyProxyUrl);
|
||||
|
||||
const fetchGifs = useCallback(async (offset = 0) => {
|
||||
const options: TrendingOptions = {
|
||||
@@ -126,7 +124,7 @@ function GifsMenu({ columns = 2, parent }: IProps) {
|
||||
|
||||
const handleGifClick = useCallback((gif, e) => {
|
||||
e?.stopPropagation();
|
||||
const url = getGifUrl(gif, proxyUrl);
|
||||
const url = getGifUrl(gif);
|
||||
|
||||
sendAnalytics(createGifSentEvent());
|
||||
batch(() => {
|
||||
@@ -189,12 +187,6 @@ function GifsMenu({ columns = 2, parent }: IProps) {
|
||||
// This fixes that.
|
||||
useEffect(() => setSearchKey(''), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (proxyUrl) {
|
||||
setServerUrl(proxyUrl);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onInputKeyPress = useCallback((e: React.KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
@@ -33,15 +33,6 @@ export function getGifRating(state: IReduxState) {
|
||||
return getGifConfig(state).rating || GIF_DEFAULT_RATING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Giphy proxy url.
|
||||
*
|
||||
* @param {IReduxState} state - Redux state.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getGiphyProxyUrl(state: IReduxState) {
|
||||
return getGifConfig(state).proxyUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the GIF for the given participant or null if there's none.
|
||||
@@ -54,6 +45,26 @@ export function getGifForParticipant(state: IReduxState, participantId: string):
|
||||
return isGifEnabled(state) ? state['features/gifs'].gifList.get(participantId) || {} : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a given URL is allowed to be rendered as gif and false otherwise.
|
||||
*
|
||||
* @param {string} url - The URL to be validated.
|
||||
* @returns {boolean} - True if a given URL is allowed to be rendered as gif and false otherwise.
|
||||
*/
|
||||
export function isGifUrlAllowed(url: string) {
|
||||
let hostname: string | undefined;
|
||||
|
||||
try {
|
||||
const urlObject = new URL(url);
|
||||
|
||||
hostname = urlObject?.hostname;
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hostname === 'i.giphy.com';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the message is a GIF message.
|
||||
*
|
||||
@@ -61,26 +72,23 @@ export function getGifForParticipant(state: IReduxState, participantId: string):
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isGifMessage(message: string) {
|
||||
const url = message.substring(GIF_PREFIX.length, message.length - 1);
|
||||
|
||||
return message.trim().toLowerCase()
|
||||
.startsWith(GIF_PREFIX);
|
||||
.startsWith(GIF_PREFIX) && isGifUrlAllowed(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url of the gif selected in the gifs menu.
|
||||
*
|
||||
* @param {Object} gif - The gif data.
|
||||
* @param {string} proxyUrl - The proxy server url.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getGifUrl(gif?: { data?: { embed_url: string; }; embed_url?: string; }, proxyUrl?: string) {
|
||||
export function getGifUrl(gif?: { data?: { embed_url: string; }; embed_url?: string; }) {
|
||||
const embedUrl = gif?.embed_url || gif?.data?.embed_url || '';
|
||||
const idx = embedUrl.lastIndexOf('/');
|
||||
const id = embedUrl.substr(idx + 1);
|
||||
|
||||
if (proxyUrl) {
|
||||
return `${proxyUrl}gifs/id/${id}`;
|
||||
}
|
||||
|
||||
return `https://i.giphy.com/media/${id}/giphy.gif`;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,14 +82,19 @@ export function invite(
|
||||
const { conference, password } = state['features/base/conference'];
|
||||
|
||||
if (typeof conference === 'undefined') {
|
||||
// Only keep invitees which can get an invite request from Jitsi UI
|
||||
const jitsiInvitees = invitees.filter(({ type }) => type !== INVITE_TYPES.EMAIL);
|
||||
|
||||
// Invite will fail before CONFERENCE_JOIN. The request will be
|
||||
// cached in order to be executed on CONFERENCE_JOIN.
|
||||
return new Promise(resolve => {
|
||||
dispatch(addPendingInviteRequest({
|
||||
invitees,
|
||||
callback: (failedInvitees: any) => resolve(failedInvitees)
|
||||
}));
|
||||
});
|
||||
if (jitsiInvitees.length) {
|
||||
return new Promise(resolve => {
|
||||
dispatch(addPendingInviteRequest({
|
||||
invitees: jitsiInvitees,
|
||||
callback: (failedInvitees: any) => resolve(failedInvitees)
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let allInvitePromises: Promise<any>[] = [];
|
||||
@@ -112,10 +117,12 @@ export function invite(
|
||||
|
||||
// For each number, dial out. On success, remove the number from
|
||||
// {@link invitesLeftToSend}.
|
||||
const phoneInvitePromises = phoneNumbers.map(item => {
|
||||
const numberToInvite = item.number;
|
||||
const phoneInvitePromises = typeof conference === 'undefined'
|
||||
? []
|
||||
: phoneNumbers.map(item => {
|
||||
const numberToInvite = item.number;
|
||||
|
||||
return conference.dial(numberToInvite)
|
||||
return conference.dial(numberToInvite)
|
||||
.then(() => {
|
||||
invitesLeftToSend
|
||||
= invitesLeftToSend.filter(
|
||||
@@ -123,13 +130,13 @@ export function invite(
|
||||
})
|
||||
.catch((error: Error) =>
|
||||
logger.error('Error inviting phone number:', error));
|
||||
});
|
||||
});
|
||||
|
||||
allInvitePromises = allInvitePromises.concat(phoneInvitePromises);
|
||||
|
||||
const usersAndRooms
|
||||
= invitesLeftToSend.filter(
|
||||
({ type }) => [ INVITE_TYPES.USER, INVITE_TYPES.ROOM ].includes(type));
|
||||
({ type }) => [ INVITE_TYPES.USER, INVITE_TYPES.EMAIL, INVITE_TYPES.ROOM ].includes(type));
|
||||
|
||||
if (usersAndRooms.length) {
|
||||
// Send a request to invite all the rooms and users. On success,
|
||||
@@ -139,12 +146,12 @@ export function invite(
|
||||
(callFlowsEnabled
|
||||
? inviteServiceCallFlowsUrl : inviteServiceUrl) ?? '',
|
||||
inviteUrl,
|
||||
jwt,
|
||||
usersAndRooms)
|
||||
usersAndRooms,
|
||||
state)
|
||||
.then(() => {
|
||||
invitesLeftToSend
|
||||
= invitesLeftToSend.filter(
|
||||
({ type }) => ![ INVITE_TYPES.USER, INVITE_TYPES.ROOM ].includes(type));
|
||||
({ type }) => ![ INVITE_TYPES.USER, INVITE_TYPES.EMAIL, INVITE_TYPES.ROOM ].includes(type));
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch(setCalleeInfoVisible(false));
|
||||
|
||||
@@ -61,6 +61,11 @@ export interface IProps {
|
||||
*/
|
||||
_peopleSearchQueryTypes: Array<string>;
|
||||
|
||||
/**
|
||||
* The localStorage key holding the alternative token for people directory.
|
||||
*/
|
||||
_peopleSearchTokenLocation: string;
|
||||
|
||||
/**
|
||||
* The URL pointing to the service allowing for people search.
|
||||
*/
|
||||
@@ -254,6 +259,7 @@ export default class AbstractAddPeopleDialog<P extends IProps, S extends IState>
|
||||
_jwt: jwt,
|
||||
_peopleSearchQueryTypes: peopleSearchQueryTypes,
|
||||
_peopleSearchUrl: peopleSearchUrl,
|
||||
_peopleSearchTokenLocation: peopleSearchTokenLocation,
|
||||
_region: region,
|
||||
_sipInviteEnabled: sipInviteEnabled
|
||||
} = this.props;
|
||||
@@ -266,6 +272,7 @@ export default class AbstractAddPeopleDialog<P extends IProps, S extends IState>
|
||||
jwt,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl,
|
||||
peopleSearchTokenLocation,
|
||||
region,
|
||||
sipInviteEnabled
|
||||
};
|
||||
@@ -295,7 +302,8 @@ export function _mapStateToProps(state: IReduxState) {
|
||||
dialOutAuthUrl,
|
||||
dialOutRegionUrl,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl
|
||||
peopleSearchUrl,
|
||||
peopleSearchTokenLocation
|
||||
} = state['features/base/config'];
|
||||
|
||||
return {
|
||||
@@ -308,6 +316,7 @@ export function _mapStateToProps(state: IReduxState) {
|
||||
_jwt: state['features/base/jwt'].jwt ?? '',
|
||||
_peopleSearchQueryTypes: peopleSearchQueryTypes ?? [],
|
||||
_peopleSearchUrl: peopleSearchUrl ?? '',
|
||||
_peopleSearchTokenLocation: peopleSearchTokenLocation ?? '',
|
||||
_region: getMeetingRegion(state),
|
||||
_sipInviteEnabled: isSipInviteEnabled(state)
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { matchesProperty, sortBy } from 'lodash-es';
|
||||
import React, { ReactElement } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -19,6 +19,7 @@ import Icon from '../../../../base/icons/components/Icon';
|
||||
import {
|
||||
IconCheck,
|
||||
IconCloseCircle,
|
||||
IconEnvelope,
|
||||
IconPhoneRinging,
|
||||
IconSearch,
|
||||
IconShare
|
||||
@@ -260,6 +261,12 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
key: item.id || item.user_id,
|
||||
title: item.name
|
||||
};
|
||||
case INVITE_TYPES.EMAIL:
|
||||
return {
|
||||
avatar: item.avatar || IconEnvelope,
|
||||
key: item.id || item.user_id,
|
||||
title: item.name
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -273,7 +280,11 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
* @returns {string}
|
||||
*/
|
||||
_keyExtractor(item: any) {
|
||||
return item.type === INVITE_TYPES.USER ? item.id || item.user_id : item.number;
|
||||
if (item.type === INVITE_TYPES.USER || item.type === INVITE_TYPES.EMAIL) {
|
||||
return item.id || item.user_id;
|
||||
}
|
||||
|
||||
return item.number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +331,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
const finderKey = item.type === INVITE_TYPES.PHONE ? 'number' : 'user_id';
|
||||
|
||||
if (inviteItems.find(
|
||||
_.matchesProperty(finderKey, item[finderKey as keyof typeof item]))) {
|
||||
matchesProperty(finderKey, item[finderKey as keyof typeof item]))) {
|
||||
// Item is already selected, need to unselect it.
|
||||
this.setState({
|
||||
inviteItems: inviteItems.filter(
|
||||
@@ -332,7 +343,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
const items = inviteItems.concat(item);
|
||||
|
||||
this.setState({
|
||||
inviteItems: _.sortBy(items, [ 'name', 'number' ])
|
||||
inviteItems: sortBy(items, [ 'name', 'number' ])
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -383,7 +394,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
_performSearch(query: string) {
|
||||
this._query(query).then(results => {
|
||||
this.setState({
|
||||
selectableItems: _.sortBy(results, [ 'name', 'number' ])
|
||||
selectableItems: sortBy(results, [ 'name', 'number' ])
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -448,12 +459,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
|
||||
switch (item.type) {
|
||||
case INVITE_TYPES.PHONE:
|
||||
selected = inviteItems.find(_.matchesProperty('number', item.number));
|
||||
selected = inviteItems.find(matchesProperty('number', item.number));
|
||||
break;
|
||||
case INVITE_TYPES.USER:
|
||||
case INVITE_TYPES.EMAIL:
|
||||
selected = item.id
|
||||
? inviteItems.find(_.matchesProperty('id', item.id))
|
||||
: inviteItems.find(_.matchesProperty('user_id', item.user_id));
|
||||
? inviteItems.find(matchesProperty('id', item.id))
|
||||
: inviteItems.find(matchesProperty('user_id', item.user_id));
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IReduxState, IStore } from '../../../../app/types';
|
||||
import Avatar from '../../../../base/avatar/components/Avatar';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconPhoneRinging } from '../../../../base/icons/svg';
|
||||
import { IconEnvelope, IconPhoneRinging, IconUser } from '../../../../base/icons/svg';
|
||||
import MultiSelectAutocomplete from '../../../../base/react/components/web/MultiSelectAutocomplete';
|
||||
import Button from '../../../../base/ui/components/web/Button';
|
||||
import { BUTTON_TYPES } from '../../../../base/ui/constants.any';
|
||||
@@ -302,9 +302,12 @@ class InviteContactsForm extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_getAvatar(user: any, className = 'avatar-small') {
|
||||
const defaultIcon = user.type === INVITE_TYPES.EMAIL ? IconEnvelope : IconUser;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
className = { className }
|
||||
defaultIcon = { defaultIcon }
|
||||
size = { 32 }
|
||||
status = { user.status }
|
||||
url = { user.avatar } />
|
||||
@@ -325,7 +328,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<IProps, IState> {
|
||||
_parseQueryResults(response: IInvitee[] = []) {
|
||||
const { t, _dialOutEnabled } = this.props;
|
||||
|
||||
const userTypes = [ INVITE_TYPES.USER, INVITE_TYPES.VIDEO_ROOM, INVITE_TYPES.ROOM ];
|
||||
const userTypes = [ INVITE_TYPES.USER, INVITE_TYPES.EMAIL, INVITE_TYPES.VIDEO_ROOM, INVITE_TYPES.ROOM ];
|
||||
const users = response.filter(item => userTypes.includes(item.type));
|
||||
const userDisplayItems: any = [];
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ export const SIP_ADDRESS_REGEX = /^[+a-zA-Z0-9]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)
|
||||
* Different invite types mapping.
|
||||
*/
|
||||
export const INVITE_TYPES = {
|
||||
EMAIL: 'email',
|
||||
PHONE: 'phone',
|
||||
ROOM: 'room',
|
||||
SIP: 'sip',
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
import { IReduxState } from '../app/types';
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { getRoomName } from '../base/conference/functions';
|
||||
@@ -143,6 +146,11 @@ export type GetInviteResultsOptions = {
|
||||
*/
|
||||
peopleSearchQueryTypes: Array<string>;
|
||||
|
||||
/**
|
||||
* Key in localStorage holding the alternative token for people directory.
|
||||
*/
|
||||
peopleSearchTokenLocation?: string;
|
||||
|
||||
/**
|
||||
* The url to query for people.
|
||||
*/
|
||||
@@ -181,6 +189,7 @@ export function getInviteResultsForQuery(
|
||||
dialOutEnabled,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl,
|
||||
peopleSearchTokenLocation,
|
||||
region,
|
||||
sipInviteEnabled,
|
||||
jwt
|
||||
@@ -193,7 +202,8 @@ export function getInviteResultsForQuery(
|
||||
peopleSearchUrl,
|
||||
jwt,
|
||||
text,
|
||||
peopleSearchQueryTypes);
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchTokenLocation);
|
||||
} else {
|
||||
peopleSearchPromise = Promise.resolve([]);
|
||||
}
|
||||
@@ -411,29 +421,40 @@ export function getInviteTypeCounts(inviteItems: IInvitee[] = []) {
|
||||
* @param {string} inviteServiceUrl - The invite service that generates the
|
||||
* invitation.
|
||||
* @param {string} inviteUrl - The url to the conference.
|
||||
* @param {string} jwt - The jwt token to pass to the search service.
|
||||
* @param {Immutable.List} inviteItems - The list of the "user" or "room" type
|
||||
* items to invite.
|
||||
* @param {IReduxState} state - Global state.
|
||||
* @returns {Promise} - The promise created by the request.
|
||||
*/
|
||||
export function invitePeopleAndChatRooms(
|
||||
inviteServiceUrl: string,
|
||||
inviteUrl: string,
|
||||
jwt: string,
|
||||
inviteItems: Array<Object>
|
||||
inviteItems: Array<Object>,
|
||||
state: IReduxState
|
||||
): Promise<any> {
|
||||
|
||||
if (!inviteItems || inviteItems.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Parse all the query strings of the search directory endpoint
|
||||
const { jwt = '' } = state['features/base/jwt'];
|
||||
const { peopleSearchTokenLocation } = state['features/base/config'];
|
||||
|
||||
let token = jwt;
|
||||
|
||||
// If token is empty, check for alternate token
|
||||
if (!token && peopleSearchTokenLocation) {
|
||||
token = jitsiLocalStorage.getItem(peopleSearchTokenLocation) ?? '';
|
||||
}
|
||||
|
||||
const headers = {
|
||||
...jwt ? { 'Authorization': `Bearer ${jwt}` } : {},
|
||||
...token ? { 'Authorization': `Bearer ${token}` } : {},
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
return fetch(
|
||||
`${inviteServiceUrl}`,
|
||||
inviteServiceUrl,
|
||||
{
|
||||
body: JSON.stringify({
|
||||
'invited': inviteItems,
|
||||
@@ -452,9 +473,14 @@ export function invitePeopleAndChatRooms(
|
||||
* @returns {boolean} Indication of whether adding people is currently enabled.
|
||||
*/
|
||||
export function isAddPeopleEnabled(state: IReduxState): boolean {
|
||||
const { peopleSearchUrl } = state['features/base/config'];
|
||||
const {
|
||||
peopleSearchUrl,
|
||||
peopleSearchTokenLocation
|
||||
} = state['features/base/config'];
|
||||
|
||||
return Boolean(state['features/base/jwt'].jwt && Boolean(peopleSearchUrl) && !isVpaasMeeting(state));
|
||||
const hasToken = Boolean(state['features/base/jwt'].jwt || Boolean(peopleSearchTokenLocation));
|
||||
|
||||
return Boolean(hasToken && Boolean(peopleSearchUrl) && !isVpaasMeeting(state));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,21 +560,30 @@ function isPhoneNumberRegex(): RegExp {
|
||||
* @param {string} jwt - The jwt token to pass to the search service.
|
||||
* @param {string} text - Text to search.
|
||||
* @param {Array<string>} queryTypes - Array with the query types that will be
|
||||
* executed - "conferenceRooms" | "user" | "room".
|
||||
* executed - "conferenceRooms" | "user" | "room" | "email".
|
||||
* @param {string} peopleSearchTokenLocation - The localStorage key holding the token value for alternate auth.
|
||||
* @returns {Promise} - The promise created by the request.
|
||||
*/
|
||||
export function searchDirectory( // eslint-disable-line max-params
|
||||
serviceUrl: string,
|
||||
jwt: string,
|
||||
text: string,
|
||||
queryTypes: Array<string> = [ 'conferenceRooms', 'user', 'room' ]
|
||||
queryTypes: Array<string> = [ 'conferenceRooms', 'user', 'room', 'email' ],
|
||||
peopleSearchTokenLocation?: string
|
||||
): Promise<Array<{ type: string; }>> {
|
||||
|
||||
const query = encodeURIComponent(text);
|
||||
const queryTypesString = encodeURIComponent(JSON.stringify(queryTypes));
|
||||
|
||||
let token = jwt;
|
||||
|
||||
// If token is empty, check for alternate token
|
||||
if (!token && peopleSearchTokenLocation) {
|
||||
token = jitsiLocalStorage.getItem(peopleSearchTokenLocation) ?? '';
|
||||
}
|
||||
|
||||
const headers = {
|
||||
...jwt ? { 'Authorization': `Bearer ${jwt}` } : {}
|
||||
...token ? { 'Authorization': `Bearer ${token}` } : {}
|
||||
};
|
||||
|
||||
return fetch(`${serviceUrl}?query=${query}&queryTypes=${
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { sortBy } from 'lodash-es';
|
||||
import React, { Component } from 'react';
|
||||
import { NativeModules, Text, TextStyle, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -201,7 +201,7 @@ class AudioRoutePickerDialog extends Component<IProps, IState> {
|
||||
|
||||
// Make sure devices is alphabetically sorted.
|
||||
return {
|
||||
devices: _.sortBy(audioDevices, 'text')
|
||||
devices: sortBy(audioDevices, 'text')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,3 @@ export const READY_TO_CLOSE = 'READY_TO_CLOSE';
|
||||
*/
|
||||
export const SCREEN_SHARE_PARTICIPANTS_UPDATED
|
||||
= 'SCREEN_SHARE_PARTICIPANTS_UPDATED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a custom button from the overflow menu was pressed.
|
||||
*
|
||||
* @returns {{
|
||||
* type: CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED,
|
||||
* id: string,
|
||||
* text: string
|
||||
* }}
|
||||
*/
|
||||
export const CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED = 'CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED';
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED,
|
||||
READY_TO_CLOSE,
|
||||
SCREEN_SHARE_PARTICIPANTS_UPDATED
|
||||
} from './actionTypes';
|
||||
import { READY_TO_CLOSE, SCREEN_SHARE_PARTICIPANTS_UPDATED } from './actionTypes';
|
||||
|
||||
|
||||
/**
|
||||
@@ -35,22 +31,3 @@ export function setParticipantsWithScreenShare(participantIds: Array<string>) {
|
||||
participantIds
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a (redux) action which that a custom overflow menu button was pressed.
|
||||
*
|
||||
* @param {string} id - The id for the custom button.
|
||||
* @param {string} text - The label for the custom button.
|
||||
* @returns {{
|
||||
* type: CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED,
|
||||
* id: string,
|
||||
* text: string
|
||||
* }}
|
||||
*/
|
||||
export function customOverflowMenuButtonPressed(id: string, text: string) {
|
||||
return {
|
||||
type: CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED,
|
||||
id,
|
||||
text
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import debounce from 'lodash/debounce';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { IParticipant } from '../../base/participants/types';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { NativeEventEmitter, NativeModules } from 'react-native';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
@@ -52,15 +52,13 @@ import { ITrack } from '../../base/tracks/types';
|
||||
import { CLOSE_CHAT, OPEN_CHAT } from '../../chat/actionTypes';
|
||||
import { closeChat, openChat, sendMessage, setPrivateMessageRecipient } from '../../chat/actions.native';
|
||||
import { setRequestingSubtitles } from '../../subtitles/actions.any';
|
||||
import { CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED } from '../../toolbox/actionTypes';
|
||||
import { muteLocal } from '../../video-menu/actions.native';
|
||||
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture/actionTypes';
|
||||
// @ts-ignore
|
||||
import { isExternalAPIAvailable } from '../react-native-sdk/functions';
|
||||
|
||||
import {
|
||||
CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED,
|
||||
READY_TO_CLOSE
|
||||
} from './actionTypes';
|
||||
import { READY_TO_CLOSE } from './actionTypes';
|
||||
import { setParticipantsWithScreenShare } from './actions';
|
||||
import { participantToParticipantInfo, sendEvent } from './functions';
|
||||
import logger from './logger';
|
||||
@@ -82,12 +80,6 @@ const CHAT_TOGGLED = 'CHAT_TOGGLED';
|
||||
*/
|
||||
const CONFERENCE_TERMINATED = 'CONFERENCE_TERMINATED';
|
||||
|
||||
/**
|
||||
* Event which will be emitted on the native side to indicate that the custom overflow menu button was pressed.
|
||||
*/
|
||||
const CUSTOM_MENU_BUTTON_PRESSED = 'CUSTOM_MENU_BUTTON_PRESSED';
|
||||
|
||||
|
||||
/**
|
||||
* Event which will be emitted on the native side to indicate a message was received
|
||||
* through the channel.
|
||||
@@ -199,7 +191,7 @@ externalAPIEnabled && MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
sendEvent(
|
||||
store,
|
||||
CUSTOM_MENU_BUTTON_PRESSED,
|
||||
CUSTOM_OVERFLOW_MENU_BUTTON_PRESSED,
|
||||
{
|
||||
id,
|
||||
text
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user