Compare commits

..

31 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
d2521bc67a feat(android) bump minimum API level to 24
Some of our dependencies (most notably WebRTC) have dropped it and we
can no longer claim to support API level 23).
2023-05-01 11:06:44 +02:00
damencho
38a293f8f6 fix(chat): Fixes long display name that overflow the message bubble. 2023-04-28 13:31:14 -05:00
damencho
13e8f992b5 fix(chat): White square when both scrolls are visible. 2023-04-28 13:31:14 -05:00
damencho
c384d0d3a9 Revert "fix(chat) Make the name fit the chat bubble"
This reverts commit e56c7070c2.
2023-04-28 13:31:14 -05:00
Дамян Минков
2b71fa512b feat: Adds conference duration to the amplitude stat. (#13294) 2023-04-28 11:09:13 -05:00
damencho
d381ceb040 feat: Shows html in notification warning. 2023-04-27 12:42:28 -05:00
damencho
e18c428f52 fix: Hide virtual background for unsupported browsers from vide preview. 2023-04-27 12:42:12 -05:00
Gabriel Borlea
9fc32dc59b fix: multiselect for invite people (#13287)
* fix: multiselect duplicates

* set multiselect height to 200px
2023-04-27 10:14:16 -05:00
robertpin
6f45622ef1 fix(dialog) Fix close animation moves whole body 2023-04-27 10:06:17 -05:00
robertpin
e56c7070c2 fix(chat) Make the name fit the chat bubble 2023-04-27 09:40:27 -05:00
damencho
0aef7a36aa fix: Fixes unresolved function. 2023-04-27 09:25:07 -05:00
damencho
c030cf941e feat: Implements amplitude events for messages count. 2023-04-27 09:24:59 -05:00
Robert Pintilii
f6760e4ac7 fix(chat) Focus input on chat open (#13285) 2023-04-27 17:23:47 +03:00
Robert Pintilii
9060c77307 ref(TS) Convert some native components to TS (#13266) 2023-04-27 08:44:20 +03:00
Дамян Минков
a1d018eef4 feat: Disables sending localstats in visitor mode. (#13279) 2023-04-27 07:38:57 +02:00
Jaya Allamsetty
3f724d8fb7 fix(visitors) Do not add tracks in redux to conference. 2023-04-26 16:51:19 -04:00
Jaya Allamsetty
49d69a5a02 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1623.0.0+c520877a...v1624.0.0+e3a8472f
2023-04-26 13:04:01 -04:00
damencho
6db9e42876 fix: Allows jicofo entering rooms without requiring a password.
The case where the main room is locked and everyone leaves it to a breakout room and then coming back allows jicofo entering without a password.
2023-04-26 09:04:55 -05:00
Jaya Allamsetty
ad3e8f9f53 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1620.0.0+7f0012f7...v1623.0.0+c520877a
2023-04-26 09:26:34 -04:00
Mihaela Dumitru
9f39caa247 feat(external-api) support assumed bandwidth bps config and command (#13164) 2023-04-26 15:32:53 +03:00
Avram Tudor
1402a63324 ref(keyboard-shortcuts) refactor keyboard shortcuts to use redux (#13260)
* ref(keyboard-shortcuts) refactor keyboard shortcuts to use redux

fix unsynced default value between config flag and local storage

* code review

* fix build
2023-04-26 11:21:42 +03:00
robertpin
a9863e65c3 fix(dial-in) Make text selectable on Dial In page 2023-04-25 19:41:27 -05:00
Robert Pintilii
646c58f7d1 ref(TS) Convert some native components to TS (#13264) 2023-04-25 13:50:52 +03:00
Дамян Минков
a78ea7ca9c fix: Updates the option for disabling iframe to show a warning. (#13263)
* fix: Updates the option for disabling iframe to show a warning.

It will give a timeout of 5 mins for the conference, before navigating away from it.

* squash: Fix lint error.

* squash: Fix mobile build.
2023-04-24 14:59:25 -05:00
Robert Pintilii
8b8565bf60 ref(TS) Convert some native components to TS (#13259) 2023-04-24 20:14:02 +03:00
Saúl Ibarra Corretgé
b9e30f3c1b ref(android) remove unused code
This basically reverts
e61ccc956f
since we are no longer interested in using Detox.

In addition, the WebRTC initialization code was only placed in the RAN
instance manager holder's App initialization path, which is now gone, so
add it to the Activity initialization path.
2023-04-24 14:08:50 +02:00
Robert Pintilii
96b6edccf8 ref(TS) Remove flow comments in TS files (#13258) 2023-04-24 14:49:56 +03:00
Robert Pintilii
2af9dc88e6 ref|(TS) Convert some native components to TS (#13239) 2023-04-24 14:09:50 +03:00
Robert Pintilii
eda25ca3c9 fix(toolbar) Remove focus on hide (#13256) 2023-04-24 14:07:42 +03:00
Amga
c99da17973 fix(lang) update Mongolian translation 2023-04-24 11:58:07 +02:00
Saúl Ibarra Corretgé
9e147d7842 fix(android) fix JitsiMeetActivity.onDestroy not leaving the room
When we refactored the external API to use broadcast actions leave() was
changes to use the hangup broadcast action.

This mechanism does not seem to work while onDestroy is getting
executed, however.

There is another way to accomplish the same, for the particular case of
hangup: to pass empty props to the running RN application. This was the
previous behavior too.

This PR introduces a new abort() method on JitsiMeetView, which does
exactly that, passes empty props to RN. This allows cleanup to happen
and the meeting properly ends when the activity is swipped from the
recents list.

Fixes: https://github.com/jitsi/jitsi-meet/issues/13175
2023-04-24 10:14:06 +02:00
268 changed files with 2093 additions and 2073 deletions

View File

@@ -7,7 +7,6 @@
android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MainApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<meta-data

View File

@@ -1,47 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet;
import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import org.jitsi.meet.sdk.JitsiReactNativeHost;
/**
* Application class for Jitsi Meet. The only reason why this exists is for Detox
* to believe our app is a "greenfield" app. SDK users need not use this.
*/
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new JitsiReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// Initialize RN
Log.d(this.getClass().getCanonicalName(), "app onCreate");
getReactNativeHost().getReactInstanceManager();
}
}

View File

@@ -19,7 +19,7 @@ buildscript {
ext {
buildToolsVersion = "31.0.0"
compileSdkVersion = 32
minSdkVersion = 23
minSdkVersion = 24
targetSdkVersion = 32
supportLibVersion = "28.0.0"

View File

@@ -16,6 +16,7 @@
package org.jitsi.meet.sdk;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -177,8 +178,11 @@ public class JitsiMeetActivity extends AppCompatActivity
}
protected void leave() {
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
if (this.jitsiView != null) {
this.jitsiView.abort();
} else {
JitsiMeetLogger.w("Cannot leave, view is null");
}
}
private @Nullable
@@ -295,6 +299,7 @@ public class JitsiMeetActivity extends AppCompatActivity
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
}
@SuppressLint("MissingSuperCall")
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);

View File

@@ -157,6 +157,14 @@ public class JitsiMeetView extends FrameLayout {
setProps(options != null ? options.asProps() : new Bundle());
}
/**
* Internal method which aborts running RN by passing empty props.
* This is only meant to be used from the enclosing Activity's onDestroy.
*/
public void abort() {
setProps(new Bundle());
}
/**
* Creates the {@code ReactRootView} for the given app name with the given
* props. Once created it's set as the view of this {@code FrameLayout}.

View File

@@ -1,41 +0,0 @@
package org.jitsi.meet.sdk;
import android.app.Application;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import java.util.List;
/**
* This is the minimal implementation of ReactNativeHost that will make things like the
* Detox testing framework believe we are a "greenfield" app.
*
* Generally speaking, apps using the SDK (other than the Jitsi Meet app itself) should not
* need to use this because the
*/
public class JitsiReactNativeHost extends ReactNativeHost {
public JitsiReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
// Unused since we override `createReactInstanceManager`.
return false;
}
@Override
protected List<ReactPackage> getPackages() {
// Unused since we override `createReactInstanceManager`.
return null;
}
@Override
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerHolder.initReactInstanceManager(this.getApplication());
return ReactInstanceManagerHolder.getReactInstanceManager();
}
}

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Application;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -227,11 +226,9 @@ class ReactInstanceManagerHolder {
* time. All {@code ReactRootView} instances will be tied to the one and
* only {@code ReactInstanceManager}.
*
* This method is only meant to be called when integrating with {@code JitsiReactNativeHost}.
*
* @param app {@code Application} current running Application.
* @param activity {@code Activity} current running Activity.
*/
static void initReactInstanceManager(Application app) {
static void initReactInstanceManager(Activity activity) {
if (reactInstanceManager != null) {
return;
}
@@ -244,34 +241,7 @@ class ReactInstanceManagerHolder {
options.videoDecoderFactory = new H264AndSoftwareVideoDecoderFactory(eglContext);
options.videoEncoderFactory = new H264AndSoftwareVideoEncoderFactory(eglContext);
Log.d(TAG, "initializing RN with Application");
reactInstanceManager
= ReactInstanceManager.builder()
.setApplication(app)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
.addPackages(getReactNativePackages())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.build();
}
/**
* Internal method to initialize the React Native instance manager. We
* create a single instance in order to load the JavaScript bundle a single
* time. All {@code ReactRootView} instances will be tied to the one and
* only {@code ReactInstanceManager}.
*
* @param activity {@code Activity} current running Activity.
*/
static void initReactInstanceManager(Activity activity) {
if (reactInstanceManager != null) {
return;
}
Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Activity");
Log.d(TAG, "initializing RN with Activity");
reactInstanceManager
= ReactInstanceManager.builder()

2
app.js
View File

@@ -18,7 +18,6 @@ import './react/features/base/jitsi-local-storage/setup';
import conference from './conference';
import API from './modules/API';
import UI from './modules/UI/UI';
import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
import translation from './modules/translation/translation';
// Initialize Olm as early as possible.
@@ -38,7 +37,6 @@ window.APP = {
'index.loaded': window.indexLoadedTime
},
keyboardshortcut,
translation,
UI
};

View File

@@ -140,6 +140,7 @@ import { downloadJSON } from './react/features/base/util/downloadJSON';
import { showDesktopPicker } from './react/features/desktop-picker/actions';
import { appendSuffix } from './react/features/display-name/functions';
import { maybeOpenFeedbackDialog, submitFeedback } from './react/features/feedback/actions';
import { initKeyboardShortcuts } from './react/features/keyboard-shortcuts/actions';
import { maybeSetLobbyChatMessageListener } from './react/features/lobby/actions.any';
import { setNoiseSuppressionEnabled } from './react/features/noise-suppression/actions';
import { hideNotification, showNotification, showWarningNotification } from './react/features/notifications/actions';
@@ -2308,10 +2309,7 @@ export default {
APP.UI.initConference();
if (!config.disableShortcuts) {
APP.keyboardshortcut.init();
}
dispatch(initKeyboardShortcuts());
dispatch(conferenceJoined(room));
const jwt = APP.store.getState()['features/base/jwt'];

View File

@@ -1406,6 +1406,7 @@ var config = {
disableAGC
disableAP
disableHPF
disableLocalStats
disableNS
enableTalkWhileMuted
forceJVB121Ratio

View File

@@ -74,6 +74,10 @@
a:active {
color: black;
}
&::-webkit-scrollbar-corner {
background: #3a3a3a;
}
}

View File

@@ -108,6 +108,10 @@
#largeVideoContainer {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
margin: 0 !important;
}
#largeVideoWrapper {

View File

@@ -57,6 +57,10 @@
line-height: 24px;
border-collapse: collapse;
* {
user-select: text;
}
thead {
text-align: left;
}

View File

@@ -84,8 +84,12 @@ Component "conference.jitmeet.example.com" "muc"
"polls";
--"token_verification";
"muc_rate_limit";
"muc_password_whitelist";
}
admins = { "focusUser@auth.jitmeet.example.com" }
muc_password_whitelist = {
"focusUser@auth.jitmeet.example.com"
}
muc_room_locking = false
muc_room_default_public_jids = true

6
globals.d.ts vendored
View File

@@ -10,12 +10,6 @@ declare global {
API: any;
conference: any;
debugLogs: any;
keyboardshortcut: {
registerShortcut: Function;
unregisterShortcut: Function;
openDialog: Function;
enable: Function;
}
};
const interfaceConfig: any;

View File

@@ -424,8 +424,8 @@
"tokenAuthFailedTitle": "Нэвтрэлт амжилтгүй",
"transcribing": "Орчуулах",
"unlockRoom": "$t(lockRoomPassword) уулзалтыг устгана уу",
"user": "User",
"userIdentifier": "User identifier",
"user": "Хэрэглэгч",
"userIdentifier": "Хэрэглэгчийн ID",
"userPassword": "хэрэглэгчийн нууц үг",
"verifyParticipantConfirm": "Нууц үгс хоорондоо тохирч байна",
"verifyParticipantDismiss": "Нууц үгс хоорондоо тохирохгүй байна",
@@ -451,8 +451,8 @@
"bad": "Муу",
"detailsLabel": "Энэ талаар илүү дэлгэрэнгүй.",
"good": "Сайн",
"rateExperience": "Уулзалтын туршлагаа үнэлэх",
"star": "Star",
"rateExperience": "Уулзалтыг үнэлэх",
"star": "Од",
"veryBad": "Маш муу",
"veryGood": "Маш сайн"
},
@@ -586,10 +586,10 @@
"youtubeTerms": "YouTube үйлчилгээний нөхцөл"
},
"lobby": {
"admit": "O",
"admit": "Ok",
"admitAll": "Бүгдийг зөвшөөр",
"backToKnockModeButton": "Зөвшөөрөл хүсэх",
"chat": "Чат",
"chat": "Зурвас",
"dialogTitle": "Лобби горим",
"disableDialogContent": "Лобби горим идэвхитэй байна. Ингэснээр таны уулзалтад зөвшөөрөлгүй хүн орох боломжгүй болдог. Та горимоо идэвхигүй болгохыг хүсч байна уу?",
"disableDialogSubmit": "Идэвхигүй болгох",
@@ -730,7 +730,7 @@
"participantsWantToJoin": "Уулзалтанд орохыг хүсч байна",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) өөр оролцогч устгасан",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) өөр оролцогчоос хийсэн",
"raiseHandAction": "Гараа өргө",
"raiseHandAction": "Гараа өргөнө үү",
"raisedHand": "{{name}} ярихыг хүсч байна.",
"raisedHands": "Нэмж {{participantName}} ба {{raisedHands}} хүмүүс",
"reactionSounds": "Дууг хаах",
@@ -991,14 +991,14 @@
"desktopShareWarning": "Шинэ тохиргоог идэвхижүүлэхийн тулд та дэлгэц хуваалцахаа дахин эхлүүлэх шаардлагатай.",
"devices": "Төхөөрөмжүүд",
"followMe": "Бүгд намайг дагаж байна",
"framesPerSecond": "frames-per-second",
"framesPerSecond": "fps",
"incomingMessage": "Ирсэн мессэж",
"language": "Хэл",
"loggedIn": "{{name}} нэвтэрсэн",
"maxStageParticipants": "Үндсэн тайз руу гарах оролцогчийн хамгийн их тоо(Туршилтынх)",
"microphones": "Микрофон",
"moderator": "Удирдагч",
"moderatorOptions": "Удирдагчийн сонголт",
"moderator": "Зохицуулагч",
"moderatorOptions": "Зохицуулагчийн сонголт",
"more": "Цааш",
"name": "Нэр",
"noDevice": "Байхгүй",
@@ -1006,9 +1006,9 @@
"participantJoined": "Оролцогч нэгдсэн",
"participantKnocking": "Оролцогчийн орсон лобби",
"participantLeft": "Оролцогч гарсан",
"playSounds": "Play sound on",
"playSounds": "Дууг нээх",
"reactions": "Уулзалтын реакциуд",
"sameAsSystem": "Same as system ({{label}})",
"sameAsSystem": "Системтэй адил({{label}})",
"selectAudioOutput": "Дууны гаралт",
"selectCamera": "Камер",
"selectMic": "Микрофон",
@@ -1018,7 +1018,7 @@
"startAudioMuted": "Бүгд дуугүй эхлэх",
"startReactionsMuted": "Бүх хүн рүү Чимээгүй эмоцийг илгээх",
"startVideoMuted": "Бүгдийн дүрс хаах",
"talkWhileMuted": "Бусад нь дуугүй үзTalk while muted",
"talkWhileMuted": "Нэг нь ярихад бусдын дууг хаах",
"title": "Тохиргоо",
"video": "Видео"
},
@@ -1038,7 +1038,7 @@
"displayNamePlaceholderText": "Жиш: Б.Болд",
"email": "Имэйл",
"emailPlaceholderText": "email@example.com",
"goTo": "Go to",
"goTo": "Шилжих",
"header": "Тохиргоо",
"help": "Тусламж",
"links": "Холбоос",
@@ -1070,8 +1070,8 @@
"sad": "Баргар",
"search": "Хайх",
"seconds": "{{count}}сек",
"speakerStats": "Яригчийн статистик",
"speakerTime": "Яригчийн цаг",
"speakerStats": "Оролцогчийн статистик",
"speakerTime": "Оролцогчийн ярьсан цаг",
"surprised": "Гайхсан"
},
"startupoverlay": {
@@ -1081,13 +1081,13 @@
},
"suspendedoverlay": {
"rejoinKeyTitle": "Дахин нэгдэх",
"text": "Дахин холбохын тулд <i>Rejoin</i> товчийг дарна уу.",
"text": "Дахин холбохын тулд <i>Дахин нэгдэх</i> товчийг дарна уу.",
"title": "Энэ компьютер унтарсан учир таны видео дуудлага тасарлаа."
},
"termsView": {
"title": "Нөхцөл"
},
"toggleTopPanelLabel": "Toggle top panel",
"toggleTopPanelLabel": "Дээд панел идэвхжүүлэх",
"toolbar": {
"Settings": "Тохиргоо",
"accessibilityLabel": {
@@ -1109,7 +1109,7 @@
"documentClose": "Хуваалцсан баримт хаах",
"documentOpen": "Хуваалцсан баримт нээх",
"download": "Манай програмуудыг татах",
"embedMeeting": "Embed meeting",
"embedMeeting": "Уулзалт нэгтгэх",
"endConference": "Уулзалтыг нийтээр нь хаах",
"enterFullScreen": "Бүтэн дэлгэцээр харах",
"enterTileView": "Нүдэн харагдац сонгох",
@@ -1118,10 +1118,10 @@
"expand": "Сунгах",
"feedback": "Санал хүсэлтээ үлдээх",
"fullScreen": "Бүтэн дэлгэц",
"giphy": "Toggle GIPHY menu",
"giphy": "GIPHY цэс идэвхжүүлэх",
"grantModerator": "Удирдагчийн эрх баталгаажуулах",
"hangup": "Салгах",
"heading": "Toolbar",
"heading": "Багаж",
"help": "Тусламж",
"hideWhiteboard": "Самбар нуух",
"invite": "Хүмүүсийг урих",
@@ -1155,7 +1155,7 @@
"remoteVideoMute": "Оролцогчийн камерийг хаах",
"security": "Нууцлалын тохиргоо",
"selectBackground": "Арын зураг сонгох",
"selfView": "Toggle self view",
"selfView": "Өөрийн харагдах идэвхжүүлэх",
"shareRoom": "Хүн урих",
"shareYourScreen": "Дэлгэц хуваалцах",
"shareaudio": "Дуугаа хуваалцах",
@@ -1190,12 +1190,12 @@
"closeParticipantsPane": "Оролцогчийн цонх хаах",
"closeReactionsMenu": "Реакци цэс хаах",
"disableNoiseSuppression": "Шуугиан дарахыг болиулах",
"disableReactionSounds": "Уулзалтын үеэр реакци чимээг хаах болно",
"disableReactionSounds": "Уулзалтын үеэр реакцийн чимээг хаах болно",
"documentClose": "Хуваалцсан бичиг баримт хаах",
"documentOpen": "Хуваалцсан бичиг баримт нээх",
"download": "Манай програмуудыг татах",
"e2ee": "Бүтэн нууцлал",
"embedMeeting": "Embed meeting",
"embedMeeting": "Уулзалтыг нэгтгэх",
"enableNoiseSuppression": "Шуугиан дарахыг идэвхижүүлэх",
"endConference": "Уулзалтыг нийтээр нь хаах",
"enterFullScreen": "Бүтэн дэлгэцээр харах",
@@ -1218,7 +1218,7 @@
"lobbyButtonEnable": "Лобби горим нээх",
"login": "Нэвтрэх",
"logout": "Гарах",
"lowerYourHand": "Гараа болих",
"lowerYourHand": "Гараа буулгах",
"moreActions": "Бусад үйлдэл",
"moreOptions": "Бусад тохиргоо",
"mute": "Дуу хаах/нээх",
@@ -1238,14 +1238,14 @@
"pip": "Зураг-зураг горим оруулах",
"privateMessage": "Хувийн зурвас илгээх",
"profile": "Профайлаа засна уу",
"raiseHand": "Гараа өргөх/болих",
"raiseYourHand": "Гараа өргө",
"reactionBoo": "Boo reaction",
"reactionClap": "Send clap reaction",
"reactionLaugh": "Send laugh reaction",
"reactionLike": "Send thumbs up reaction",
"reactionSilence": "Send silence reaction",
"reactionSurprised": "Send surprised reaction",
"raiseHand": "Гараа өргөх/буулгах",
"raiseYourHand": "Гараа өргөх",
"reactionBoo": "Boo реакци илгээх",
"reactionClap": "Алга таших реакци илгээх",
"reactionLaugh": "Инээх реакци илгээх",
"reactionLike": "Таалагдсан реакци илгээх",
"reactionSilence": "Чимээгүй реакци илгээх",
"reactionSurprised": "Гайхсан реакци илгээх",
"security": "Нууцлалын тохиргоо",
"selectBackground": "Арын зураг сонгох",
"shareRoom": "Хэн нэгнийг урих",
@@ -1349,7 +1349,7 @@
"remoteControl": "Алсын удирдлагыг эхлүүлэх / зогсоох",
"screenSharing": "Оролцогч дэлгэцээ хуваалцаж байна",
"show": "Үзүүлэх",
"showSelfView": "Show self view",
"showSelfView": "Өөрийн харагдац харуулах",
"unpinFromStage": "Онцлохоо болих",
"verify": "Оролцогчийг баталгаажуулах",
"videoMuted": "Камер идэвхигүй болсон",
@@ -1361,8 +1361,8 @@
"backgroundEffectError": "Арын зургийг идэвхижүүлэхэд алдаа гарлаа.",
"blur": "Бүдгэрүүлэх",
"deleteImage": "Зураг устгах",
"desktopShare": "Desktop share",
"desktopShareError": "Could not create desktop share",
"desktopShare": "Дэлгэцээ хуваалцах",
"desktopShareError": "Дэлгэц хуваалцахад алдаа гарлаа",
"image1": "Beach",
"image2": "White neutral wall",
"image3": "White empty room",
@@ -1377,7 +1377,7 @@
"title": "Виртуал бүдгэрүүлэлт",
"uploadedImage": "Оруулсан зураг {{index}}",
"webAssemblyWarning": "WebAssembly-г дэмждэггүй",
"webAssemblyWarningDescription": "WebAssembly disabled or not supported by this browser"
"webAssemblyWarningDescription": "Энэ хөтөч WebAssembly-г дэмждэггүй"
},
"visitors": {
"chatIndicator": "(visitor)",
@@ -1406,13 +1406,13 @@
"getHelp": "Тусламж авах",
"go": "OK",
"goSmall": "OK",
"headerSubtitle": "Secure and high quality meetings",
"headerSubtitle": "Найдвартай, чанартай цахим уулзалт",
"headerTitle": "Jitsi Meet",
"info": "Мэдээлэл",
"jitsiOnMobile": "Jitsi on mobile download our apps and start a meeting from anywhere",
"jitsiOnMobile": "Jitsi on mobile бидний аппыг татаад уулзалтанд хаанаас ч оролцоорой.",
"join": "ҮҮСГЭХ / НЭГДЭХ",
"logo": {
"calendar": "Calendar лого",
"calendar": "Хуанли лого",
"desktopPreviewThumbnail": "Desktop preview thumbnail",
"googleLogo": "Google лого",
"logoDeepLinking": "Jitsi meet лого",
@@ -1423,7 +1423,7 @@
"mobileDownLoadLinkAndroid": "Android апп татах",
"mobileDownLoadLinkFDroid": "F-Droid апп татах",
"mobileDownLoadLinkIos": "iOS апп татах",
"moderatedMessage": "Or <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">book a meeting URL</a> in advance where you are the only moderator.",
"moderatedMessage": "Эсвэл та энэ уулзалтын ганц зохицуулагч бол <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\"> уулзалтын URL-г</a> хадгалаарай.",
"privacy": "Нууцлал",
"recentList": "Онцлох",
"recentListDelete": "Устгах",
@@ -1436,7 +1436,7 @@
"sendFeedback": "Санал илгээх",
"settings": "Тохиргоо",
"startMeeting": "Уулзалт эхлүүлэх",
"terms": "Нөхцөлүүд",
"terms": "Нөхцлүүд",
"title": "Аюулгүй, үнэгүй видео уулзалт хийх боломжтой",
"upcomingMeetings": "Таны дараагийн уулзалтууд"
},

View File

@@ -675,6 +675,7 @@
"connectedTwoMembers": "{{first}} and {{second}} joined the meeting",
"dataChannelClosed": "Video quality impaired",
"dataChannelClosedDescription": "The bridge channel has been disconnected and thus video quality is limited to its lowest setting.",
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
"disconnected": "disconnected",
"displayNotifications": "Display notifications for",
"dontRemindMe": "Do not remind me",

View File

@@ -115,7 +115,11 @@ import { muteAllParticipants } from '../../react/features/video-menu/actions';
import { setVideoQuality } from '../../react/features/video-quality/actions';
import { getJitsiMeetTransport } from '../transport';
import { API_ID, ENDPOINT_TEXT_MESSAGE_NAME } from './constants';
import {
API_ID,
ASSUMED_BANDWIDTH_BPS,
ENDPOINT_TEXT_MESSAGE_NAME
} from './constants';
const logger = Logger.getLogger(__filename);
@@ -310,6 +314,23 @@ function initCommands() {
APP.store.dispatch(sendTones(tones, duration, pause));
},
'set-assumed-bandwidth-bps': value => {
logger.debug('Set assumed bandwidth bps command received', value);
if (typeof value !== 'number' || isNaN(value)) {
logger.error('Assumed bandwidth bps must be a number.');
return;
}
const { conference } = APP.store.getState()['features/base/conference'];
if (conference) {
conference.setAssumedBandwidthBps(value < ASSUMED_BANDWIDTH_BPS
? ASSUMED_BANDWIDTH_BPS
: value);
}
},
'set-follow-me': value => {
logger.debug('Set follow me command received');

View File

@@ -15,3 +15,10 @@ export const API_ID = parseURLParams(window.location).jitsi_meet_external_api_id
* The payload name for the datachannel/endpoint text message event.
*/
export const ENDPOINT_TEXT_MESSAGE_NAME = 'endpoint-text-message';
/**
* The min value that can be set for the assumed bandwidth.
* Setting it to this value means not assuming any bandwidth,
* but rather allowing the estimations to take place.
*/
export const ASSUMED_BANDWIDTH_BPS = -1;

View File

@@ -59,6 +59,7 @@ const commands = {
sendEndpointTextMessage: 'send-endpoint-text-message',
sendParticipantToRoom: 'send-participant-to-room',
sendTones: 'send-tones',
setAssumedBandwidthBps: 'set-assumed-bandwidth-bps',
setFollowMe: 'set-follow-me',
setLargeVideoParticipant: 'set-large-video-participant',
setMediaEncryptionKey: 'set-media-encryption-key',

View File

@@ -11,11 +11,11 @@ import { setColorAlpha } from '../../react/features/base/util/helpers';
import { setDocumentUrl } from '../../react/features/etherpad/actions';
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
import {
joinLeaveNotificationsDisabled,
setNotificationsEnabled,
showNotification
} from '../../react/features/notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../react/features/notifications/constants';
import { joinLeaveNotificationsDisabled } from '../../react/features/notifications/functions';
import {
dockToolbox,
setToolboxEnabled,

View File

@@ -1,261 +0,0 @@
/* global APP */
import { jitsiLocalStorage } from '@jitsi/js-utils';
import Logger from '@jitsi/logger';
import {
ACTION_SHORTCUT_PRESSED as PRESSED,
ACTION_SHORTCUT_RELEASED as RELEASED,
createShortcutEvent
} from '../../react/features/analytics/AnalyticsEvents';
import { sendAnalytics } from '../../react/features/analytics/functions';
import { clickOnVideo } from '../../react/features/filmstrip/actions';
import { openSettingsDialog } from '../../react/features/settings/actions';
import { SETTINGS_TABS } from '../../react/features/settings/constants';
const logger = Logger.getLogger(__filename);
/**
* Map of shortcuts. When a shortcut is registered it enters the mapping.
* @type {Map}
*/
const _shortcuts = new Map();
/**
* Map of registered keyboard keys and translation keys describing the
* action performed by the key.
* @type {Map}
*/
const _shortcutsHelp = new Map();
/**
* The key used to save in local storage if keyboard shortcuts are enabled.
*/
const _enableShortcutsKey = 'enableShortcuts';
/**
* Prefer keyboard handling of these elements over global shortcuts.
* If a button is triggered using the Spacebar it should not trigger PTT.
* If an input element is focused and M is pressed it should not mute audio.
*/
const _elementsBlacklist = [
'input',
'textarea',
'button',
'[role=button]',
'[role=menuitem]',
'[role=radio]',
'[role=tab]',
'[role=option]',
'[role=switch]',
'[role=range]',
'[role=log]'
];
/**
* An element selector for elements that have their own keyboard handling.
*/
const _focusedElementsSelector = `:focus:is(${_elementsBlacklist.join(',')})`;
/**
* Maps keycode to character, id of popover for given function and function.
*/
const KeyboardShortcut = {
init() {
this._initGlobalShortcuts();
window.onkeyup = e => {
if (!this.getEnabled()) {
return;
}
const key = this._getKeyboardKey(e).toUpperCase();
const num = parseInt(key, 10);
if (!document.querySelector(_focusedElementsSelector)) {
if (_shortcuts.has(key)) {
_shortcuts.get(key).function(e);
} else if (!isNaN(num) && num >= 0 && num <= 9) {
APP.store.dispatch(clickOnVideo(num));
}
}
};
window.onkeydown = e => {
if (!this.getEnabled()) {
return;
}
const focusedElement = document.querySelector(_focusedElementsSelector);
if (!focusedElement) {
if (this._getKeyboardKey(e).toUpperCase() === ' ') {
if (APP.conference.isLocalAudioMuted()) {
sendAnalytics(createShortcutEvent(
'push.to.talk',
PRESSED));
logger.log('Talk shortcut pressed');
APP.conference.muteAudio(false);
}
}
} else if (this._getKeyboardKey(e).toUpperCase() === 'ESCAPE') {
// Allow to remove focus from selected elements using ESC key.
if (focusedElement && focusedElement.blur) {
focusedElement.blur();
}
}
};
},
/**
* Enables/Disables the keyboard shortcuts.
* @param {boolean} value - the new value.
*/
enable(value) {
jitsiLocalStorage.setItem(_enableShortcutsKey, value);
},
getEnabled() {
// Should be enabled if not explicitly set to false
// eslint-disable-next-line no-unneeded-ternary
return jitsiLocalStorage.getItem(_enableShortcutsKey) === 'false' ? false : true;
},
getShortcutsDescriptions() {
return _shortcutsHelp;
},
/**
* Opens the {@SettingsDialog} dialog on the Shortcuts page.
*
* @returns {void}
*/
openDialog() {
APP.store.dispatch(openSettingsDialog(SETTINGS_TABS.SHORTCUTS, false));
},
/**
* Registers a new shortcut.
*
* @param shortcutChar the shortcut character triggering the action
* @param shortcutAttr the "shortcut" html element attribute mapping an
* element to this shortcut and used to show the shortcut character on the
* element tooltip
* @param exec the function to be executed when the shortcut is pressed
* @param helpDescription the description of the shortcut that would appear
* in the help menu
* @param altKey whether or not the alt key must be pressed.
*/
registerShortcut(// eslint-disable-line max-params
shortcutChar,
shortcutAttr,
exec,
helpDescription,
altKey = false) {
_shortcuts.set(altKey ? `:${shortcutChar}` : shortcutChar, {
character: shortcutChar,
function: exec,
shortcutAttr,
altKey
});
if (helpDescription) {
this._addShortcutToHelp(altKey ? `:${shortcutChar}` : shortcutChar, helpDescription);
}
},
/**
* Unregisters a shortcut.
*
* @param shortcutChar unregisters the given shortcut, which means it will
* no longer be usable
* @param altKey whether or not shortcut is combo with alt key
*/
unregisterShortcut(shortcutChar, altKey = false) {
_shortcuts.delete(altKey ? `:${shortcutChar}` : shortcutChar);
_shortcutsHelp.delete(shortcutChar);
},
/**
* @param e a KeyboardEvent
* @returns {string} e.key or something close if not supported
*/
_getKeyboardKey(e) {
// If alt is pressed a different char can be returned so this takes
// the char from the code. It also prefixes with a colon to differentiate
// alt combo from simple keypress.
if (e.altKey) {
const key = e.code.replace('Key', '');
return `:${key}`;
}
// If e.key is a string, then it is assumed it already plainly states
// the key pressed. This may not be true in all cases, such as with Edge
// and "?", when the browser cannot properly map a key press event to a
// keyboard key. To be safe, when a key is "Unidentified" it must be
// further analyzed by jitsi to a key using e.which.
if (typeof e.key === 'string' && e.key !== 'Unidentified') {
return e.key;
}
if (e.type === 'keypress'
&& ((e.which >= 32 && e.which <= 126)
|| (e.which >= 160 && e.which <= 255))) {
return String.fromCharCode(e.which);
}
// try to fallback (0-9A-Za-z and QWERTY keyboard)
switch (e.which) {
case 27:
return 'Escape';
case 191:
return e.shiftKey ? '?' : '/';
}
if (e.shiftKey || e.type === 'keypress') {
return String.fromCharCode(e.which);
}
return String.fromCharCode(e.which).toLowerCase();
},
/**
* Adds the given shortcut to the help dialog.
*
* @param shortcutChar the shortcut character
* @param shortcutDescriptionKey the description of the shortcut
* @private
*/
_addShortcutToHelp(shortcutChar, shortcutDescriptionKey) {
_shortcutsHelp.set(shortcutChar, shortcutDescriptionKey);
},
/**
* Initialise global shortcuts.
* Global shortcuts are shortcuts for features that don't have a button or
* link associated with the action. In other words they represent actions
* triggered _only_ with a shortcut.
*/
_initGlobalShortcuts() {
this.registerShortcut('?', null, () => {
sendAnalytics(createShortcutEvent('help'));
this.openDialog();
}, 'keyboardShortcuts.toggleShortcuts');
// register SPACE shortcut in two steps to insure visibility of help
// message
this.registerShortcut(' ', null, () => {
sendAnalytics(createShortcutEvent('push.to.talk', RELEASED));
logger.log('Talk shortcut released');
APP.conference.muteAudio(true);
});
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
/**
* FIXME: Currently focus keys are directly implemented below in
* onkeyup. They should be moved to the SmallVideo instead.
*/
this._addShortcutToHelp('0', 'keyboardShortcuts.focusLocal');
this._addShortcutToHelp('1-9', 'keyboardShortcuts.focusRemote');
}
};
export default KeyboardShortcut;

79
package-lock.json generated
View File

@@ -60,7 +60,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -130,10 +130,12 @@
"@types/audioworklet": "0.0.29",
"@types/js-md5": "0.4.3",
"@types/lodash": "4.14.182",
"@types/punycode": "2.1.0",
"@types/react": "17.0.14",
"@types/react-dom": "17.0.14",
"@types/react-linkify": "1.0.1",
"@types/react-native": "0.68.9",
"@types/react-native-video": "5.0.14",
"@types/react-redux": "7.1.24",
"@types/react-window": "1.8.5",
"@types/resemblejs": "^4.1.0",
@@ -5761,6 +5763,12 @@
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"node_modules/@types/punycode": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/punycode/-/punycode-2.1.0.tgz",
"integrity": "sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g==",
"dev": true
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@@ -5818,6 +5826,16 @@
"@types/react": "^17"
}
},
"node_modules/@types/react-native-video": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/@types/react-native-video/-/react-native-video-5.0.14.tgz",
"integrity": "sha512-KdcyY4HY/Q1l6f5qQA337BNVN+GsdZy836j9CXbWHZ008VVNzSlnJypJQPsnUgI0EPBw/uG/lyJk6cg9Jj1syg==",
"dev": true,
"dependencies": {
"@types/react": "*",
"@types/react-native": "*"
}
},
"node_modules/@types/react-redux": {
"version": "7.1.24",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
@@ -12721,8 +12739,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"integrity": "sha512-F3vOUwD/ys5dGtswR3C5IK8JPDOG+71J+nhkEDLotkni/Zws8KHzf8Ye332UuetUjCTGJP3CbRFe9yJdYERe7g==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -12740,7 +12758,7 @@
"patch-package": "6.5.1",
"promise.allsettled": "1.0.4",
"sdp-transform": "2.3.0",
"strophe.js": "1.6.0",
"strophe.js": "1.5.0",
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "git+https://github.com/jitsi/strophejs-plugin-stream-management#679be5902097ed612fb5062b5549f3f32b6f5f47",
"uuid": "8.1.0",
@@ -17597,18 +17615,27 @@
}
},
"node_modules/strophe.js": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
"dependencies": {
"abab": "^2.0.3",
"karma-rollup-preprocessor": "^7.0.8"
},
"optionalDependencies": {
"@xmldom/xmldom": "0.8.3",
"@xmldom/xmldom": "0.8.2",
"ws": "^8.5.0"
}
},
"node_modules/strophe.js/node_modules/@xmldom/xmldom": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/strophe.js/node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
@@ -23792,6 +23819,12 @@
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"@types/punycode": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/punycode/-/punycode-2.1.0.tgz",
"integrity": "sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g==",
"dev": true
},
"@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@@ -23849,6 +23882,16 @@
"@types/react": "^17"
}
},
"@types/react-native-video": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/@types/react-native-video/-/react-native-video-5.0.14.tgz",
"integrity": "sha512-KdcyY4HY/Q1l6f5qQA337BNVN+GsdZy836j9CXbWHZ008VVNzSlnJypJQPsnUgI0EPBw/uG/lyJk6cg9Jj1syg==",
"dev": true,
"requires": {
"@types/react": "*",
"@types/react-native": "*"
}
},
"@types/react-redux": {
"version": "7.1.24",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
@@ -29074,8 +29117,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"integrity": "sha512-F3vOUwD/ys5dGtswR3C5IK8JPDOG+71J+nhkEDLotkni/Zws8KHzf8Ye332UuetUjCTGJP3CbRFe9yJdYERe7g==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
"requires": {
"@jitsi/js-utils": "2.0.0",
"@jitsi/logger": "2.0.0",
@@ -29091,7 +29134,7 @@
"patch-package": "6.5.1",
"promise.allsettled": "1.0.4",
"sdp-transform": "2.3.0",
"strophe.js": "1.6.0",
"strophe.js": "1.5.0",
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "git+https://github.com/jitsi/strophejs-plugin-stream-management#679be5902097ed612fb5062b5549f3f32b6f5f47",
"uuid": "8.1.0",
@@ -32802,16 +32845,22 @@
"dev": true
},
"strophe.js": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
"requires": {
"@xmldom/xmldom": "0.8.7",
"@xmldom/xmldom": "0.8.2",
"abab": "^2.0.3",
"karma-rollup-preprocessor": "^7.0.8",
"ws": "^8.5.0"
},
"dependencies": {
"@xmldom/xmldom": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
"optional": true
},
"ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",

View File

@@ -65,7 +65,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -135,10 +135,12 @@
"@types/audioworklet": "0.0.29",
"@types/js-md5": "0.4.3",
"@types/lodash": "4.14.182",
"@types/punycode": "2.1.0",
"@types/react": "17.0.14",
"@types/react-dom": "17.0.14",
"@types/react-linkify": "1.0.1",
"@types/react-native": "0.68.9",
"@types/react-native-video": "5.0.14",
"@types/react-redux": "7.1.24",
"@types/react-window": "1.8.5",
"@types/resemblejs": "^4.1.0",
@@ -178,7 +180,7 @@
},
"overrides": {
"strophe.js@1.6.0": {
"@xmldom/xmldom": "0.8.7"
"@xmldom/xmldom": "0.8.7"
}
},
"engines": {

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action which signals that local media duration has changed.
*

View File

@@ -129,14 +129,6 @@ export function appNavigate(uri?: string) {
dispatch(setLocationURL(locationURL));
dispatch(setConfig(config));
if (inIframe() && getState()['features/base/config'].disableIframeAPI) {
// in case iframeAPI is disabled redirect to the promotional page
dispatch(redirectToStaticPage('static/close3.html', `#jitsi_meet_external_api_id=${API_ID}`));
return;
}
dispatch(setRoom(room));
};
}

View File

@@ -5,6 +5,7 @@ import '../base/media/middleware';
import '../dynamic-branding/middleware';
import '../e2ee/middleware';
import '../external-api/middleware';
import '../keyboard-shortcuts/middleware';
import '../no-audio-signal/middleware';
import '../notifications/middleware';
import '../noise-detection/middleware';

View File

@@ -1,5 +1,3 @@
// @flow
import '../analytics/reducer';
import '../authentication/reducer';
import '../av-moderation/reducer';

View File

@@ -1,5 +1,3 @@
// @flow
import '../mobile/audio-mode/reducer';
import '../mobile/background/reducer';
import '../mobile/call-integration/reducer';

View File

@@ -3,6 +3,7 @@ import '../base/tooltip/reducer';
import '../e2ee/reducer';
import '../face-landmarks/reducer';
import '../feedback/reducer';
import '../keyboard-shortcuts/reducer';
import '../no-audio-signal/reducer';
import '../noise-detection/reducer';
import '../participants-pane/reducer';

View File

@@ -43,6 +43,7 @@ import { IGifsState } from '../gifs/reducer';
import { IGoogleApiState } from '../google-api/reducer';
import { IInviteState } from '../invite/reducer';
import { IJaaSState } from '../jaas/reducer';
import { IKeyboardShortcutsState } from '../keyboard-shortcuts/types';
import { ILargeVideoState } from '../large-video/reducer';
import { ILobbyState } from '../lobby/reducer';
import { IMobileAudioModeState } from '../mobile/audio-mode/reducer';
@@ -133,6 +134,7 @@ export interface IReduxState {
'features/google-api': IGoogleApiState;
'features/invite': IInviteState;
'features/jaas': IJaaSState;
'features/keyboard-shortcuts': IKeyboardShortcutsState;
'features/large-video': ILargeVideoState;
'features/lobby': ILobbyState;
'features/mobile/audio-mode': IMobileAudioModeState;

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action which sets the audio-only flag for the current
* conference.

View File

@@ -3,6 +3,7 @@ import { sendAnalytics } from '../../analytics/functions';
import { appNavigate } from '../../app/actions';
import { IReduxState, IStore } from '../../app/types';
import { endpointMessageReceived } from '../../subtitles/actions.any';
import { iAmVisitor } from '../../visitors/functions';
import { getReplaceParticipant } from '../config/functions';
import { disconnect } from '../connection/actions';
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
@@ -450,11 +451,12 @@ export function conferenceUniqueIdSet(conference: IJitsiConference) {
*/
export function _conferenceWillJoin(conference: IJitsiConference) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const localTracks
= getLocalTracks(getState()['features/base/tracks'])
= getLocalTracks(state['features/base/tracks'])
.map(t => t.jitsiTrack);
if (localTracks.length) {
if (localTracks.length && !iAmVisitor(state)) {
_addLocalTracksToConference(conference, localTracks);
}
@@ -806,7 +808,7 @@ export function setStartReactionsMuted(muted: boolean, updateBackend = false) {
export function setPassword(
conference: IJitsiConference | undefined,
method: Function | undefined,
password: string) {
password?: string) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
if (!conference) {
return;

View File

@@ -159,7 +159,6 @@ export function forEachConference(
// Does the value of the base/conference's property look like a
// JitsiConference?
if (v && typeof v === 'object') {
// $FlowFixMe
const url: URL = v[JITSI_CONFERENCE_URL_KEY];
// XXX The Web version of Jitsi Meet does not utilize
@@ -309,6 +308,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
muc: config.oldConfig.hosts.muc
},
focusUserJid: focusJid,
disableLocalStats: false,
bosh: config.oldConfig.bosh && appendURLParam(config.oldConfig.bosh, 'customusername', username),
websocket: config.oldConfig.websocket
&& appendURLParam(config.oldConfig.websocket, 'customusername', username),
@@ -339,6 +339,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
},
focusUserJid: focusJid,
disableFocus: true, // This flag disables sending the initial conference request
disableLocalStats: true,
bosh: config.bosh && appendURLParam(config.bosh, 'vnode', vnode),
websocket: config.websocket && appendURLParam(config.websocket, 'vnode', vnode)
};

View File

@@ -104,6 +104,7 @@ export interface IJitsiConference {
sendTextMessage: Function;
sendTones: Function;
sessionId: string;
setAssumedBandwidthBps: (value: number) => void;
setDesktopSharingFrameRate: Function;
setDisplayName: Function;
setLocalParticipantProperty: Function;

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action which signals that a connection disconnected.
*

View File

@@ -19,7 +19,7 @@ interface IProps extends AbstractProps, WithTranslation {
/**
* The i18n key of the text label for the cancel button.
*/
cancelLabel: string;
cancelLabel?: string;
/**
* The React {@code Component} children.
@@ -29,7 +29,7 @@ interface IProps extends AbstractProps, WithTranslation {
/**
* The i18n key of the text label for the confirm button.
*/
confirmLabel: string;
confirmLabel?: string;
/**
* Dialog description key for translations.

View File

@@ -30,12 +30,12 @@ export type DialogProps = {
/**
* The handler for onCancel event.
*/
onCancel: Function;
onCancel?: Function;
/**
* The handler for the event when submitting the dialog.
*/
onSubmit: Function;
onSubmit?: Function;
/**
* Additional style to be applied on the dialog.

View File

@@ -1,5 +1,3 @@
// @flow
/**
* Flag indicating if add-people functionality should be enabled.
* Default: enabled (true).

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action which signals that i18next has been initialized.
*/

View File

@@ -90,7 +90,7 @@ interface IProps extends IIconProps {
/**
* Style object to be applied.
*/
style?: StyleType;
style?: StyleType | StyleType[];
/**
* TabIndex for the Icon.

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action to add known domains to the list of domains known
* to the feature base/known-domains.

View File

@@ -29,7 +29,7 @@ interface IState {
* A react {@code Component} that implements an expanded label as tooltip-like
* component to explain the meaning of the {@code Label}.
*/
export default class ExpandedLabel<P extends IProps> extends Component<P, IState> {
export default abstract class ExpandedLabel<P extends IProps> extends Component<P, IState> {
/**
* Instantiates a new {@code ExpandedLabel} instance.
*
@@ -85,7 +85,7 @@ export default class ExpandedLabel<P extends IProps> extends Component<P, IState
*
* @returns {string}
*/
_getLabel: () => string;
abstract _getLabel(): string;
/**
* Defines the color of the expanded label. This function returns a default

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action which sets the last-n for the conference.
*

View File

@@ -1,5 +1,5 @@
import { useHeaderHeight } from '@react-navigation/elements';
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import {
Keyboard,
KeyboardAvoidingView,
@@ -8,44 +8,44 @@ import {
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { StyleType } from '../../styles';
import { StyleType } from '../../styles/functions.any';
type Props = {
interface IProps {
/**
* Adds bottom padding.
*/
addBottomPadding?: boolean,
addBottomPadding?: boolean;
/**
* The children component(s) of the Modal, to be rendered.
*/
children: ReactElement,
children: React.ReactNode;
/**
* Additional style to be appended to the KeyboardAvoidingView content container.
*/
contentContainerStyle?: StyleType,
contentContainerStyle?: StyleType;
/**
* Disable forced keyboard dismiss?
*/
disableForcedKeyboardDismiss?: boolean,
disableForcedKeyboardDismiss?: boolean;
/**
* Is a text input rendered at the bottom of the screen?
*/
hasBottomTextInput: boolean,
hasBottomTextInput: boolean;
/**
* Is the screen rendering a tab navigator?
*/
hasTabNavigator: boolean,
hasTabNavigator: boolean;
/**
* Additional style to be appended to the KeyboardAvoidingView.
*/
style?: StyleType
style?: StyleType;
}
const JitsiKeyboardAvoidingView = (
@@ -57,7 +57,7 @@ const JitsiKeyboardAvoidingView = (
hasTabNavigator,
hasBottomTextInput,
style
}: Props) => {
}: IProps) => {
const headerHeight = useHeaderHeight();
const insets = useSafeAreaInsets();
const [ bottomPadding, setBottomPadding ] = useState(insets.bottom);
@@ -77,11 +77,11 @@ const JitsiKeyboardAvoidingView = (
const iosVerticalOffset
= headerHeight + noNotchDevicePadding + tabNavigatorPadding;
const androidVerticalOffset = hasBottomTextInput
? headerHeight + StatusBar.currentHeight : headerHeight;
? headerHeight + Number(StatusBar.currentHeight) : headerHeight;
// Tells the view what to do with taps
const shouldSetResponse = useCallback(() => !disableForcedKeyboardDismiss);
const onRelease = useCallback(() => Keyboard.dismiss());
const shouldSetResponse = useCallback(() => !disableForcedKeyboardDismiss, []);
const onRelease = useCallback(() => Keyboard.dismiss(), []);
return (
<KeyboardAvoidingView

View File

@@ -1,59 +1,58 @@
import React from 'react';
import { View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Edge, SafeAreaView } from 'react-native-safe-area-context';
import { StyleType } from '../../styles';
import { StyleType } from '../../styles/functions.any';
import JitsiKeyboardAvoidingView from './JitsiKeyboardAvoidingView';
import styles from './styles';
type Props = {
interface IProps {
/**
* Adds bottom padding.
*/
addBottomPadding?: boolean,
/**
* Additional style to be appended to the KeyboardAvoidingView content container.
*/
contentContainerStyle?: StyleType,
addBottomPadding?: boolean;
/**
* The children component(s) of the Modal, to be rendered.
*/
children: React.ReactNode,
children: React.ReactNode;
/**
* Additional style to be appended to the KeyboardAvoidingView content container.
*/
contentContainerStyle?: StyleType;
/**
* Disabled forced keyboard dismiss?
*/
disableForcedKeyboardDismiss?: boolean,
disableForcedKeyboardDismiss?: boolean;
/**
* Optional function that renders a footer component, if needed.
*/
footerComponent?: Function,
footerComponent?: Function;
/**
* Is a text input rendered at the bottom of the screen?
*/
hasBottomTextInput?: boolean,
hasBottomTextInput?: boolean;
/**
* Is the screen rendering a tab navigator?
*/
hasTabNavigator?: boolean,
hasTabNavigator?: boolean;
/**
* Insets for the SafeAreaView.
*/
safeAreaInsets?: Array,
safeAreaInsets?: Edge[];
/**
* Additional style to be appended to the KeyboardAvoidingView containing the content of the modal.
*/
style?: StyleType
style?: StyleType;
}
const JitsiScreen = ({
@@ -66,7 +65,7 @@ const JitsiScreen = ({
hasBottomTextInput = false,
safeAreaInsets = [ 'left', 'right' ],
style
}: Props) => {
}: IProps) => {
const renderContent = () => (
<JitsiKeyboardAvoidingView
addBottomPadding = { addBottomPadding }
@@ -80,7 +79,7 @@ const JitsiScreen = ({
style = { styles.safeArea }>
{ children }
</SafeAreaView>
{ footerComponent && footerComponent() }
{ footerComponent?.() }
</JitsiKeyboardAvoidingView>
);

View File

@@ -1,5 +1,4 @@
// @flow
import { IStateful } from '../../app/types';
import { toState } from '../../redux/functions';
/**
@@ -11,7 +10,7 @@ import { toState } from '../../redux/functions';
* features/base/config.
* @returns {number}.
*/
export function getClientWidth(stateful: Object) {
export function getClientWidth(stateful: IStateful) {
const state = toState(stateful)['features/base/responsive-ui'];
return state.clientWidth;
@@ -26,7 +25,7 @@ export function getClientWidth(stateful: Object) {
* features/base/config.
* @returns {number}.
*/
export function getClientHeight(stateful: Object) {
export function getClientHeight(stateful: IStateful) {
const state = toState(stateful)['features/base/responsive-ui'];
return state.clientHeight;

View File

@@ -1,5 +1,3 @@
// @flow
/**
* Create an action for when dominant speaker changes.
*

View File

@@ -1,20 +1,24 @@
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { GestureResponderEvent, Text, TextStyle, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import {
isTrackStreamingStatusActive,
isTrackStreamingStatusInactive
} from '../../../connection-indicator/functions';
import SharedVideo from '../../../shared-video/components/native/SharedVideo';
import { IStateful } from '../../app/types';
import Avatar from '../../avatar/components/Avatar';
import { translate } from '../../i18n/functions';
import VideoTrack from '../../media/components/native/VideoTrack';
import { shouldRenderVideoTrack } from '../../media/functions';
import Container from '../../react/components/native/Container';
import { toState } from '../../redux/functions';
import { StyleType } from '../../styles/functions.any';
import TestHint from '../../testing/components/TestHint';
import { getVideoTrackByParticipant } from '../../tracks/functions';
import { ITrack } from '../../tracks/types';
import { getParticipantById, getParticipantDisplayName, isSharedVideoParticipant } from '../functions';
import styles from './styles';
@@ -22,69 +26,69 @@ import styles from './styles';
/**
* The type of the React {@link Component} props of {@link ParticipantView}.
*/
type Props = {
interface IProps {
/**
* Whether the connection is inactive or not.
*
* @private
*/
_isConnectionInactive: boolean,
_isConnectionInactive: boolean;
/**
* Whether the participant is a shared video participant.
*/
_isSharedVideoParticipant: boolean,
_isSharedVideoParticipant: boolean;
/**
* The name of the participant which this component represents.
*
* @private
*/
_participantName: string,
_participantName: string;
/**
* True if the video should be rendered, false otherwise.
*/
_renderVideo: boolean,
_renderVideo: boolean;
/**
* The video Track of the participant with {@link #participantId}.
*/
_videoTrack: Object,
_videoTrack?: ITrack;
/**
* The avatar size.
*/
avatarSize: number,
avatarSize: number;
/**
* Whether video should be disabled for his view.
*/
disableVideo: ?boolean,
disableVideo?: boolean;
/**
* Callback to invoke when the {@code ParticipantView} is clicked/pressed.
*/
onPress: Function,
onPress: (e?: GestureResponderEvent) => void;
/**
* The ID of the participant (to be) depicted by {@link ParticipantView}.
*
* @public
*/
participantId: string,
participantId: string;
/**
* The style, if any, to apply to {@link ParticipantView} in addition to its
* default style.
*/
style: Object,
style: StyleType;
/**
* The function to translate human-readable text.
*/
t: Function,
t: Function;
/**
* The test hint id which can be used to locate the {@code ParticipantView}
@@ -92,26 +96,26 @@ type Props = {
* {@code participantId} with the following format will be used:
* {@code `org.jitsi.meet.Participant#${participantId}`}.
*/
testHintId: ?string,
testHintId?: string;
/**
* Indicates if the connectivity info label should be shown, if appropriate.
* It will be shown in case the connection is interrupted.
*/
useConnectivityInfoLabel: boolean,
useConnectivityInfoLabel: boolean;
/**
* The z-order of the {@link Video} of {@link ParticipantView} in the
* stacking space of all {@code Video}s. For more details, refer to the
* {@code zOrder} property of the {@code Video} class for React Native.
*/
zOrder: number,
zOrder: number;
/**
* Indicates whether zooming (pinch to zoom and/or drag) is enabled.
*/
zoomEnabled: boolean
};
zoomEnabled: boolean;
}
/**
* Implements a React Component which depicts a specific participant's avatar
@@ -119,7 +123,7 @@ type Props = {
*
* @augments Component
*/
class ParticipantView extends Component<Props> {
class ParticipantView extends Component<IProps> {
/**
* Renders the inactive connection status label.
@@ -144,8 +148,8 @@ class ParticipantView extends Component<Props> {
return (
<View
pointerEvents = 'box-none'
style = { containerStyle }>
<Text style = { styles.connectionInfoText }>
style = { containerStyle as ViewStyle }>
<Text style = { styles.connectionInfoText as TextStyle }>
{ t('connection.LOW_BANDWIDTH', { displayName }) }
</Text>
</View>
@@ -200,7 +204,7 @@ class ParticipantView extends Component<Props> {
zoomEnabled = { this.props.zoomEnabled } /> }
{ !renderSharedVideo && !renderVideo
&& <View style = { styles.avatarContainer }>
&& <View style = { styles.avatarContainer as ViewStyle }>
<Avatar
participantId = { this.props.participantId }
size = { this.props.avatarSize } />
@@ -221,9 +225,9 @@ class ParticipantView extends Component<Props> {
* @param {Object} ownProps - The React {@code Component} props passed to the
* associated (instance of) {@code ParticipantView}.
* @private
* @returns {Props}
* @returns {IProps}
*/
function _mapStateToProps(state, ownProps) {
function _mapStateToProps(state: IReduxState, ownProps: any) {
const { disableVideo, participantId } = ownProps;
const participant = getParticipantById(state, participantId);
const videoTrack = getVideoTrackByParticipant(state, participant);
@@ -245,7 +249,7 @@ function _mapStateToProps(state, ownProps) {
* @param {string} id - The ID of the participant.
* @returns {boolean}
*/
function shouldRenderParticipantVideo(stateful, id) {
function shouldRenderParticipantVideo(stateful: IStateful, id: string) {
const state = toState(stateful);
const participant = getParticipantById(state, id);

View File

@@ -1,5 +1,3 @@
// @flow
import { BoxModel } from '../../styles/components/styles/BoxModel';
import { ColorPalette } from '../../styles/components/styles/ColorPalette';

View File

@@ -48,7 +48,7 @@ export interface IProps {
* The style (as in stylesheet) to be applied to this
* {@code AbstractContainer}.
*/
style?: StyleType;
style?: StyleType | StyleType[];
tabIndex?: number;
@@ -58,7 +58,7 @@ export interface IProps {
* undefined and {@link onClick} is defined, {@code touchFeedback} is
* considered defined as {@code true}.
*/
touchFeedback?: Function;
touchFeedback?: boolean;
/**
* Color to display when clicked.

View File

@@ -1,15 +1,13 @@
// @flow
import React, { Component } from 'react';
import { View } from 'react-native';
import { View, ViewStyle } from 'react-native';
import Icon from '../../../icons/components/Icon';
import { type StyleType } from '../../../styles';
import { StyleType } from '../../../styles/functions.any';
import styles from './indicatorstyles';
import styles from './indicatorStyles';
import { BASE_INDICATOR } from './styles';
type Props = {
interface IProps {
/**
* Overwritten background color when indicator is highlighted.
@@ -19,24 +17,24 @@ type Props = {
/**
* True if a highlighted background has to be applied.
*/
highlight: boolean,
highlight?: boolean;
/**
* The name of the icon to be used as the indicator.
*/
icon: string,
icon: Function;
/**
* Additional style to be applied to the icon element.
*/
iconStyle: StyleType
};
iconStyle?: StyleType;
}
/**
* Implements a base indicator to be reused across all native indicators on
* the filmstrip.
*/
export default class BaseIndicator extends Component<Props> {
export default class BaseIndicator extends Component<IProps> {
/**
* Implements React's {@link Component#render()}.
*
@@ -47,12 +45,12 @@ export default class BaseIndicator extends Component<Props> {
return (
<View
style = { BASE_INDICATOR }>
style = { BASE_INDICATOR as ViewStyle }>
<Icon
src = { icon }
style = { [
styles.indicator,
iconStyle
iconStyle ?? {}
] } />
</View>
);

View File

@@ -1,30 +1,28 @@
// @flow
import React, { Component } from 'react';
import { Image } from 'react-native';
/**
* The type of the React {@code Component} props of {@link Image}.
*/
type Props = {
interface IProps {
/**
* The ImageSource to be rendered as image.
*/
src: Object,
src: Object;
/**
* The component's external style.
*/
style: Object
};
style: Object;
}
/**
* A component rendering aN IMAGE.
*
* @augments Component
*/
export default class ImageImpl extends Component<Props> {
export default class ImageImpl extends Component<IProps> {
/**
* Implements React's {@link Component#render()}.
*

View File

@@ -1,5 +1,3 @@
/* @flow */
import React, { Component } from 'react';
import { Linking } from 'react-native';
@@ -8,40 +6,40 @@ import Text from './Text';
/**
* The type of the React {@code Component} props of {@link Link}.
*/
type Props = {
interface IProps {
/**
* The children to be displayed within this Link.
*/
children: React$Node,
children: React.ReactNode;
/**
* Notifies that this Link failed to open the URL associated with it.
*/
onLinkingOpenURLRejected?: Function,
onLinkingOpenURLRejected?: Function;
/**
* The CSS style to be applied to this Link for the purposes of display.
*/
style?: Object,
style?: Object;
/**
* The URL to be opened when this Link is clicked/pressed.
*/
url: string
};
url: string;
}
/**
* Implements a (hyper)link to a URL in the fashion of the HTML anchor element
* and its href attribute.
*/
export default class Link extends Component<Props> {
export default class Link extends Component<IProps> {
/**
* Initializes a new Link instance.
*
* @param {Object} props - Component properties.
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
// Bind event handlers so they are only bound once for every instance.
@@ -71,14 +69,12 @@ export default class Link extends Component<Props> {
* @private
* @returns {void}
*/
_onLinkingOpenURLRejected(reason) {
_onLinkingOpenURLRejected(reason: Error) {
const onRejected = this.props.onLinkingOpenURLRejected;
onRejected && onRejected(reason);
onRejected?.(reason);
}
_onPress: () => void;
/**
* Handles press on this Link. Opens the URL associated with this Link.
*

View File

@@ -1,42 +1,40 @@
// @flow
import punycode from 'punycode';
import React, { Component } from 'react';
import ReactLinkify from 'react-linkify';
import { Text } from 'react-native';
import { type StyleType } from '../../../styles';
import { StyleType } from '../../../styles/functions.any';
import Link from './Link';
type Props = {
interface IProps {
/**
* The children of the component.
*/
children: React$Node,
children: React.ReactNode;
/**
* The extra styles to be applied to links.
*/
linkStyle: StyleType,
linkStyle: StyleType;
/**
* The extra styles to be applied to text.
*/
style?: StyleType
};
style?: StyleType;
}
/**
* Implements a react native wrapper for the react-linkify component.
*/
export default class Linkify extends Component<Props> {
export default class Linkify extends Component<IProps> {
/**
* Initiates a new {@code Component}.
*
* @inheritdoc
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
this._componentDecorator = this._componentDecorator.bind(this);
@@ -60,8 +58,6 @@ export default class Linkify extends Component<Props> {
);
}
_componentDecorator: (string, string, number) => React$Node;
/**
* Implements a component decorator for react-linkify.
*

View File

@@ -15,6 +15,8 @@ interface IProps {
* prop of the native component.
*/
size?: 'large' | 'small' | 'medium';
style?: any;
}
/**

View File

@@ -1,7 +1,5 @@
// @flow
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewStyle } from 'react-native';
import { TINTED_VIEW_DEFAULT } from './styles';
@@ -17,24 +15,24 @@ const BASE_STYLE = {
/**
* {@code TintedView}'s React {@code Component} prop types.
*/
type Props = {
interface IProps {
/**
* The children components of this component.
*/
children?: React$Node,
children?: React.ReactNode;
/**
* Style to override the base style.
*/
style: Object
};
style?: Object;
}
/**
* Implements a component aimed at covering another view and tinting it with
* the given color and opacity.
*/
export default class TintedView extends Component<Props> {
export default class TintedView extends Component<IProps> {
/**
* Implements React's {@link Component#render()}.
*
@@ -48,7 +46,7 @@ export default class TintedView extends Component<Props> {
return (
<View
pointerEvents = 'box-none'
style = { BASE_STYLE }>
style = { BASE_STYLE as ViewStyle }>
<View
pointerEvents = 'none'
style = { [
@@ -58,7 +56,7 @@ export default class TintedView extends Component<Props> {
] } />
<View
pointerEvents = 'box-none'
style = { BASE_STYLE }>
style = { BASE_STYLE as ViewStyle }>
{ children }
</View>
</View>

View File

@@ -1,9 +1,8 @@
// @flex
import { StyleSheet } from 'react-native';
import { ColorSchemeRegistry, schemeColor } from '../../../color-scheme';
import { BoxModel } from '../../../styles';
import ColorSchemeRegistry from '../../../color-scheme/ColorSchemeRegistry';
import { schemeColor } from '../../../color-scheme/functions';
import { BoxModel } from '../../../styles/components/styles/BoxModel';
const HEADER_FONT_SIZE = 18;
const HEADER_HEIGHT = 48;

View File

@@ -1,5 +1,3 @@
// @flow
import { ColorPalette } from '../../../styles/components/styles/ColorPalette';
export default {

View File

@@ -163,7 +163,7 @@ class MultiSelectAutocomplete extends Component<IProps, IState> {
const autoFocus = this.props.shouldFocus || false;
const disabled = this.props.isDisabled || false;
const placeholder = this.props.placeholder || '';
const noMatchesFound = this.props.noMatchesFound || '';
const noMatchesFound = this.state.loading ? this.props.loadingMessage : this.props.noMatchesFound || '';
const errorDialog = this._renderError();
return (
@@ -200,7 +200,7 @@ class MultiSelectAutocomplete extends Component<IProps, IState> {
error: this.state.error && Boolean(filterValue),
filterValue,
isOpen: Boolean(this.state.items.length) && Boolean(filterValue),
items: filterValue ? this.state.items : [],
items: [],
loading: Boolean(filterValue)
});
if (filterValue) {

View File

@@ -46,7 +46,7 @@ const _WELL_KNOWN_NUMBER_PROPERTIES = [ 'height', 'width' ];
* @param {Styletype} st - The complex style type.
* @returns {Object}
*/
export function styleTypeToObject(st: StyleType) {
export function styleTypeToObject(st: StyleType | StyleType[]) {
if (!st) {
return {};
}

View File

@@ -9,7 +9,7 @@ export * from './functions.any';
* @param {StyleType} style - The passed style prop to the component.
* @returns {StyleType}
*/
export function getFixedPlatformStyle(style?: StyleType): StyleType {
export function getFixedPlatformStyle(style?: StyleType | StyleType[]) {
// There is nothing to do on mobile - yet.
return style ?? {};

View File

@@ -9,7 +9,7 @@ export * from './functions.any';
* @param {StyleType} style - The passed style prop to the component.
* @returns {StyleType}
*/
export function getFixedPlatformStyle(style?: StyleType) {
export function getFixedPlatformStyle(style?: StyleType | StyleType[]) {
if (Array.isArray(style)) {
const _style = {};

View File

@@ -1,5 +1,3 @@
/* @flow */
import { SET_CONNECTION_STATE } from './actionTypes';
// eslint-disable-next-line jsdoc/require-description-complete-sentence

View File

@@ -1,3 +1,5 @@
import { GestureResponderEvent } from 'react-native';
import { IReduxState } from '../../../app/types';
import { isTestModeEnabled } from '../functions';
@@ -28,7 +30,7 @@ export type TestHintProps = {
* The optional "on press" handler which can be used to bind a click handler
* to a {@link TestHint}.
*/
onPress?: Function;
onPress?: (e?: GestureResponderEvent) => void;
/**
* The test hint's (text) value which is to be consumed by the tests.

View File

@@ -1,5 +1,3 @@
// @flow
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';

View File

@@ -1,11 +1,8 @@
/* @flow */
import React, { Component } from 'react';
import { Text } from 'react-native';
import { connect } from 'react-redux';
import type { TestHintProps } from './AbstractTestHint';
import { _mapStateToProps } from './AbstractTestHint';
import { TestHintProps, _mapStateToProps } from './AbstractTestHint';
/**
* The Android version of <code>TestHint</code>. It will put the identifier,

View File

@@ -1,11 +1,8 @@
/* @flow */
import React, { Component } from 'react';
import { Text } from 'react-native';
import { connect } from 'react-redux';
import type { TestHintProps } from './AbstractTestHint';
import { _mapStateToProps } from './AbstractTestHint';
import { TestHintProps, _mapStateToProps } from './AbstractTestHint';
/**
* This is the iOS version of the TestHint.

View File

@@ -1,16 +1,14 @@
// @flow
import * as React from 'react';
import { Provider as PaperProvider } from 'react-native-paper';
import BaseTheme from './BaseTheme.native';
type Props = {
interface IProps {
/**
/**
* The children of the component.
*/
children: React.ChildrenArray<any>
children: React.ReactNode;
}
/**
@@ -19,6 +17,6 @@ type Props = {
* @param {Object} props - The props of the component.
* @returns {React.ReactNode}
*/
export default function JitsiThemePaperProvider(props: Props) {
export default function JitsiThemePaperProvider(props: IProps) {
return <PaperProvider theme = { BaseTheme }>{ props.children }</PaperProvider>;
}

View File

@@ -24,6 +24,8 @@ interface IProps {
selectedItems?: MultiSelectItem[];
}
const MULTI_SELECT_HEIGHT = 200;
const useStyles = makeStyles()(theme => {
return {
container: {
@@ -41,7 +43,7 @@ const useStyles = makeStyles()(theme => {
borderRadius: `${Number(theme.shape.borderRadius)}px`,
...withPixelLineHeight(theme.typography.bodyShortRegular),
zIndex: 2,
maxHeight: '400px',
maxHeight: `${MULTI_SELECT_HEIGHT}px`,
overflowY: 'auto',
padding: '0'
},
@@ -128,7 +130,7 @@ const MultiSelect = ({
</div>
</div>
))
: <div>{noMatchesText}</div>
: <div className = { classes.listItem }>{noMatchesText}</div>
}
</div>
), [ items ]);

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of (redux) action to reset the breakout rooms data.
*/

View File

@@ -1,5 +1,3 @@
// @flow
/**
* Resets the state of calendar integration so stored events and selected
* calendar type are cleared.

View File

@@ -1,5 +1,3 @@
// @flow
import { Component } from 'react';
/**
@@ -7,7 +5,7 @@ import { Component } from 'react';
*
* @augments Component
*/
class AddMeetingUrlButton extends Component<*> {
class AddMeetingUrlButton extends Component<void> {
/**
* Implements React's {@link Component#render}.
*

View File

@@ -1,5 +1,3 @@
// @flow
import { Component } from 'react';
/**
@@ -7,7 +5,7 @@ import { Component } from 'react';
*
* @augments Component
*/
class JoinButton extends Component<*> {
class JoinButton extends Component<void> {
/**
* Implements React's {@link Component#render}.
*

View File

@@ -1,35 +1,35 @@
// @flow
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { IStore } from '../../app/types';
import ConfirmDialog from '../../base/dialog/components/native/ConfirmDialog';
import { translate } from '../../base/i18n/functions';
import { updateCalendarEvent } from '../actions';
type Props = {
interface IProps extends WithTranslation {
/**
* The Redux dispatch function.
*/
dispatch: Function,
dispatch: IStore['dispatch'];
/**
* The ID of the event to be updated.
*/
eventId: string
};
eventId: string;
}
/**
* Component for the add Jitsi link confirm dialog.
*/
class UpdateCalendarEventDialog extends Component<Props> {
class UpdateCalendarEventDialog extends Component<IProps> {
/**
* Initializes a new {@code UpdateCalendarEventDialog} instance.
*
* @inheritdoc
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
this._onSubmit = this._onSubmit.bind(this);
@@ -49,8 +49,6 @@ class UpdateCalendarEventDialog extends Component<Props> {
);
}
_onSubmit: () => boolean;
/**
* Callback for the confirm button.
*
@@ -58,7 +56,7 @@ class UpdateCalendarEventDialog extends Component<Props> {
* @returns {boolean} - True (to note that the modal should be closed).
*/
_onSubmit() {
this.props.dispatch(updateCalendarEvent(this.props.eventId, ''));
this.props.dispatch(updateCalendarEvent(this.props.eventId));
return true;
}

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The type of the action which signals to add a new chat message.
*

View File

@@ -1,5 +1,6 @@
/* eslint-disable lines-around-comment, max-len */
import { IParticipant } from '../base/participants/types';
import { navigate }
// @ts-ignore
from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
@@ -21,7 +22,7 @@ export * from './actions.any';
* type: OPEN_CHAT
* }}
*/
export function openChat(participant: Object, disablePolls?: boolean) {
export function openChat(participant: IParticipant | undefined | Object, disablePolls?: boolean) {
if (disablePolls) {
navigate(screen.conference.chat);
}

View File

@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { Platform } from 'react-native';
import { WithTranslation } from 'react-i18next';
import { Platform, ViewStyle } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { translate } from '../../../base/i18n/functions';
@@ -10,47 +11,42 @@ import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import styles from './styles';
type Props = {
interface IProps extends WithTranslation {
/**
* Callback to invoke on message send.
*/
onSend: Function,
onSend: Function;
}
/**
* Function to be used to translate i18n labels.
*/
t: Function
};
type State = {
interface IState {
/**
* Boolean to show if an extra padding needs to be added to the bar.
*/
addPadding: boolean,
addPadding: boolean;
/**
* The value of the input field.
*/
message: string,
message: string;
/**
* Boolean to show or hide the send button.
*/
showSend: boolean
};
showSend: boolean;
}
/**
* Implements the chat input bar with text field and action(s).
*/
class ChatInputBar extends Component<Props, State> {
class ChatInputBar extends Component<IProps, IState> {
/**
* Instantiates a new instance of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
this.state = {
@@ -76,7 +72,7 @@ class ChatInputBar extends Component<Props, State> {
style = { [
styles.inputBar,
this.state.addPadding ? styles.extraBarPadding : null
] }>
] as ViewStyle[] }>
<Input
blurOnSubmit = { false }
customStyles = {{ container: styles.customInputContainer }}
@@ -98,30 +94,26 @@ class ChatInputBar extends Component<Props, State> {
);
}
_onChangeText: string => void;
/**
* Callback to handle the change of the value of the text field.
*
* @param {string} text - The current value of the field.
* @returns {void}
*/
_onChangeText(text) {
_onChangeText(text: string) {
this.setState({
message: text,
showSend: Boolean(text)
});
}
_onFocused: boolean => Function;
/**
* Constructs a callback to be used to update the padding of the field if necessary.
*
* @param {boolean} focused - True of the field is focused.
* @returns {Function}
*/
_onFocused(focused) {
_onFocused(focused: boolean) {
return () => {
Platform.OS === 'android' && this.setState({
addPadding: focused
@@ -129,8 +121,6 @@ class ChatInputBar extends Component<Props, State> {
};
}
_onSubmit: () => void;
/**
* Callback to handle the submit event of the text field.
*

View File

@@ -1,14 +1,15 @@
import React from 'react';
import { Text, View } from 'react-native';
import { Text, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
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';
import { isGifMessage } from '../../../gifs/functions.native';
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
import { replaceNonUnicodeEmojis } from '../../functions';
import AbstractChatMessage, { type Props } from '../AbstractChatMessage';
import AbstractChatMessage, { IProps } from '../AbstractChatMessage';
import GifMessage from './GifMessage';
import PrivateMessageButton from './PrivateMessageButton';
@@ -18,7 +19,7 @@ import styles from './styles';
/**
* Renders a single chat message.
*/
class ChatMessage extends AbstractChatMessage<Props> {
class ChatMessage extends AbstractChatMessage<IProps> {
/**
* Implements {@code Component#render}.
*
@@ -31,18 +32,18 @@ class ChatMessage extends AbstractChatMessage<Props> {
// Style arrays that need to be updated in various scenarios, such as
// error messages or others.
const detailsWrapperStyle = [
styles.detailsWrapper
const detailsWrapperStyle: ViewStyle[] = [
styles.detailsWrapper as ViewStyle
];
const messageBubbleStyle = [
styles.messageBubble
const messageBubbleStyle: ViewStyle[] = [
styles.messageBubble as ViewStyle
];
if (localMessage) {
// This is a message sent by the local participant.
// The wrapper needs to be aligned to the right.
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper);
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper as ViewStyle);
// The bubble needs some additional styling
messageBubbleStyle.push(styles.localMessageBubble);
@@ -69,11 +70,11 @@ class ChatMessage extends AbstractChatMessage<Props> {
const messageText = replaceNonUnicodeEmojis(this._getMessageText());
return (
<View style = { styles.messageWrapper } >
<View style = { styles.messageWrapper as ViewStyle } >
{ this._renderAvatar() }
<View style = { detailsWrapperStyle }>
<View style = { messageBubbleStyle }>
<View style = { styles.textWrapper } >
<View style = { styles.textWrapper as ViewStyle } >
{ this._renderDisplayName() }
{ isGifMessage(messageText)
? <GifMessage message = { messageText } />
@@ -94,12 +95,6 @@ class ChatMessage extends AbstractChatMessage<Props> {
);
}
_getFormattedTimestamp: () => string;
_getMessageText: () => string;
_getPrivateNoticeMessage: () => string;
/**
* Renders the avatar of the sender.
*
@@ -171,7 +166,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
}
return (
<View style = { styles.replyContainer }>
<View style = { styles.replyContainer as ViewStyle }>
<PrivateMessageButton
isLobbyMessage = { lobbyChat }
participantID = { message.id }
@@ -204,9 +199,9 @@ class ChatMessage extends AbstractChatMessage<Props> {
* Maps part of the redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
* @returns {IProps}
*/
function _mapStateToProps(state) {
function _mapStateToProps(state: IReduxState) {
return {
knocking: state['features/lobby'].knocking
};

View File

@@ -1,28 +1,29 @@
import React, { Component, ReactElement } from 'react';
import React, { Component } from 'react';
import { FlatList } from 'react-native';
import { MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from '../../constants';
import { IMessage } from '../../reducer';
import ChatMessage from './ChatMessage';
type Props = {
interface IProps {
/**
* The messages array to render.
*/
messages: Array<Object>
/**
* The messages array to render.
*/
messages: Array<IMessage>;
}
/**
* Implements a container to render all the chat messages in a conference.
*/
export default class ChatMessageGroup extends Component<Props> {
export default class ChatMessageGroup extends Component<IProps> {
/**
* Instantiates a new instance of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
this._keyExtractor = this._keyExtractor.bind(this);
@@ -44,29 +45,25 @@ export default class ChatMessageGroup extends Component<Props> {
);
}
_keyExtractor: Object => string;
/**
* Key extractor for the flatlist.
*
* @param {Object} item - The flatlist item that we need the key to be
* @param {Object} _item - The flatlist item that we need the key to be
* generated for.
* @param {number} index - The index of the element.
* @returns {string}
*/
_keyExtractor(item, index) {
_keyExtractor(_item: Object, index: number) {
return `key_${index}`;
}
_renderMessage: Object => ReactElement;
/**
* Renders a single chat message.
*
* @param {Object} message - The chat message to render.
* @returns {React$Element<*>}
*/
_renderMessage({ index, item: message }) {
_renderMessage({ index, item: message }: { index: number; item: IMessage; }) {
return (
<ChatMessage
message = { message }

View File

@@ -1,26 +1,26 @@
import React from 'react';
import { Image, View } from 'react-native';
import { Image, ImageStyle, View } from 'react-native';
import { GIF_PREFIX } from '../../../gifs/constants';
import styles from './styles';
type Props = {
interface IProps {
/**
* The formatted gif message.
*/
message: string
message: string;
}
const GifMessage = ({ message }: Props) => {
const GifMessage = ({ message }: IProps) => {
const url = message.substring(GIF_PREFIX.length, message.length - 1);
return (<View
style = { styles.gifContainer }>
<Image
source = {{ uri: url }}
style = { styles.gifImage } />
style = { styles.gifImage as ImageStyle } />
</View>);
};

View File

@@ -1,32 +1,32 @@
import React, { ReactElement } from 'react';
import { FlatList, Text, View } from 'react-native';
import React from 'react';
import { FlatList, Text, TextStyle, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
import { translate } from '../../../base/i18n/functions';
import AbstractMessageContainer, { type Props as AbstractProps }
from '../AbstractMessageContainer';
import { IMessage } from '../../reducer';
import AbstractMessageContainer, { IProps as AbstractProps } from '../AbstractMessageContainer';
import ChatMessageGroup from './ChatMessageGroup';
import styles from './styles';
type Props = AbstractProps & {
interface IProps extends AbstractProps {
/**
* Function to be used to translate i18n labels.
*/
t: Function
};
t: Function;
}
/**
* Implements a container to render all the chat messages in a conference.
*/
class MessageContainer extends AbstractMessageContainer<Props> {
class MessageContainer extends AbstractMessageContainer<IProps, any> {
/**
* Instantiates a new instance of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
this._keyExtractor = this._keyExtractor.bind(this);
@@ -57,24 +57,18 @@ class MessageContainer extends AbstractMessageContainer<Props> {
);
}
_getMessagesGroupedBySender: () => Array<Array<Object>>;
_keyExtractor: Object => string;
/**
* Key extractor for the flatlist.
*
* @param {Object} item - The flatlist item that we need the key to be
* @param {Object} _item - The flatlist item that we need the key to be
* generated for.
* @param {number} index - The index of the element.
* @returns {string}
*/
_keyExtractor(item, index) {
_keyExtractor(_item: Object, index: number) {
return `key_${index}`;
}
_renderListEmptyComponent: () => ReactElement;
/**
* Renders a message when there are no messages in the chat yet.
*
@@ -84,23 +78,21 @@ class MessageContainer extends AbstractMessageContainer<Props> {
const { t } = this.props;
return (
<View style = { styles.emptyComponentWrapper }>
<Text style = { styles.emptyComponentText }>
<View style = { styles.emptyComponentWrapper as ViewStyle }>
<Text style = { styles.emptyComponentText as TextStyle }>
{ t('chat.noMessagesMessage') }
</Text>
</View>
);
}
_renderMessageGroup: Object => ReactElement;
/**
* Renders a single chat message.
*
* @param {Array<Object>} messages - The chat message to render.
* @returns {React$Element<*>}
*/
_renderMessageGroup({ item: messages }) {
_renderMessageGroup({ item: messages }: { item: IMessage[]; }) {
return <ChatMessageGroup messages = { messages } />;
}
}

View File

@@ -1,57 +1,49 @@
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { CHAT_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { translate } from '../../../base/i18n/functions';
import { IconMessage, IconReply } from '../../../base/icons/svg';
import { getParticipantById } from '../../../base/participants/functions';
import { IParticipant } from '../../../base/participants/types';
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import { handleLobbyChatInitialized, openChat } from '../../../chat/actions.native';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
import { handleLobbyChatInitialized, openChat } from '../../actions.native';
export type Props = AbstractButtonProps & {
/**
* The Redux Dispatch function.
*/
dispatch: Function,
/**
* The ID of the participant that the message is to be sent.
*/
participantID: string,
/**
* True if the button is rendered as a reply button.
*/
reply: boolean,
/**
* Function to be used to translate i18n labels.
*/
t: Function,
/**
* True if the polls feature is disabled.
*/
_isPollsDisabled: boolean,
export interface IProps extends AbstractButtonProps {
/**
* True if message is a lobby chat message.
*/
_isLobbyMessage: boolean,
_isLobbyMessage: boolean;
/**
* True if the polls feature is disabled.
*/
_isPollsDisabled?: boolean;
/**
* The participant object retrieved from Redux.
*/
_participant: Object,
};
_participant?: IParticipant;
/**
* The ID of the participant that the message is to be sent.
*/
participantID: string;
/**
* True if the button is rendered as a reply button.
*/
reply: boolean;
}
/**
* Class to render a button that initiates the sending of a private message through chet.
* Class to render a button that initiates the sending of a private message through chat.
*/
class PrivateMessageButton extends AbstractButton<Props, any> {
class PrivateMessageButton extends AbstractButton<IProps, any> {
accessibilityLabel = 'toolbar.accessibilityLabel.privateMessage';
icon = IconMessage;
label = 'toolbar.privateMessage';
@@ -99,10 +91,10 @@ class PrivateMessageButton extends AbstractButton<Props, any> {
* Maps part of the Redux store to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {Props} ownProps - The own props of the component.
* @returns {Props}
* @param {IProps} ownProps - The own props of the component.
* @returns {IProps}
*/
export function _mapStateToProps(state: Object, ownProps: Props) {
export function _mapStateToProps(state: IReduxState, ownProps: any) {
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
const { disablePolls } = state['features/base/config'];
const { visible = enabled, isLobbyMessage, participantID } = ownProps;

View File

@@ -90,6 +90,8 @@ class ChatInput extends Component<IProps, IState> {
if (isMobileBrowser()) {
// Ensure textarea is not focused when opening chat on mobile browser.
this._textArea?.current && this._textArea.current.blur();
} else {
this._focus();
}
}

View File

@@ -29,9 +29,9 @@ const styles = (theme: Theme) => {
chatMessage: {
display: 'inline-flex',
padding: '12px',
marginRight: '12px',
backgroundColor: theme.palette.ui02,
borderRadius: '4px 12px 12px 12px',
boxSizing: 'border-box' as const,
maxWidth: '100%',
marginTop: '4px',

View File

@@ -1,5 +1,3 @@
// @flow
/**
* The size of the chat.
*/

View File

@@ -0,0 +1,12 @@
import { IFRAME_EMBED_ALLOWED_LOCATIONS as ADDITIONAL_LOCATIONS } from './extraConstants';
/**
* Timeout of the conference when iframe is disabled in minutes.
*/
export const IFRAME_DISABLED_TIMEOUT_MINUTES = 5;
/**
* A list of allowed location to embed iframe.
*/
/* eslint-disable-next-line no-extra-parens*/
export const IFRAME_EMBED_ALLOWED_LOCATIONS = ([] as string[]).concat(ADDITIONAL_LOCATIONS);

View File

@@ -0,0 +1,5 @@
/**
* Deploy-specific configuration constants.
*/
export const IFRAME_EMBED_ALLOWED_LOCATIONS = [];

View File

@@ -1,8 +1,11 @@
import i18n from 'i18next';
import { batch } from 'react-redux';
// @ts-expect-error
import { API_ID } from '../../../modules/API/constants';
import { appNavigate } from '../app/actions';
import { IStore } from '../app/types';
import { redirectToStaticPage } from '../app/actions.any';
import { IReduxState, IStore } from '../app/types';
import {
CONFERENCE_FAILED,
CONFERENCE_JOINED,
@@ -15,17 +18,22 @@ import { getURLWithoutParamsNormalized } from '../base/connection/utils';
import { hideDialog } from '../base/dialog/actions';
import { isDialogOpen } from '../base/dialog/functions';
import { getLocalizedDateFormatter } from '../base/i18n/dateUtil';
import { translateToHTML } from '../base/i18n/functions';
import i18next from '../base/i18n/i18next';
import { browser } from '../base/lib-jitsi-meet';
import { pinParticipant } from '../base/participants/actions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
import { SET_REDUCED_UI } from '../base/responsive-ui/actionTypes';
import { BUTTON_TYPES } from '../base/ui/constants.any';
import { inIframe } from '../base/util/iframeUtils';
import { isCalendarEnabled } from '../calendar-sync/functions';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import FeedbackDialog from '../feedback/components/FeedbackDialog';
import { setFilmstripEnabled } from '../filmstrip/actions.any';
import { hideNotification, showNotification } from '../notifications/actions';
import { isVpaasMeeting } from '../jaas/functions';
import { hideNotification, showNotification, showWarningNotification } from '../notifications/actions';
import {
CALENDAR_NOTIFICATION_ID,
NOTIFICATION_ICON,
@@ -36,6 +44,7 @@ import { setToolboxEnabled } from '../toolbox/actions.any';
import { DISMISS_CALENDAR_NOTIFICATION } from './actionTypes';
import { dismissCalendarNotification, notifyKickedOut } from './actions';
import { IFRAME_DISABLED_TIMEOUT_MINUTES, IFRAME_EMBED_ALLOWED_LOCATIONS } from './constants';
let intervalID: any;
@@ -155,6 +164,49 @@ function _conferenceJoined({ dispatch, getState }: IStore) {
}
dispatch(showSalesforceNotification());
_checkIframe(getState(), dispatch);
}
/**
* Additional checks for embedding in iframe.
*
* @param {IReduxState} state - The current state of the app.
* @param {Function} dispatch - The Redux dispatch function.
* @private
* @returns {void}
*/
function _checkIframe(state: IReduxState, dispatch: IStore['dispatch']) {
let allowIframe = false;
if (document.referrer === '') {
// no iframe
allowIframe = true;
} else {
try {
allowIframe = IFRAME_EMBED_ALLOWED_LOCATIONS.includes(new URL(document.referrer).hostname);
} catch (e) {
// wrong URL in referrer
}
}
if (inIframe() && state['features/base/config'].disableIframeAPI && !browser.isElectron()
&& !isVpaasMeeting(state) && !allowIframe) {
// show sticky notification and redirect in 5 minutes
dispatch(showWarningNotification({
description: translateToHTML(
i18next.t.bind(i18next),
'notify.disabledIframe',
{
timeout: IFRAME_DISABLED_TIMEOUT_MINUTES
}
)
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
setTimeout(() => {
// redirect to the promotional page
dispatch(redirectToStaticPage('static/close3.html', `#jitsi_meet_external_api_id=${API_ID}`));
}, IFRAME_DISABLED_TIMEOUT_MINUTES * 60 * 1000);
}
}
/**

View File

@@ -1,5 +1,3 @@
/* eslint-disable lines-around-comment */
import React from 'react';
import { StyleProp, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
@@ -12,12 +10,10 @@ import {
getParticipantById,
isScreenShareParticipant
} from '../../../base/participants/functions';
// @ts-ignore
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
import {
getTrackByMediaTypeAndParticipant
} from '../../../base/tracks/functions.native';
// @ts-ignore
import indicatorStyles from '../../../filmstrip/components/native/styles';
import {
isTrackStreamingStatusInactive,
@@ -50,12 +46,12 @@ type IProps = AbstractProps & {
/**
* Whether the connection is inactive or not.
*/
_isConnectionStatusInactive: boolean;
_isConnectionStatusInactive?: boolean;
/**
* Whether the connection is interrupted or not.
*/
_isConnectionStatusInterrupted: boolean;
_isConnectionStatusInterrupted?: boolean;
/**
* Whether the current participant is a virtual screenshare.
@@ -70,7 +66,7 @@ type IProps = AbstractProps & {
/**
* Icon style override.
*/
iconStyle: any;
iconStyle?: any;
};
type IState = {
@@ -127,12 +123,10 @@ class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
_isVirtualScreenshareParticipant,
_isConnectionStatusInactive,
_isConnectionStatusInterrupted
// @ts-ignore
} = this.props;
const {
showIndicator,
stats
// @ts-ignore
} = this.state;
const { percent } = stats;
@@ -167,10 +161,8 @@ class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
indicatorStyles.indicatorContainer as StyleProp<ViewStyle>,
{ backgroundColor: indicatorColor }
] }>
{/* @ts-ignore */}
<BaseIndicator
icon = { IconConnection }
// @ts-ignore
iconStyle = { this.props.iconStyle || iconStyle } />
</View>
);
@@ -185,7 +177,7 @@ class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
* @param {IProps} ownProps - The own props of the component.
* @returns {IProps}
*/
export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
export function _mapStateToProps(state: IReduxState, ownProps: any) {
const { participantId } = ownProps;
const tracks = state['features/base/tracks'];
const participant = participantId ? getParticipantById(state, participantId) : getLocalParticipant(state);
@@ -212,5 +204,4 @@ export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
};
}
// @ts-ignore
export default connect(_mapStateToProps)(ConnectionIndicator);

View File

@@ -1,5 +1,3 @@
/* @flow */
/**
* The type of the action which signals to open the conference in the desktop
* app.

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