Compare commits
70 Commits
8088
...
jitsi-meet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f59d04586c | ||
|
|
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 | ||
|
|
7bc9913b29 | ||
|
|
2483d901d6 | ||
|
|
6ff7995cee | ||
|
|
5d563402d0 | ||
|
|
6727004930 | ||
|
|
c04000ea20 | ||
|
|
23be14697c | ||
|
|
ca07eed85f | ||
|
|
72779e5ba5 | ||
|
|
1b3b949218 | ||
|
|
d510390edc | ||
|
|
1de1381847 | ||
|
|
639114f2e1 | ||
|
|
411e9a2372 |
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(this.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;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ import {
|
||||
setAudioAvailable,
|
||||
setAudioMuted,
|
||||
setAudioUnmutePermissions,
|
||||
setInitialGUMPromise,
|
||||
setVideoAvailable,
|
||||
setVideoMuted,
|
||||
setVideoUnmutePermissions
|
||||
@@ -154,8 +155,7 @@ import {
|
||||
import { isModerationNotificationDisplayed } from './react/features/notifications/functions';
|
||||
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay/actions';
|
||||
import { suspendDetected } from './react/features/power-monitor/actions';
|
||||
import { initPrejoin } from './react/features/prejoin/actions';
|
||||
import { isPrejoinPageVisible } from './react/features/prejoin/functions';
|
||||
import { initPrejoin, isPrejoinPageVisible } from './react/features/prejoin/functions';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control/actions';
|
||||
import { setScreenAudioShareState } from './react/features/screen-share/actions.web';
|
||||
import { isScreenAudioShared } from './react/features/screen-share/functions';
|
||||
@@ -591,7 +591,7 @@ export default {
|
||||
const handleInitialTracks = (options, tracks) => {
|
||||
let localTracks = tracks;
|
||||
|
||||
if (options.startWithAudioMuted || room?.isStartAudioMuted()) {
|
||||
if (options.startWithAudioMuted) {
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (browser.isWebKitBased()) {
|
||||
@@ -600,58 +600,38 @@ export default {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
if (room?.isStartVideoMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
return localTracks;
|
||||
};
|
||||
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
const localTracks = await tryCreateLocalTracks;
|
||||
|
||||
// Initialize device list a second time to ensure device labels get populated in case of an initial gUM
|
||||
// acceptance; otherwise they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
|
||||
return APP.store.dispatch(initPrejoin(localTracks, errors));
|
||||
}
|
||||
|
||||
logger.debug('Prejoin screen no longer displayed at the time when tracks were created');
|
||||
|
||||
APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
|
||||
|
||||
const tracks = handleInitialTracks(initialOptions, localTracks);
|
||||
|
||||
setGUMPendingStateOnFailedTracks(tracks, APP.store.dispatch);
|
||||
|
||||
return this._setLocalAudioVideoStreams(tracks);
|
||||
}
|
||||
|
||||
const { dispatch, getState } = APP.store;
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
|
||||
return Promise.all([
|
||||
tryCreateLocalTracks.then(tr => {
|
||||
dispatch(setInitialGUMPromise(tryCreateLocalTracks.then(async tr => {
|
||||
const tracks = handleInitialTracks(initialOptions, tr);
|
||||
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (isPrejoinPageVisible(getState())) {
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
dispatch(setInitialGUMPromise());
|
||||
|
||||
// Note: Not sure if initPrejoin needs to be async. But let's wait for it just to be sure the
|
||||
// tracks are added.
|
||||
initPrejoin(tracks, errors, dispatch);
|
||||
} else {
|
||||
APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
|
||||
setGUMPendingStateOnFailedTracks(tracks, APP.store.dispatch);
|
||||
}
|
||||
|
||||
return tr;
|
||||
}).then(tr => {
|
||||
this._initDeviceList(true);
|
||||
return {
|
||||
tracks,
|
||||
errors
|
||||
};
|
||||
})));
|
||||
|
||||
const filteredTracks = handleInitialTracks(initialOptions, tr);
|
||||
|
||||
setGUMPendingStateOnFailedTracks(filteredTracks, APP.store.dispatch);
|
||||
|
||||
return filteredTracks;
|
||||
}),
|
||||
APP.store.dispatch(connect())
|
||||
]).then(([ tracks, _ ]) => {
|
||||
this.startConference(tracks).catch(logger.error);
|
||||
});
|
||||
if (!isPrejoinPageVisible(getState())) {
|
||||
dispatch(connect());
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
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 ",
|
||||
@@ -786,6 +786,7 @@
|
||||
"newDeviceAction": "Use",
|
||||
"newDeviceAudioTitle": "New audio device detected",
|
||||
"newDeviceCameraTitle": "New camera detected",
|
||||
"nextToSpeak": "You are the next in line to speak",
|
||||
"noiseSuppressionDesktopAudioDescription": "Noise suppression can't be enabled while sharing desktop audio, please disable it and try again.",
|
||||
"noiseSuppressionFailedTitle": "Failed to start noise suppression",
|
||||
"noiseSuppressionStereoDescription": "Stereo audio noise suppression is not currently supported.",
|
||||
@@ -820,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}}",
|
||||
@@ -1502,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.",
|
||||
|
||||
@@ -1338,14 +1338,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,
|
||||
@@ -1814,9 +1814,9 @@ class API {
|
||||
* Notify external application of a participant, remote or local, being
|
||||
* removed from the conference by another participant.
|
||||
*
|
||||
* @param {string} kicked - The ID of the participant removed from the
|
||||
* @param {Object} kicked - The participant removed from the
|
||||
* conference.
|
||||
* @param {string} kicker - The ID of the participant that removed the
|
||||
* @param {Object} kicker - The participant that removed the
|
||||
* other participant.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
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/v1844.0.0+a9b6dd7e/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/v1844.0.0+a9b6dd7e/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-ZLOF2lmTgMU+eEltT7UyZ9LY16g+IjA4sWfdFafbTxKub99Db8/B56vfeN3AEXy4ldtslifhgP2Cx0x+CX3Esg==",
|
||||
"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/v1844.0.0+a9b6dd7e/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-ZLOF2lmTgMU+eEltT7UyZ9LY16g+IjA4sWfdFafbTxKub99Db8/B56vfeN3AEXy4ldtslifhgP2Cx0x+CX3Esg==",
|
||||
"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/v1844.0.0+a9b6dd7e/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",
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { IRoomsInfo } from '../react/features/breakout-rooms/types';
|
||||
|
||||
import { appNavigate } from './react/features/app/actions.native';
|
||||
import { App } from './react/features/app/components/App.native';
|
||||
import { setAudioOnly } from './react/features/base/audio-only/actions';
|
||||
import { setAudioMuted, setVideoMuted } from './react/features/base/media/actions';
|
||||
import { getRoomsInfo } from './react/features/breakout-rooms/functions';
|
||||
|
||||
@@ -55,6 +56,7 @@ interface IAppProps {
|
||||
|
||||
export interface JitsiRefProps {
|
||||
close: Function;
|
||||
setAudioOnly?: (value: boolean) => void;
|
||||
setAudioMuted?: (muted: boolean) => void;
|
||||
setVideoMuted?: (muted: boolean) => void;
|
||||
getRoomsInfo?: () => IRoomsInfo;
|
||||
@@ -84,6 +86,11 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
|
||||
|
||||
dispatch(appNavigate(undefined));
|
||||
},
|
||||
setAudioOnly: value => {
|
||||
const dispatch = app.current.state.store.dispatch;
|
||||
|
||||
dispatch(setAudioOnly(value));
|
||||
},
|
||||
setAudioMuted: muted => {
|
||||
const dispatch = app.current.state.store.dispatch;
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -5,12 +5,12 @@ import { connect as reduxConnect } from 'react-redux';
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { IJitsiConference } from '../../../base/conference/reducer';
|
||||
import { IConfig } from '../../../base/config/configType';
|
||||
import { connect } from '../../../base/connection/actions.web';
|
||||
import { toJid } from '../../../base/connection/functions';
|
||||
import { translate, translateToHTML } from '../../../base/i18n/functions';
|
||||
import { JitsiConnectionErrors } from '../../../base/lib-jitsi-meet';
|
||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
||||
import Input from '../../../base/ui/components/web/Input';
|
||||
import { joinConference } from '../../../prejoin/actions.web';
|
||||
import {
|
||||
authenticateAndUpgradeRole,
|
||||
cancelLogin
|
||||
@@ -134,9 +134,7 @@ class LoginDialog extends Component<IProps, IState> {
|
||||
if (conference) {
|
||||
dispatch(authenticateAndUpgradeRole(jid, password, conference));
|
||||
} else {
|
||||
// dispatch(connect(jid, password));
|
||||
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
|
||||
dispatch(joinConference(undefined, false, jid, password));
|
||||
dispatch(connect(jid, password));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -143,7 +143,8 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case CONNECTION_FAILED: {
|
||||
const { error } = action;
|
||||
const state = store.getState();
|
||||
const { getState } = store;
|
||||
const state = getState();
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
if (error
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,11 +21,5 @@ export function setupVisitorStartupMedia(media: Array<MediaType>) {
|
||||
if (media && Array.isArray(media) && media.length > 0) {
|
||||
dispatch(createAndAddInitialAVTracks(media));
|
||||
}
|
||||
|
||||
// FIXME: The name of the function doesn't fit the startConference execution but another PR will removes
|
||||
// this and calls startConference based on the connection status. This will stay here temporary.
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.conference.startConference([]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
@@ -571,7 +571,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)
|
||||
, '');
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import LocalRecordingManager from '../../recording/components/Recording/LocalRec
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { overwriteConfig } from '../config/actions';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection/actionTypes';
|
||||
import { connect, connectionDisconnected, disconnect } from '../connection/actions';
|
||||
import { connectionDisconnected, disconnect } from '../connection/actions';
|
||||
import { validateJwt } from '../jwt/functions';
|
||||
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
|
||||
import { PARTICIPANT_UPDATED, PIN_PARTICIPANT } from '../participants/actionTypes';
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks/actionTypes';
|
||||
import { getLocalTracks } from '../tracks/functions.any';
|
||||
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
@@ -209,17 +208,7 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
dispatch(conferenceWillLeave(conference));
|
||||
|
||||
conference.leave()
|
||||
.then(() => dispatch(disconnect()))
|
||||
.then(() => dispatch(connect()))
|
||||
.then(() => {
|
||||
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
|
||||
if (typeof APP !== 'undefined') {
|
||||
const localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
|
||||
|
||||
APP.conference.startConference(jitsiTracks).catch(logger.error);
|
||||
}
|
||||
});
|
||||
.then(() => dispatch(disconnect()));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -4,9 +4,17 @@ import {
|
||||
setPrejoinPageVisibility,
|
||||
setSkipPrejoinOnReload
|
||||
} from '../../prejoin/actions.web';
|
||||
import { isPrejoinPageVisible } from '../../prejoin/functions';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { CONNECTION_DISCONNECTED, CONNECTION_ESTABLISHED } from '../connection/actionTypes';
|
||||
import { hangup } from '../connection/actions.web';
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import { JitsiConferenceErrors, browser } from '../lib-jitsi-meet';
|
||||
import { gumPending, setInitialGUMPromise } from '../media/actions';
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import { IGUMPendingState } from '../media/types';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import { replaceLocalTrack } from '../tracks/actions.any';
|
||||
import { getLocalTracks } from '../tracks/functions.any';
|
||||
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
@@ -131,6 +139,75 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
releaseScreenLock();
|
||||
|
||||
break;
|
||||
case CONNECTION_DISCONNECTED: {
|
||||
const { initialGUMPromise } = getState()['features/base/media'];
|
||||
|
||||
if (initialGUMPromise) {
|
||||
store.dispatch(setInitialGUMPromise());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CONNECTION_ESTABLISHED: {
|
||||
if (isPrejoinPageVisible(getState())) {
|
||||
let { initialGUMPromise } = getState()['features/base/media'];
|
||||
|
||||
initialGUMPromise = initialGUMPromise || Promise.resolve({ tracks: [] });
|
||||
|
||||
initialGUMPromise.then(() => {
|
||||
const state = getState();
|
||||
let localTracks = getLocalTracks(state['features/base/tracks']);
|
||||
const trackReplacePromises = [];
|
||||
|
||||
// Do not signal audio/video tracks if the user joins muted.
|
||||
for (const track of localTracks) {
|
||||
// Always add the audio track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted.
|
||||
if ((track.muted && !(browser.isWebKitBased() && track.jitsiTrack
|
||||
&& track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) || iAmVisitor(state)) {
|
||||
trackReplacePromises.push(dispatch(replaceLocalTrack(track.jitsiTrack, null))
|
||||
.catch((error: any) => {
|
||||
logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Promise.allSettled(trackReplacePromises).then(() => {
|
||||
|
||||
// Re-fetch the local tracks after muted tracks have been removed above.
|
||||
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should
|
||||
// not be used anymore.
|
||||
localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||
|
||||
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
|
||||
|
||||
|
||||
return APP.conference.startConference(jitsiTracks);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
let { initialGUMPromise } = getState()['features/base/media'];
|
||||
|
||||
initialGUMPromise = initialGUMPromise || Promise.resolve({ tracks: [] });
|
||||
|
||||
initialGUMPromise.then(({ tracks }) => {
|
||||
let tracksToUse = tracks ?? [];
|
||||
|
||||
if (iAmVisitor(getState())) {
|
||||
tracksToUse = [];
|
||||
tracks.forEach(track => track.dispose().catch(logger.error));
|
||||
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
|
||||
}
|
||||
|
||||
dispatch(setInitialGUMPromise());
|
||||
|
||||
return APP.conference.startConference(tracksToUse);
|
||||
})
|
||||
.catch(logger.error);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -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,9 +3,10 @@ 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';
|
||||
import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
import { IConfig } from './configType';
|
||||
@@ -88,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,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.
|
||||
@@ -165,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;
|
||||
@@ -184,6 +213,31 @@ export function isNameReadOnly(state: IReduxState): boolean {
|
||||
|| state['features/base/config'].readOnlyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for determining if the participant is the next one in the queue to speak.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isNextToSpeak(state: IReduxState): boolean {
|
||||
const raisedHandsQueue = state['features/base/participants'].raisedHandsQueue || [];
|
||||
const participantId = getLocalParticipant(state)?.id;
|
||||
|
||||
return participantId === raisedHandsQueue[0]?.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for determining if the next to speak participant in the queue has been notified.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasBeenNotified(state: IReduxState): boolean {
|
||||
const raisedHandsQueue = state['features/base/participants'].raisedHandsQueue;
|
||||
|
||||
return Boolean(raisedHandsQueue[0]?.hasBeenNotified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for determining if the display name is visible.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -51,6 +51,16 @@ export const SET_AUDIO_UNMUTE_PERMISSIONS = 'SET_AUDIO_UNMUTE_PERMISSIONS';
|
||||
*/
|
||||
export const SET_CAMERA_FACING_MODE = 'SET_CAMERA_FACING_MODE';
|
||||
|
||||
/**
|
||||
* Sets the initial GUM promise.
|
||||
*
|
||||
* {
|
||||
* type: SET_INITIAL_GUM_PROMISE,
|
||||
* promise: Promise
|
||||
* }}
|
||||
*/
|
||||
export const SET_INITIAL_GUM_PROMISE = 'SET_INITIAL_GUM_PROMISE';
|
||||
|
||||
/**
|
||||
* The type of (redux) action to set the muted state of the local screenshare.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SET_AUDIO_MUTED,
|
||||
SET_AUDIO_UNMUTE_PERMISSIONS,
|
||||
SET_CAMERA_FACING_MODE,
|
||||
SET_INITIAL_GUM_PROMISE,
|
||||
SET_SCREENSHARE_MUTED,
|
||||
SET_VIDEO_AVAILABLE,
|
||||
SET_VIDEO_MUTED,
|
||||
@@ -93,6 +94,22 @@ export function setCameraFacingMode(cameraFacingMode: string) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial GUM promise.
|
||||
*
|
||||
* @param {Promise<Array<Object>> | undefined} promise - The promise.
|
||||
* @returns {{
|
||||
* type: SET_INITIAL_GUM_PROMISE,
|
||||
* promise: Promise
|
||||
* }}
|
||||
*/
|
||||
export function setInitialGUMPromise(promise: Promise<{ errors: any; tracks: Array<any>; }> | null = null) {
|
||||
return {
|
||||
type: SET_INITIAL_GUM_PROMISE,
|
||||
promise
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to set the muted state of the local screenshare.
|
||||
*
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
SET_AUDIO_MUTED,
|
||||
SET_AUDIO_UNMUTE_PERMISSIONS,
|
||||
SET_CAMERA_FACING_MODE,
|
||||
SET_INITIAL_GUM_PROMISE,
|
||||
SET_SCREENSHARE_MUTED,
|
||||
SET_VIDEO_AVAILABLE,
|
||||
SET_VIDEO_MUTED,
|
||||
@@ -87,6 +88,22 @@ function _audio(state: IAudioState = _AUDIO_INITIAL_MEDIA_STATE, action: AnyActi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reducer fot the common properties in media state.
|
||||
*
|
||||
* @param {ICommonState} state - Common media state.
|
||||
* @param {Object} action - Action object.
|
||||
* @param {string} action.type - Type of action.
|
||||
* @returns {ICommonState}
|
||||
*/
|
||||
function _initialGUMPromise(state: initialGUMPromise | null = null, action: AnyAction) {
|
||||
if (action.type === SET_INITIAL_GUM_PROMISE) {
|
||||
return action.promise ?? null;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Media state object for local screenshare.
|
||||
*
|
||||
@@ -247,6 +264,11 @@ interface IAudioState {
|
||||
unmuteBlocked: boolean;
|
||||
}
|
||||
|
||||
type initialGUMPromise = Promise<{
|
||||
errors?: any;
|
||||
tracks: Array<any>;
|
||||
}> | null;
|
||||
|
||||
interface IScreenshareState {
|
||||
available: boolean;
|
||||
muted: number;
|
||||
@@ -264,6 +286,7 @@ interface IVideoState {
|
||||
|
||||
export interface IMediaState {
|
||||
audio: IAudioState;
|
||||
initialGUMPromise: initialGUMPromise;
|
||||
screenshare: IScreenshareState;
|
||||
video: IVideoState;
|
||||
}
|
||||
@@ -280,6 +303,7 @@ export interface IMediaState {
|
||||
*/
|
||||
ReducerRegistry.register<IMediaState>('features/base/media', combineReducers({
|
||||
audio: _audio,
|
||||
initialGUMPromise: _initialGUMPromise,
|
||||
screenshare: _screenshare,
|
||||
video: _video
|
||||
}));
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
/**
|
||||
* Create an action to mark the participant as notified to speak next.
|
||||
*
|
||||
* {
|
||||
* type: NOTIFIED_TO_SPEAK
|
||||
* }
|
||||
*/
|
||||
export const NOTIFIED_TO_SPEAK = 'NOTIFIED_TO_SPEAK';
|
||||
|
||||
/**
|
||||
* Create an action for when dominant speaker changes.
|
||||
*
|
||||
|
||||
@@ -544,23 +544,27 @@ export function createVirtualScreenshareParticipant(sourceName: string, local: b
|
||||
*/
|
||||
export function participantKicked(kicker: any, kicked: any) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const kickedId = kicked.getId();
|
||||
const kickerId = kicker?.getId();
|
||||
|
||||
dispatch({
|
||||
type: PARTICIPANT_KICKED,
|
||||
kicked: kicked.getId(),
|
||||
kicker: kicker?.getId()
|
||||
kicked: kickedId,
|
||||
kicker: kickerId
|
||||
});
|
||||
|
||||
if (kicked.isReplaced?.()) {
|
||||
if (kicked.isReplaced?.() || !kickerId || kickerId === localParticipant?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(showNotification({
|
||||
titleArguments: {
|
||||
kicked:
|
||||
getParticipantDisplayName(getState, kicked.getId()),
|
||||
getParticipantDisplayName(state, kickedId),
|
||||
kicker:
|
||||
getParticipantDisplayName(getState, kicker.getId())
|
||||
getParticipantDisplayName(state, kickerId)
|
||||
},
|
||||
titleKey: 'notify.kickParticipant'
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { set } from '../redux/functions';
|
||||
|
||||
import {
|
||||
DOMINANT_SPEAKER_CHANGED,
|
||||
NOTIFIED_TO_SPEAK,
|
||||
OVERWRITE_PARTICIPANT_NAME,
|
||||
PARTICIPANT_ID_CHANGED,
|
||||
PARTICIPANT_JOINED,
|
||||
@@ -92,7 +93,7 @@ export interface IParticipantsState {
|
||||
numberOfParticipantsNotSupportingE2EE: number;
|
||||
overwrittenNameList: { [id: string]: string; };
|
||||
pinnedParticipant?: string;
|
||||
raisedHandsQueue: Array<{ id: string; raisedHandTimestamp: number; }>;
|
||||
raisedHandsQueue: Array<{ hasBeenNotified?: boolean; id: string; raisedHandTimestamp: number; }>;
|
||||
remote: Map<string, IParticipant>;
|
||||
remoteVideoSources: Set<string>;
|
||||
sortedRemoteParticipants: Map<string, string>;
|
||||
@@ -114,6 +115,23 @@ export interface IParticipantsState {
|
||||
ReducerRegistry.register<IParticipantsState>('features/base/participants',
|
||||
(state = DEFAULT_STATE, action): IParticipantsState => {
|
||||
switch (action.type) {
|
||||
case NOTIFIED_TO_SPEAK: {
|
||||
return {
|
||||
...state,
|
||||
raisedHandsQueue: state.raisedHandsQueue.map((item, index) => {
|
||||
if (index === 0) {
|
||||
|
||||
return {
|
||||
...item,
|
||||
hasBeenNotified: true
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
case PARTICIPANT_ID_CHANGED: {
|
||||
const { local } = state;
|
||||
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import { difference } from 'lodash-es';
|
||||
import { batch } from 'react-redux';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { hideNotification, showNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE, RAISE_HAND_NOTIFICATION_ID } from '../../notifications/constants';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { getSsrcRewritingFeatureFlag } from '../config/functions.any';
|
||||
import {
|
||||
getDisableNextSpeakerNotification,
|
||||
getSsrcRewritingFeatureFlag,
|
||||
hasBeenNotified,
|
||||
isNextToSpeak } from '../config/functions.any';
|
||||
import { VIDEO_TYPE } from '../media/constants';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
|
||||
import { NOTIFIED_TO_SPEAK } from './actionTypes';
|
||||
import { createVirtualScreenshareParticipant, participantLeft } from './actions';
|
||||
import {
|
||||
getParticipantById,
|
||||
@@ -25,6 +34,22 @@ StateListenerRegistry.register(
|
||||
&& _updateScreenshareParticipantsBasedOnPresence(store)
|
||||
);
|
||||
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].raisedHandsQueue,
|
||||
/* listener */ (raisedHandsQueue, store) => {
|
||||
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));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
|
||||
* tiles accodingly.
|
||||
@@ -40,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, {
|
||||
@@ -121,3 +146,23 @@ function _updateScreenshareParticipantsBasedOnPresence(store: IStore): void {
|
||||
|
||||
_createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles notifying the next speaker in the raised hand queue.
|
||||
*
|
||||
* @param {*} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _notifyNextSpeakerInRaisedHandQueue(store: IStore): void {
|
||||
const { dispatch } = store;
|
||||
|
||||
batch(() => {
|
||||
dispatch(showNotification({
|
||||
titleKey: 'notify.nextToSpeak',
|
||||
maxLines: 2
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
||||
dispatch({
|
||||
type: NOTIFIED_TO_SPEAK
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -29,7 +29,18 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
const etherpadBaseUrl = sanitizeUrl(etherpadBase);
|
||||
|
||||
if (etherpadBaseUrl) {
|
||||
url = new URL(value, etherpadBaseUrl.toString()).toString();
|
||||
const urlObj = new URL(value, etherpadBaseUrl.toString());
|
||||
|
||||
// Merge query string parameters on top of internal ones
|
||||
if (etherpadBaseUrl.search) {
|
||||
const searchParams = new URLSearchParams(urlObj.search);
|
||||
|
||||
for (const [ key, val ] of new URLSearchParams(etherpadBaseUrl.search)) {
|
||||
searchParams.set(key, val);
|
||||
}
|
||||
urlObj.search = searchParams.toString();
|
||||
}
|
||||
url = urlObj.toString();
|
||||
}
|
||||
|
||||
dispatch(setDocumentUrl(url));
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
import {
|
||||
getDominantSpeakerParticipant,
|
||||
getLocalParticipant,
|
||||
getParticipantById
|
||||
getParticipantById,
|
||||
getParticipantDisplayName
|
||||
} from '../base/participants/functions';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import { getBaseUrl } from '../base/util/helpers';
|
||||
@@ -133,15 +134,29 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
APP.API.notifyDataChannelOpened();
|
||||
break;
|
||||
|
||||
case KICKED_OUT:
|
||||
case KICKED_OUT: {
|
||||
const state = store.getState();
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
if (!localParticipant) {
|
||||
break;
|
||||
}
|
||||
|
||||
const pId = action.participant.getId();
|
||||
|
||||
APP.API.notifyKickedOut(
|
||||
{
|
||||
id: getLocalParticipant(store.getState())?.id,
|
||||
id: localParticipant.id,
|
||||
name: getParticipantDisplayName(state, localParticipant.id),
|
||||
local: true
|
||||
},
|
||||
{ id: action.participant ? action.participant.getId() : undefined }
|
||||
{
|
||||
id: pId,
|
||||
name: getParticipantDisplayName(state, pId)
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case NOTIFY_CAMERA_ERROR:
|
||||
if (action.error) {
|
||||
@@ -156,14 +171,28 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
break;
|
||||
|
||||
case PARTICIPANT_KICKED:
|
||||
case PARTICIPANT_KICKED: {
|
||||
const state = store.getState();
|
||||
const kickedParticipant = getParticipantById(state, action.kicked);
|
||||
const kickerParticipant = getParticipantById(state, action.kicker);
|
||||
|
||||
if (!kickerParticipant || !kickedParticipant) {
|
||||
break;
|
||||
}
|
||||
|
||||
APP.API.notifyKickedOut(
|
||||
{
|
||||
id: action.kicked,
|
||||
local: false
|
||||
id: kickedParticipant.id,
|
||||
local: kickedParticipant.local,
|
||||
name: getParticipantDisplayName(state, kickedParticipant.id)
|
||||
},
|
||||
{ id: action.kicker });
|
||||
{
|
||||
id: kickerParticipant.id,
|
||||
local: kickerParticipant.local,
|
||||
name: getParticipantDisplayName(state, kickerParticipant.id)
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case PARTICIPANT_LEFT: {
|
||||
const { participant } = action;
|
||||
|
||||
@@ -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));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user