mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-11 09:10:20 +00:00
Compare commits
49 Commits
7196
...
android-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2521bc67a | ||
|
|
38a293f8f6 | ||
|
|
13e8f992b5 | ||
|
|
c384d0d3a9 | ||
|
|
2b71fa512b | ||
|
|
d381ceb040 | ||
|
|
e18c428f52 | ||
|
|
9fc32dc59b | ||
|
|
6f45622ef1 | ||
|
|
e56c7070c2 | ||
|
|
0aef7a36aa | ||
|
|
c030cf941e | ||
|
|
f6760e4ac7 | ||
|
|
9060c77307 | ||
|
|
a1d018eef4 | ||
|
|
3f724d8fb7 | ||
|
|
49d69a5a02 | ||
|
|
6db9e42876 | ||
|
|
ad3e8f9f53 | ||
|
|
9f39caa247 | ||
|
|
1402a63324 | ||
|
|
a9863e65c3 | ||
|
|
646c58f7d1 | ||
|
|
a78ea7ca9c | ||
|
|
8b8565bf60 | ||
|
|
b9e30f3c1b | ||
|
|
96b6edccf8 | ||
|
|
2af9dc88e6 | ||
|
|
eda25ca3c9 | ||
|
|
c99da17973 | ||
|
|
9e147d7842 | ||
|
|
d7afaf871f | ||
|
|
c4f6d37aa1 | ||
|
|
a5663872d9 | ||
|
|
007283aab3 | ||
|
|
3b612376f2 | ||
|
|
1a312e2140 | ||
|
|
6f5d0400b8 | ||
|
|
aec86cecc0 | ||
|
|
bf1dde7cd1 | ||
|
|
91e9005f08 | ||
|
|
57f9ea2865 | ||
|
|
1f6425fbfd | ||
|
|
e169979bab | ||
|
|
6e9e9c9a6a | ||
|
|
102a369bca | ||
|
|
b318b987a7 | ||
|
|
78ce68160a | ||
|
|
6aff616af4 |
3
.npmrc
3
.npmrc
@@ -1,6 +1,3 @@
|
||||
package-lock=true
|
||||
; FIXME Set legacy-peer-deps=false when we upgrade RN.
|
||||
legacy-peer-deps=true
|
||||
; Omit optional dependencies
|
||||
omit=optional
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
compileSdkVersion = 32
|
||||
minSdkVersion = 23
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 32
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
2
app.js
@@ -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
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
@@ -162,6 +163,7 @@ import { endpointMessageReceived } from './react/features/subtitles/actions.any'
|
||||
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
|
||||
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||
import { setIAmVisitor } from './react/features/visitors/actions';
|
||||
import { iAmVisitor } from './react/features/visitors/functions';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
@@ -804,13 +806,14 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async init({ roomName }) {
|
||||
const state = APP.store.getState();
|
||||
const initialOptions = {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: getStartWithAudioMuted(APP.store.getState())
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|
||||
startWithVideoMuted: getStartWithVideoMuted(APP.store.getState())
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|
||||
startWithAudioMuted: getStartWithAudioMuted(state)
|
||||
|| isUserInteractionRequiredForUnmute(state),
|
||||
startWithVideoMuted: getStartWithVideoMuted(state)
|
||||
|| isUserInteractionRequiredForUnmute(state)
|
||||
};
|
||||
|
||||
this.roomName = roomName;
|
||||
@@ -840,7 +843,7 @@ export default {
|
||||
return tracks;
|
||||
};
|
||||
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
_connectionPromise = connect(roomName).then(c => {
|
||||
// we want to initialize it early, in case of errors to be able
|
||||
// to gather logs
|
||||
@@ -863,7 +866,7 @@ export default {
|
||||
// they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
return APP.store.dispatch(initPrejoin(tracks, errors));
|
||||
}
|
||||
|
||||
@@ -873,7 +876,7 @@ export default {
|
||||
|
||||
let localTracks = handleStartAudioMuted(initialOptions, tracks);
|
||||
|
||||
// in case where gum is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
|
||||
// In case where gUM is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
|
||||
// join unmuted even though jicofo had instruct us to mute, so let's respect that before passing the tracks
|
||||
if (!browser.isWebKitBased()) {
|
||||
if (room?.isStartAudioMuted()) {
|
||||
@@ -885,6 +888,11 @@ export default {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
// Do not add the tracks if the user has joined the call as a visitor.
|
||||
if (iAmVisitor(state)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._setLocalAudioVideoStreams(localTracks);
|
||||
}
|
||||
|
||||
@@ -2301,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'];
|
||||
|
||||
@@ -1406,6 +1406,7 @@ var config = {
|
||||
disableAGC
|
||||
disableAP
|
||||
disableHPF
|
||||
disableLocalStats
|
||||
disableNS
|
||||
enableTalkWhileMuted
|
||||
forceJVB121Ratio
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
#largeVideoContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#largeVideoWrapper {
|
||||
|
||||
@@ -60,7 +60,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'filmstrip/vertical_filmstrip';
|
||||
@import 'filmstrip/vertical_filmstrip_overrides';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'modals/invite/add-people';
|
||||
@import 'deep-linking/main';
|
||||
@import 'transcription-subtitles';
|
||||
@import '_meetings_list.scss';
|
||||
|
||||
@@ -12,24 +12,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styling inline dialog errors.
|
||||
*/
|
||||
.inline-dialog-error {
|
||||
margin-top: 16px;
|
||||
|
||||
&-text {
|
||||
color: $dialogErrorText;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: block;
|
||||
margin: 16px auto 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styling shared video dialog errors.
|
||||
*/
|
||||
|
||||
@@ -41,10 +41,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles errors in the MultiSelectAutocomplete.
|
||||
*/
|
||||
.autocomplete-error {
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
line-height: 24px;
|
||||
border-collapse: collapse;
|
||||
|
||||
* {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
thead {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -1,57 +1,4 @@
|
||||
.invite-more {
|
||||
&-container {
|
||||
margin-bottom: 8px;
|
||||
transition: margin-bottom 0.3s;
|
||||
|
||||
&.elevated {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-header {
|
||||
max-width: 100%;
|
||||
margin-bottom: 16px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 16px;
|
||||
background: #0376DA;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: #278ADF;
|
||||
}
|
||||
}
|
||||
|
||||
&-text {
|
||||
margin-left: 8px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
&-dialog {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
@@ -65,59 +12,6 @@
|
||||
background: #5E6D7A;
|
||||
}
|
||||
|
||||
&.email-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 24px;
|
||||
width: calc(100% - 26px);
|
||||
height: 22px;
|
||||
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.invite-buttons {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
margin-top: 8px;
|
||||
|
||||
& > a {
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
min-width: 48px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-cancel {
|
||||
margin-right: 16px;
|
||||
padding: 7px 15px;
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
}
|
||||
|
||||
&-add {
|
||||
padding: 8px 16px;
|
||||
background: #0376DA;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
& > a {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.stream {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -158,14 +52,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser {
|
||||
.invite-more-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.invite-more-button {
|
||||
height: 48px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
6
globals.d.ts
vendored
@@ -10,12 +10,6 @@ declare global {
|
||||
API: any;
|
||||
conference: any;
|
||||
debugLogs: any;
|
||||
keyboardshortcut: {
|
||||
registerShortcut: Function;
|
||||
unregisterShortcut: Function;
|
||||
openDialog: Function;
|
||||
enable: Function;
|
||||
}
|
||||
};
|
||||
const interfaceConfig: any;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
1
modules/API/external/external_api.js
vendored
1
modules/API/external/external_api.js
vendored
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
2964
package-lock.json
generated
2964
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@@ -16,14 +16,10 @@
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"@amplitude/react-native": "2.7.0",
|
||||
"@atlaskit/inline-dialog": "13.0.9",
|
||||
"@atlaskit/inline-message": "11.0.8",
|
||||
"@atlaskit/multi-select": "15.0.5",
|
||||
"@atlaskit/theme": "11.0.2",
|
||||
"@emotion/react": "11.10.0",
|
||||
"@emotion/styled": "11.10.0",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
"@giphy/js-fetch-api": "4.7.1",
|
||||
"@giphy/react-components": "5.6.0",
|
||||
"@giphy/react-components": "6.8.1",
|
||||
"@giphy/react-native-sdk": "1.7.0",
|
||||
"@hapi/bourne": "2.0.0",
|
||||
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.12/jitsi-excalidraw-0.0.12.tgz",
|
||||
@@ -33,8 +29,8 @@
|
||||
"@jitsi/rtcstats": "9.5.1",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
"@mui/material": "5.10.2",
|
||||
"@mui/styles": "5.10.2",
|
||||
"@mui/material": "5.12.1",
|
||||
"@mui/styles": "5.12.0",
|
||||
"@react-native-async-storage/async-storage": "1.17.3",
|
||||
"@react-native-community/clipboard": "1.5.1",
|
||||
"@react-native-community/netinfo": "7.1.7",
|
||||
@@ -48,10 +44,6 @@
|
||||
"@svgr/webpack": "6.3.1",
|
||||
"@tensorflow/tfjs-backend-wasm": "3.13.0",
|
||||
"@tensorflow/tfjs-core": "3.13.0",
|
||||
"@types/amplitude-js": "8.16.2",
|
||||
"@types/audioworklet": "0.0.29",
|
||||
"@types/w3c-image-capture": "1.0.6",
|
||||
"@types/w3c-web-hid": "1.0.3",
|
||||
"@vladmandic/human": "2.6.5",
|
||||
"@vladmandic/human-models": "2.5.9",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
@@ -73,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/v1619.0.0+c8d76d4a/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",
|
||||
@@ -123,7 +115,6 @@
|
||||
"redux-thunk": "2.4.1",
|
||||
"resemblejs": "4.0.0",
|
||||
"seamless-scroll-polyfill": "2.1.8",
|
||||
"styled-components": "3.4.9",
|
||||
"tss-react": "4.4.4",
|
||||
"util": "0.12.1",
|
||||
"uuid": "8.3.2",
|
||||
@@ -140,17 +131,23 @@
|
||||
"@babel/preset-flow": "7.16.0",
|
||||
"@babel/preset-react": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.1.5",
|
||||
"@types/amplitude-js": "8.16.2",
|
||||
"@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",
|
||||
"@types/unorm": "1.3.28",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/w3c-image-capture": "1.0.6",
|
||||
"@types/w3c-web-hid": "1.0.3",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||
"@typescript-eslint/parser": "5.30.4",
|
||||
@@ -181,6 +178,11 @@
|
||||
"webpack-cli": "4.9.0",
|
||||
"webpack-dev-server": "4.7.3"
|
||||
},
|
||||
"overrides": {
|
||||
"strophe.js@1.6.0": {
|
||||
"@xmldom/xmldom": "0.8.7"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
@@ -197,6 +199,8 @@
|
||||
"lint-fix": "eslint --ext .js,.ts,.tsx --max-warnings 0 --fix .",
|
||||
"postinstall": "patch-package --error-on-fail && jetify",
|
||||
"validate": "npm ls",
|
||||
"tsc-test:web": "tsc --project tsconfig.web.json --listFilesOnly | grep -v node_modules | grep native",
|
||||
"tsc-test:native": "tsc --project tsconfig.native.json --listFilesOnly | grep -v node_modules | grep web",
|
||||
"start": "make dev"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
diff --git a/node_modules/@giphy/js-analytics/dist/send-pingback.js b/node_modules/@giphy/js-analytics/dist/send-pingback.js
|
||||
index 001345a..f303443 100644
|
||||
index 989f0ff..149e77c 100644
|
||||
--- a/node_modules/@giphy/js-analytics/dist/send-pingback.js
|
||||
+++ b/node_modules/@giphy/js-analytics/dist/send-pingback.js
|
||||
@@ -10,6 +10,9 @@ var global_1 = __importDefault(require("./global"));
|
||||
var environment = (global_1.default === null || global_1.default === void 0 ? void 0 : global_1.default.GIPHY_PINGBACK_URL) || 'https://pingback.giphy.com';
|
||||
var pingBackUrl = environment + "/v2/pingback?apikey=l0HlIwPWyBBUDAUgM";
|
||||
var pingBackUrl = "".concat(environment, "/v2/pingback?apikey=l0HlIwPWyBBUDAUgM");
|
||||
var sendPingback = function (events) {
|
||||
+ // Disabled.
|
||||
+ return Promise.resolve();
|
||||
+
|
||||
var headers = js_util_1.getGiphySDKRequestHeaders();
|
||||
var headers = (0, js_util_1.getGiphySDKRequestHeaders)();
|
||||
/* istanbul ignore next */
|
||||
headers === null || headers === void 0 ? void 0 : headers.set('Content-Type', 'application/json');
|
||||
@@ -1,12 +0,0 @@
|
||||
diff --git a/node_modules/@giphy/js-brand/dist/typography.js b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
index af796bc..585fa00 100644
|
||||
--- a/node_modules/@giphy/js-brand/dist/typography.js
|
||||
+++ b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.css = exports.fontSize = exports.fontFamily = void 0;
|
||||
var emotion_1 = require("emotion");
|
||||
// eslint-disable-next-line
|
||||
-emotion_1.injectGlobal(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"], ["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"])));
|
||||
exports.fontFamily = {
|
||||
title: "'nexablack', sans-serif",
|
||||
body: 'interface, Helvetica Neue, helvetica, sans-serif;',
|
||||
13
patches/@giphy+js-brand+2.2.2.patch
Normal file
13
patches/@giphy+js-brand+2.2.2.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/node_modules/@giphy/js-brand/dist/typography.js b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
index 75ad96b..815cb8b 100644
|
||||
--- a/node_modules/@giphy/js-brand/dist/typography.js
|
||||
+++ b/node_modules/@giphy/js-brand/dist/typography.js
|
||||
@@ -6,7 +6,7 @@ var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cook
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.css = exports.fontSize = exports.fontFamily = exports.addFonts = void 0;
|
||||
var emotion_1 = require("emotion");
|
||||
-var addFonts = function () { return (0, emotion_1.injectGlobal)(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"], ["\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Rg.woff') format('woff');\n}\n\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: bold;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_Bd.woff') format('woff');\n}\n@font-face {\n font-family: 'interface';\n font-style: normal;\n font-weight: 900;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/InterFace_W_XBd.woff') format('woff');\n}\n@font-face {\n font-family: 'nexablack'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff2') format('woff2'),\n url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/nexa_black-webfont.woff') format('woff');\n}\n@font-face {\n font-family: 'SSStandard'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-standard.woff') format('woff');\n}\n@font-face {\n font-family: 'SSSocial'; \n font-style: normal;\n font-weight: normal;\n src: url('https://s3.amazonaws.com/giphyscripts/react-giphy-brand/fonts/ss-social.woff') format('woff');\n}\n"]))); };
|
||||
+var addFonts = function () { };
|
||||
exports.addFonts = addFonts;
|
||||
try {
|
||||
// in an env where process.env exists,
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that local media duration has changed.
|
||||
*
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { AtlasKitThemeProvider } from '@atlaskit/theme';
|
||||
import React from 'react';
|
||||
|
||||
import GlobalStyles from '../../base/ui/components/GlobalStyles.web';
|
||||
@@ -46,11 +45,9 @@ export class App extends AbstractApp {
|
||||
_createMainElement(component: React.ComponentType, props: any) {
|
||||
return (
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<GlobalStyles />
|
||||
<ChromeExtensionBanner />
|
||||
{ super._createMainElement(component, props) }
|
||||
</AtlasKitThemeProvider>
|
||||
<GlobalStyles />
|
||||
<ChromeExtensionBanner />
|
||||
{ super._createMainElement(component, props) }
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -63,9 +60,7 @@ export class App extends AbstractApp {
|
||||
_renderDialogContainer() {
|
||||
return (
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
<DialogContainer />
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import '../analytics/reducer';
|
||||
import '../authentication/reducer';
|
||||
import '../av-moderation/reducer';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import '../mobile/audio-mode/reducer';
|
||||
import '../mobile/background/reducer';
|
||||
import '../mobile/call-integration/reducer';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which sets the audio-only flag for the current
|
||||
* conference.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import { readyToClose } from '../../../features/mobile/external-api/actions';
|
||||
import {
|
||||
ACTION_PINNED,
|
||||
ACTION_UNPINNED,
|
||||
@@ -9,11 +8,14 @@ import {
|
||||
} from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { reloadNow } from '../../app/actions';
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { removeLobbyChatParticipant } from '../../chat/actions.any';
|
||||
import { openDisplayNamePrompt } from '../../display-name/actions';
|
||||
import { readyToClose } from '../../mobile/external-api/actions';
|
||||
import { showErrorNotification, showWarningNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
import { setIAmVisitor } from '../../visitors/actions';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
import { overwriteConfig } from '../config/actions';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection/actionTypes';
|
||||
import { connect, connectionDisconnected, disconnect } from '../connection/actions';
|
||||
@@ -62,12 +64,10 @@ import {
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Handler for before unload event.
|
||||
*/
|
||||
let beforeUnloadHandler;
|
||||
let beforeUnloadHandler: Function | undefined;
|
||||
|
||||
/**
|
||||
* Implements the middleware of the feature base/conference.
|
||||
@@ -129,7 +129,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
function _conferenceFailed({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { conference, error } = action;
|
||||
|
||||
if (error.name === JitsiConferenceErrors.REDIRECTED) {
|
||||
@@ -191,7 +191,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
const newConfig = restoreConferenceOptions(getState);
|
||||
|
||||
if (newConfig) {
|
||||
dispatch(overwriteConfig(newConfig))
|
||||
dispatch(overwriteConfig(newConfig)) // @ts-ignore
|
||||
.then(dispatch(conferenceWillLeave(conference)))
|
||||
.then(conference.leave())
|
||||
.then(dispatch(disconnect()))
|
||||
@@ -217,7 +217,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
|
||||
const [ vnode ] = error.params;
|
||||
|
||||
dispatch(overwriteConfig(newConfig))
|
||||
dispatch(overwriteConfig(newConfig)) // @ts-ignore
|
||||
.then(dispatch(conferenceWillLeave(conference)))
|
||||
.then(conference.leave())
|
||||
.then(dispatch(disconnect()))
|
||||
@@ -234,7 +234,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
if (typeof APP === 'undefined') {
|
||||
!error.recoverable
|
||||
&& conference
|
||||
&& conference.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch(reason => {
|
||||
&& conference.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch((reason: Error) => {
|
||||
// Even though we don't care too much about the failure, it may be
|
||||
// good to know that it happen, so log it (on the info level).
|
||||
logger.info('JitsiConference.leave() rejected with:', reason);
|
||||
@@ -267,7 +267,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
function _conferenceJoined({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
const { conference } = action;
|
||||
const { pendingSubjectChange } = getState()['features/base/conference'];
|
||||
@@ -288,6 +288,8 @@ function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
beforeUnloadHandler = () => {
|
||||
dispatch(conferenceWillLeave(conference));
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
window.addEventListener(disableBeforeUnloadHandlers ? 'unload' : 'beforeunload', beforeUnloadHandler);
|
||||
|
||||
if (requireDisplayName
|
||||
@@ -313,7 +315,7 @@ function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _connectionEstablished({ dispatch }, next, action) {
|
||||
function _connectionEstablished({ dispatch }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
|
||||
// FIXME: Workaround for the web version. Currently, the creation of the
|
||||
@@ -330,7 +332,7 @@ function _connectionEstablished({ dispatch }, next, action) {
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _logJwtErrors(message, state) {
|
||||
function _logJwtErrors(message: string, state: IReduxState) {
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
if (!jwt) {
|
||||
@@ -357,7 +359,7 @@ function _logJwtErrors(message, state) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
function _connectionFailed({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
_logJwtErrors(action.error.message, getState());
|
||||
|
||||
const result = next(action);
|
||||
@@ -417,7 +419,7 @@ function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceSubjectChanged({ dispatch, getState }, next, action) {
|
||||
function _conferenceSubjectChanged({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const result = next(action);
|
||||
const { subject } = getState()['features/base/conference'];
|
||||
|
||||
@@ -442,7 +444,7 @@ function _conferenceSubjectChanged({ dispatch, getState }, next, action) {
|
||||
* @param {Object} store - The redux store.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _conferenceWillLeave({ getState }: { getState: Function }) {
|
||||
function _conferenceWillLeave({ getState }: IStore) {
|
||||
_removeUnloadHandler(getState);
|
||||
}
|
||||
|
||||
@@ -460,7 +462,7 @@ function _conferenceWillLeave({ getState }: { getState: Function }) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _pinParticipant({ getState }, next, action) {
|
||||
function _pinParticipant({ getState }: IStore, next: Function, action: AnyAction) {
|
||||
const state = getState();
|
||||
const { conference } = state['features/base/conference'];
|
||||
|
||||
@@ -473,7 +475,7 @@ function _pinParticipant({ getState }, next, action) {
|
||||
const pinnedParticipant = getPinnedParticipant(state);
|
||||
const actionName = id ? ACTION_PINNED : ACTION_UNPINNED;
|
||||
const local
|
||||
= (participantById && participantById.local)
|
||||
= participantById?.local
|
||||
|| (!id && pinnedParticipant && pinnedParticipant.local);
|
||||
let participantIdForEvent;
|
||||
|
||||
@@ -481,7 +483,7 @@ function _pinParticipant({ getState }, next, action) {
|
||||
participantIdForEvent = local;
|
||||
} else {
|
||||
participantIdForEvent
|
||||
= actionName === ACTION_PINNED ? id : pinnedParticipant && pinnedParticipant.id;
|
||||
= actionName === ACTION_PINNED ? id : pinnedParticipant?.id;
|
||||
}
|
||||
|
||||
sendAnalytics(createPinnedEvent(
|
||||
@@ -501,10 +503,11 @@ function _pinParticipant({ getState }, next, action) {
|
||||
* @param {Function} getState - The redux getState function.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _removeUnloadHandler(getState) {
|
||||
function _removeUnloadHandler(getState: IStore['getState']) {
|
||||
if (typeof beforeUnloadHandler !== 'undefined') {
|
||||
const { disableBeforeUnloadHandlers = false } = getState()['features/base/config'];
|
||||
|
||||
// @ts-ignore
|
||||
window.removeEventListener(disableBeforeUnloadHandlers ? 'unload' : 'beforeunload', beforeUnloadHandler);
|
||||
beforeUnloadHandler = undefined;
|
||||
}
|
||||
@@ -522,7 +525,7 @@ function _removeUnloadHandler(getState) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _sendTones({ getState }, next, action) {
|
||||
function _sendTones({ getState }: IStore, next: Function, action: AnyAction) {
|
||||
const state = getState();
|
||||
const { conference } = state['features/base/conference'];
|
||||
|
||||
@@ -549,15 +552,15 @@ function _sendTones({ getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _setRoom({ dispatch, getState }, next, action) {
|
||||
function _setRoom({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const state = getState();
|
||||
const { localSubject, subject } = state['features/base/config'];
|
||||
const { room } = action;
|
||||
|
||||
if (room) {
|
||||
// Set the stored subject.
|
||||
dispatch(setLocalSubject(localSubject));
|
||||
dispatch(setSubject(subject));
|
||||
dispatch(setLocalSubject(localSubject ?? ''));
|
||||
dispatch(setSubject(subject ?? ''));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
@@ -572,15 +575,20 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _syncConferenceLocalTracksWithState({ getState }, action) {
|
||||
const conference = getCurrentConference(getState);
|
||||
function _syncConferenceLocalTracksWithState({ getState }: IStore, action: AnyAction) {
|
||||
const state = getState();
|
||||
const conference = getCurrentConference(state);
|
||||
let promise;
|
||||
|
||||
if (conference) {
|
||||
const track = action.track.jitsiTrack;
|
||||
|
||||
if (action.type === TRACK_ADDED) {
|
||||
promise = _addLocalTracksToConference(conference, [ track ]);
|
||||
// If gUM is slow and tracks are created after the user has already joined the conference, avoid
|
||||
// adding the tracks to the conference if the user is a visitor.
|
||||
if (!iAmVisitor(state)) {
|
||||
promise = _addLocalTracksToConference(conference, [ track ]);
|
||||
}
|
||||
} else {
|
||||
promise = _removeLocalTracksFromConference(conference, [ track ]);
|
||||
}
|
||||
@@ -603,7 +611,7 @@ function _syncConferenceLocalTracksWithState({ getState }, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _trackAddedOrRemoved(store, next, action) {
|
||||
function _trackAddedOrRemoved(store: IStore, next: Function, action: AnyAction) {
|
||||
const track = action.track;
|
||||
|
||||
// TODO All track swapping should happen here instead of conference.js.
|
||||
@@ -628,7 +636,7 @@ function _trackAddedOrRemoved(store, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _updateLocalParticipantInConference({ dispatch, getState }, next, action) {
|
||||
function _updateLocalParticipantInConference({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { conference } = getState()['features/base/conference'];
|
||||
const { participant } = action;
|
||||
const result = next(action);
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
setPrejoinPageVisibility,
|
||||
setSkipPrejoinOnReload
|
||||
@@ -60,6 +60,7 @@ export interface IJitsiConference {
|
||||
getMetadataHandler: Function;
|
||||
getName: Function;
|
||||
getParticipantById: Function;
|
||||
getParticipantCount: Function;
|
||||
getParticipants: Function;
|
||||
getRole: Function;
|
||||
getSpeakerStats: () => ISpeakerStats;
|
||||
@@ -101,7 +102,9 @@ export interface IJitsiConference {
|
||||
sendMessage: Function;
|
||||
sendPrivateTextMessage: Function;
|
||||
sendTextMessage: Function;
|
||||
sendTones: Function;
|
||||
sessionId: string;
|
||||
setAssumedBandwidthBps: (value: number) => void;
|
||||
setDesktopSharingFrameRate: Function;
|
||||
setDisplayName: Function;
|
||||
setLocalParticipantProperty: Function;
|
||||
|
||||
@@ -253,6 +253,7 @@ export interface IConfig {
|
||||
disableDeepLinking?: boolean;
|
||||
disableFilmstripAutohiding?: boolean;
|
||||
disableFocus?: boolean;
|
||||
disableIframeAPI?: boolean;
|
||||
disableIncomingMessageSound?: boolean;
|
||||
disableInitialGUM?: boolean;
|
||||
disableInviteFunctions?: boolean;
|
||||
|
||||
@@ -7,6 +7,7 @@ import _ from 'lodash';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { browser } from '../lib-jitsi-meet';
|
||||
import { IMediaState } from '../media/reducer';
|
||||
import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
import { IConfig } from './configType';
|
||||
@@ -65,7 +66,7 @@ export function getMeetingRegion(state: IReduxState) {
|
||||
* @param {Object} _state - The global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function getMultipleVideoSendingSupportFeatureFlag(_state: IReduxState) {
|
||||
export function getMultipleVideoSendingSupportFeatureFlag(_state: IReduxState | IMediaState) {
|
||||
return browser.supportsUnifiedPlan();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IReduxState } from '../../app/types';
|
||||
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
||||
|
||||
import { IConfig, IDeeplinkingConfig, IDeeplinkingMobileConfig, IDeeplinkingPlatformConfig } from './configType';
|
||||
import { TOOLBAR_BUTTONS } from './constants';
|
||||
@@ -75,8 +76,7 @@ export function isToolbarButtonEnabled(buttonName: string, state: IReduxState |
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function areAudioLevelsEnabled(state: IReduxState): boolean {
|
||||
// Default to false for React Native as audio levels are of no interest to the mobile app.
|
||||
return navigator.product !== 'ReactNative' && !state['features/base/config'].disableAudioLevels;
|
||||
return !state['features/base/config'].disableAudioLevels && JitsiMeetJS.isCollectingLocalStats();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,25 +45,18 @@ const INITIAL_NON_RN_STATE: IConfig = {
|
||||
const INITIAL_RN_STATE: IConfig = {
|
||||
analytics: {},
|
||||
|
||||
// FIXME The support for audio levels in lib-jitsi-meet polls the statistics
|
||||
// of WebRTC at a short interval multiple times a second. Unfortunately,
|
||||
// React Native is slow to fetch these statistics from the native WebRTC
|
||||
// API, through the React Native bridge and eventually to JavaScript.
|
||||
// Because the audio levels are of no interest to the mobile app, it is
|
||||
// fastest to merely disable them.
|
||||
disableAudioLevels: true,
|
||||
|
||||
// FIXME: Mobile codecs should probably be configurable separately, rather
|
||||
// than requiring this override here...
|
||||
// FIXME: than requiring this override here...
|
||||
|
||||
// TODO: Remove comments later, after next release, so that the fix is applied
|
||||
p2p: {
|
||||
disabledCodec: 'vp9',
|
||||
preferredCodec: 'vp8'
|
||||
// disabledCodec: 'vp9',
|
||||
// preferredCodec: 'vp8'
|
||||
},
|
||||
|
||||
videoQuality: {
|
||||
disabledCodec: 'vp9',
|
||||
preferredCodec: 'vp8'
|
||||
// disabledCodec: 'vp9',
|
||||
// preferredCodec: 'vp8'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a connection disconnected.
|
||||
*
|
||||
|
||||
@@ -72,7 +72,7 @@ export type ConnectionFailedError = {
|
||||
* connection: JitsiConnection
|
||||
* }}
|
||||
*/
|
||||
export function connectionDisconnected(connection: Object) {
|
||||
export function connectionDisconnected(connection?: Object) {
|
||||
return {
|
||||
type: CONNECTION_DISCONNECTED,
|
||||
connection
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* Flag indicating if add-people functionality should be enabled.
|
||||
* Default: enabled (true).
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that i18next has been initialized.
|
||||
*/
|
||||
|
||||
@@ -90,7 +90,7 @@ interface IProps extends IIconProps {
|
||||
/**
|
||||
* Style object to be applied.
|
||||
*/
|
||||
style?: StyleType;
|
||||
style?: StyleType | StyleType[];
|
||||
|
||||
/**
|
||||
* TabIndex for the Icon.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/jitsi-local-storage');
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
|
||||
// @ts-ignore
|
||||
import Bourne from '@hapi/bourne';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
|
||||
|
||||
import { browser } from '../lib-jitsi-meet';
|
||||
@@ -9,9 +10,6 @@ import { parseURLParams } from '../util/parseURLParams';
|
||||
|
||||
import logger from './logger';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
|
||||
|
||||
/**
|
||||
* Handles changes of the fake local storage.
|
||||
@@ -29,7 +27,7 @@ function onFakeLocalStorageChanged() {
|
||||
* @returns {boolean} - True if the local storage of the host page needs to be used instead jitsi-meet's local storage
|
||||
* and false otherwise.
|
||||
*/
|
||||
function shouldUseHostPageLocalStorage(urlParams) {
|
||||
function shouldUseHostPageLocalStorage(urlParams: { 'config.useHostPageLocalStorage'?: boolean; }) {
|
||||
// NOTE: normally the url params and the config will be merged into the redux store. But we want to setup the local
|
||||
// storage as soon as possible, the store is not created yet and the merging of the URL params and the config
|
||||
// haven't been executed yet. That's why we need to manually parse the URL params and also access the config through
|
||||
@@ -58,6 +56,7 @@ function shouldUseHostPageLocalStorage(urlParams) {
|
||||
* @returns {void}
|
||||
*/
|
||||
function setupJitsiLocalStorage() {
|
||||
// @ts-ignore
|
||||
const urlParams = parseURLParams(window.location);
|
||||
|
||||
if (shouldUseHostPageLocalStorage(urlParams)) {
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which sets the last-n for the conference.
|
||||
*
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
// of) the project jitsi-meet.
|
||||
//
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from 'lib-jitsi-meet';
|
||||
export { JitsiMeetJS as default };
|
||||
@@ -1,5 +0,0 @@
|
||||
// @flow
|
||||
|
||||
declare var JitsiMeetJS: Object;
|
||||
|
||||
export default JitsiMeetJS;
|
||||
3
react/features/base/lib-jitsi-meet/_.web.ts
Normal file
3
react/features/base/lib-jitsi-meet/_.web.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
declare let JitsiMeetJS: any;
|
||||
|
||||
export default JitsiMeetJS;
|
||||
@@ -157,7 +157,7 @@ export function setVideoAvailable(available: boolean) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setVideoMuted(
|
||||
muted: boolean,
|
||||
muted: boolean | number,
|
||||
authority: number = VIDEO_MUTISM_AUTHORITY.USER,
|
||||
ensureTrack = false) {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @flow
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import {
|
||||
createStartAudioOnlyEvent,
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
createTrackMutedEvent
|
||||
} from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { IStore } from '../../app/types';
|
||||
import { APP_STATE_CHANGED } from '../../mobile/background/actionTypes';
|
||||
import { showWarningNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
@@ -23,6 +24,7 @@ import { getPropertyValue } from '../settings/functions.any';
|
||||
import { TRACK_ADDED } from '../tracks/actionTypes';
|
||||
import { destroyLocalTracks } from '../tracks/actions.any';
|
||||
import { isLocalTrackMuted, isLocalVideoTrackDesktop, setTrackMuted } from '../tracks/functions.any';
|
||||
import { ITrack } from '../tracks/types';
|
||||
|
||||
import {
|
||||
SET_AUDIO_MUTED,
|
||||
@@ -155,7 +157,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _appStateChanged({ dispatch, getState }, next, action) {
|
||||
function _appStateChanged({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
if (navigator.product === 'ReactNative') {
|
||||
const { appState } = action;
|
||||
const mute = appState !== 'active' && !isLocalVideoTrackDesktop(getState());
|
||||
@@ -180,7 +182,7 @@ function _appStateChanged({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _setAudioOnly({ dispatch, getState }, next, action) {
|
||||
function _setAudioOnly({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { audioOnly } = action;
|
||||
const state = getState();
|
||||
|
||||
@@ -209,7 +211,7 @@ function _setAudioOnly({ dispatch, getState }, next, action) {
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
* specified {@code action}.
|
||||
*/
|
||||
function _setRoom({ dispatch, getState }, next, action) {
|
||||
function _setRoom({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
// Figure out the desires/intents i.e. the state of base/media. There are
|
||||
// multiple desires/intents ordered by precedence such as server-side
|
||||
// config, config overrides in the user-supplied URL, user's own app
|
||||
@@ -222,7 +224,7 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||
const videoMuted = roomIsValid ? getStartWithVideoMuted(state) : _VIDEO_INITIAL_MEDIA_STATE.muted;
|
||||
|
||||
sendAnalytics(
|
||||
createStartMutedConfigurationEvent('local', audioMuted, videoMuted));
|
||||
createStartMutedConfigurationEvent('local', audioMuted, Boolean(videoMuted)));
|
||||
logger.log(
|
||||
`Start muted: ${audioMuted ? 'audio, ' : ''}${
|
||||
videoMuted ? 'video' : ''}`);
|
||||
@@ -295,7 +297,7 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _syncTrackMutedState({ getState }, track) {
|
||||
function _syncTrackMutedState({ getState }: IStore, track: ITrack) {
|
||||
const state = getState()['features/base/media'];
|
||||
const mediaType = track.mediaType;
|
||||
const muted = Boolean(state[mediaType].muted);
|
||||
@@ -1 +1 @@
|
||||
import './middleware.any.js';
|
||||
import './middleware.any';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import './middleware.any.js';
|
||||
import './middleware.any';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { showNotification } from '../../notifications/actions';
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* Create an action for when dominant speaker changes.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { BoxModel } from '../../styles/components/styles/BoxModel';
|
||||
import { ColorPalette } from '../../styles/components/styles/ColorPalette';
|
||||
|
||||
@@ -3,14 +3,18 @@ import { getGravatarURL } from '@jitsi/js-utils/avatar';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { isStageFilmstripAvailable } from '../../filmstrip/functions';
|
||||
import { isAddPeopleEnabled, isDialOutEnabled } from '../../invite/functions';
|
||||
import { toggleShareDialog } from '../../share-room/actions';
|
||||
import { IStateful } from '../app/types';
|
||||
import { GRAVATAR_BASE_URL } from '../avatar/constants';
|
||||
import { isCORSAvatarURL } from '../avatar/functions';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { ADD_PEOPLE_ENABLED } from '../flags/constants';
|
||||
import { getFeatureFlag } from '../flags/functions';
|
||||
import i18next from '../i18n/i18next';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
|
||||
import { toState } from '../redux/functions';
|
||||
import { getScreenShareTrack } from '../tracks/functions';
|
||||
import { getScreenShareTrack } from '../tracks/functions.any';
|
||||
import { createDeferred } from '../util/helpers';
|
||||
|
||||
import {
|
||||
@@ -22,6 +26,7 @@ import {
|
||||
import { preloadImage } from './preloadImage';
|
||||
import { FakeParticipant, IJitsiParticipant, IParticipant, ISourceInfo } from './types';
|
||||
|
||||
|
||||
/**
|
||||
* Temp structures for avatar urls to be checked/preloaded.
|
||||
*/
|
||||
@@ -706,3 +711,32 @@ export function getRaiseHandsQueue(stateful: IStateful): Array<{ id: string; rai
|
||||
export function hasRaisedHand(participant?: IParticipant): boolean {
|
||||
return Boolean(participant?.raisedHandTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add people feature enabling/disabling.
|
||||
*
|
||||
* @param {Object|Function} stateful - Object or function that can be resolved
|
||||
* to the Redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const addPeopleFeatureControl = (stateful: IStateful) => {
|
||||
const state = toState(stateful);
|
||||
|
||||
return getFeatureFlag(state, ADD_PEOPLE_ENABLED, true)
|
||||
&& (isAddPeopleEnabled(state) || isDialOutEnabled(state));
|
||||
};
|
||||
|
||||
/**
|
||||
* Controls share dialog visibility.
|
||||
*
|
||||
* @param {boolean} addPeopleFeatureEnabled - Checks if add people functionality is enabled.
|
||||
* @param {Function} dispatch - The Redux dispatch function.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export const setShareDialogVisiblity = (addPeopleFeatureEnabled: boolean, dispatch: Function) => {
|
||||
if (addPeopleFeatureEnabled) {
|
||||
dispatch(toggleShareDialog(false));
|
||||
} else {
|
||||
dispatch(toggleShareDialog(true));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
@@ -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()}.
|
||||
*
|
||||
@@ -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.
|
||||
*
|
||||
@@ -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.
|
||||
*
|
||||
@@ -15,6 +15,8 @@ interface IProps {
|
||||
* prop of the native component.
|
||||
*/
|
||||
size?: 'large' | 'small' | 'medium';
|
||||
|
||||
style?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { ColorPalette } from '../../../styles/components/styles/ColorPalette';
|
||||
|
||||
export default {
|
||||
@@ -1,13 +1,31 @@
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { translate } from '../../../i18n/functions';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
import Button from '../../../ui/components/web/Button';
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
dialog: {
|
||||
backgroundColor: theme.palette.ui01,
|
||||
border: `1px solid ${theme.palette.ui04}`,
|
||||
borderRadius: `${Number(theme.shape.borderRadius)}px`,
|
||||
boxShadow: '0px 1px 2px rgba(41, 41, 41, 0.25)',
|
||||
color: theme.palette.text01,
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
padding: `${theme.spacing(3)} 10`,
|
||||
'& .retry-button': {
|
||||
margin: '16px auto 0 auto'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link InlineDialogFailure}.
|
||||
*/
|
||||
interface IProps extends WithTranslation {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Allows to retry the call that previously didn't succeed.
|
||||
@@ -22,50 +40,48 @@ interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Inline dialog that represents a failure and allows a retry.
|
||||
*
|
||||
* @returns {Element}
|
||||
*/
|
||||
class InlineDialogFailure extends Component<IProps> {
|
||||
/**
|
||||
* Renders the content of this component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t, showSupportLink } = this.props;
|
||||
const InlineDialogFailure = ({
|
||||
onRetry,
|
||||
showSupportLink
|
||||
}: IProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { classes } = useStyles();
|
||||
|
||||
const supportLink = interfaceConfig.SUPPORT_URL;
|
||||
const supportString = t('inlineDialogFailure.supportMsg');
|
||||
const supportLinkElem
|
||||
= supportLink && showSupportLink
|
||||
? (
|
||||
<div className = 'inline-dialog-error-text'>
|
||||
<span>{ supportString.padEnd(supportString.length + 1) }
|
||||
</span>
|
||||
<span>
|
||||
<a
|
||||
href = { supportLink }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{ t('inlineDialogFailure.support') }
|
||||
</a>
|
||||
</span>
|
||||
<span>.</span>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className = 'inline-dialog-error'>
|
||||
<div className = 'inline-dialog-error-text'>
|
||||
{ t('inlineDialogFailure.msg') }
|
||||
</div>
|
||||
{ supportLinkElem }
|
||||
<Button
|
||||
className = 'inline-dialog-error-button'
|
||||
label = { t('inlineDialogFailure.retry') }
|
||||
onClick = { this.props.onRetry } />
|
||||
const supportLink = interfaceConfig.SUPPORT_URL;
|
||||
const supportString = t('inlineDialogFailure.supportMsg');
|
||||
const supportLinkElem = supportLink && showSupportLink
|
||||
? (
|
||||
<div>
|
||||
<span>{ supportString.padEnd(supportString.length + 1) }
|
||||
</span>
|
||||
<span>
|
||||
<a
|
||||
href = { supportLink }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{ t('inlineDialogFailure.support') }
|
||||
</a>
|
||||
</span>
|
||||
<span>.</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
: null;
|
||||
|
||||
export default translate(InlineDialogFailure);
|
||||
return (
|
||||
<div className = { classes.dialog }>
|
||||
<div>
|
||||
{ t('inlineDialogFailure.msg') }
|
||||
</div>
|
||||
{ supportLinkElem }
|
||||
<Button
|
||||
className = 'retry-button'
|
||||
label = { t('inlineDialogFailure.retry') }
|
||||
onClick = { onRetry } />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineDialogFailure;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import AKInlineDialog from '@atlaskit/inline-dialog';
|
||||
import { MultiSelectStateless } from '@atlaskit/multi-select';
|
||||
import _debounce from 'lodash/debounce';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { MultiSelectItem } from '../../../ui/components/types';
|
||||
import MultiSelect from '../../../ui/components/web/MultiSelect';
|
||||
import logger from '../../logger';
|
||||
|
||||
import InlineDialogFailure from './InlineDialogFailure';
|
||||
@@ -13,119 +11,119 @@ import InlineDialogFailure from './InlineDialogFailure';
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link MultiSelectAutocomplete}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The default value of the selected item.
|
||||
*/
|
||||
defaultValue: Array<Object>,
|
||||
defaultValue?: Array<any>;
|
||||
|
||||
/**
|
||||
* Optional footer to show as a last element in the results.
|
||||
* Should be of type {content: <some content>}.
|
||||
*/
|
||||
footer: Object,
|
||||
footer?: any;
|
||||
|
||||
/**
|
||||
* Indicates if the component is disabled.
|
||||
*/
|
||||
isDisabled: boolean,
|
||||
isDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Text to display while a query is executing.
|
||||
*/
|
||||
loadingMessage: string,
|
||||
loadingMessage: string;
|
||||
|
||||
/**
|
||||
* The text to show when no matches are found.
|
||||
*/
|
||||
noMatchesFound: string,
|
||||
noMatchesFound: string;
|
||||
|
||||
/**
|
||||
* The function called immediately before a selection has been actually
|
||||
* selected. Provides an opportunity to do any formatting.
|
||||
*/
|
||||
onItemSelected: Function,
|
||||
onItemSelected: Function;
|
||||
|
||||
/**
|
||||
* The function called when the selection changes.
|
||||
*/
|
||||
onSelectionChange: Function,
|
||||
onSelectionChange: Function;
|
||||
|
||||
/**
|
||||
* The placeholder text of the input component.
|
||||
*/
|
||||
placeholder: string,
|
||||
placeholder: string;
|
||||
|
||||
/**
|
||||
* The service providing the search.
|
||||
*/
|
||||
resourceClient: { makeQuery: Function, parseResults: Function },
|
||||
resourceClient: { makeQuery: Function; parseResults: Function; };
|
||||
|
||||
/**
|
||||
* Indicates if the component should fit the container.
|
||||
*/
|
||||
shouldFitContainer: boolean,
|
||||
shouldFitContainer: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if we should focus.
|
||||
*/
|
||||
shouldFocus: boolean,
|
||||
shouldFocus: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the support link should be shown in case of an error.
|
||||
*/
|
||||
showSupportLink: Boolean,
|
||||
};
|
||||
showSupportLink: Boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of
|
||||
* {@link MultiSelectAutocomplete}.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Indicates if the dropdown is open.
|
||||
*/
|
||||
isOpen: boolean,
|
||||
|
||||
/**
|
||||
* The text that filters the query result of the search.
|
||||
*/
|
||||
filterValue: string,
|
||||
|
||||
/**
|
||||
* Indicates if the component is currently loading results.
|
||||
*/
|
||||
loading: boolean,
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* Indicates if there was an error.
|
||||
*/
|
||||
error: boolean,
|
||||
error: boolean;
|
||||
|
||||
/**
|
||||
* The text that filters the query result of the search.
|
||||
*/
|
||||
filterValue: string;
|
||||
|
||||
/**
|
||||
* Indicates if the dropdown is open.
|
||||
*/
|
||||
isOpen: boolean;
|
||||
|
||||
/**
|
||||
* The list of result items.
|
||||
*/
|
||||
items: Array<Object>,
|
||||
items: Array<MultiSelectItem>;
|
||||
|
||||
/**
|
||||
* Indicates if the component is currently loading results.
|
||||
*/
|
||||
loading: boolean;
|
||||
|
||||
/**
|
||||
* The list of selected items.
|
||||
*/
|
||||
selectedItems: Array<Object>
|
||||
};
|
||||
selectedItems: Array<MultiSelectItem>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A MultiSelect that is also auto-completing.
|
||||
*/
|
||||
class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
class MultiSelectAutocomplete extends Component<IProps, IState> {
|
||||
/**
|
||||
* Initializes a new {@code MultiSelectAutocomplete} instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
const defaultValue = this.props.defaultValue || [];
|
||||
@@ -148,11 +146,11 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
/**
|
||||
* Sets the items to display as selected.
|
||||
*
|
||||
* @param {Array<Object>} selectedItems - The list of items to display as
|
||||
* @param {Array<MultiSelectItem>} selectedItems - The list of items to display as
|
||||
* having been selected.
|
||||
* @returns {void}
|
||||
*/
|
||||
setSelectedItems(selectedItems: Array<Object> = []) {
|
||||
setSelectedItems(selectedItems: Array<MultiSelectItem> = []) {
|
||||
this.setState({ selectedItems });
|
||||
}
|
||||
|
||||
@@ -162,37 +160,32 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const shouldFitContainer = this.props.shouldFitContainer || false;
|
||||
const shouldFocus = this.props.shouldFocus || false;
|
||||
const isDisabled = this.props.isDisabled || false;
|
||||
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 (
|
||||
<div>
|
||||
<MultiSelectStateless
|
||||
<MultiSelect
|
||||
autoFocus = { autoFocus }
|
||||
disabled = { disabled }
|
||||
error = { this.state.error }
|
||||
errorDialog = { errorDialog }
|
||||
filterValue = { this.state.filterValue }
|
||||
footer = { this.props.footer }
|
||||
icon = { null }
|
||||
isDisabled = { isDisabled }
|
||||
isLoading = { this.state.loading }
|
||||
isOpen = { this.state.isOpen }
|
||||
items = { this.state.items }
|
||||
loadingMessage = { this.props.loadingMessage }
|
||||
noMatchesFound = { noMatchesFound }
|
||||
noMatchesText = { noMatchesFound }
|
||||
onFilterChange = { this._onFilterChange }
|
||||
onRemoved = { this._onSelectionChange }
|
||||
onSelected = { this._onSelectionChange }
|
||||
placeholder = { placeholder }
|
||||
selectedItems = { this.state.selectedItems }
|
||||
shouldFitContainer = { shouldFitContainer }
|
||||
shouldFocus = { shouldFocus } />
|
||||
{ this._renderError() }
|
||||
selectedItems = { this.state.selectedItems } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_onFilterChange: (string) => void;
|
||||
|
||||
/**
|
||||
* Sets the state and sends a query on filter change.
|
||||
@@ -201,13 +194,13 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onFilterChange(filterValue) {
|
||||
_onFilterChange(filterValue: string) {
|
||||
this.setState({
|
||||
// Clean the error if the filterValue is empty.
|
||||
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) {
|
||||
@@ -215,8 +208,6 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onRetry: () => void;
|
||||
|
||||
/**
|
||||
* Retries the query on retry.
|
||||
*
|
||||
@@ -227,18 +218,16 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
this._sendQuery(this.state.filterValue);
|
||||
}
|
||||
|
||||
_onSelectionChange: (Object) => void;
|
||||
|
||||
/**
|
||||
* Updates the selected items when a selection event occurs.
|
||||
*
|
||||
* @param {Object} item - The selected item.
|
||||
* @param {any} item - The selected item.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSelectionChange(item) {
|
||||
_onSelectionChange(item: any) {
|
||||
const existing
|
||||
= this.state.selectedItems.find(k => k.value === item.value);
|
||||
= this.state.selectedItems.find((k: any) => k.value === item.value);
|
||||
let selectedItems = this.state.selectedItems;
|
||||
|
||||
if (existing) {
|
||||
@@ -265,30 +254,22 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
if (!this.state.error) {
|
||||
return null;
|
||||
}
|
||||
const content = (
|
||||
<div className = 'autocomplete-error'>
|
||||
<InlineDialogFailure
|
||||
onRetry = { this._onRetry }
|
||||
showSupportLink = { this.props.showSupportLink } />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<AKInlineDialog
|
||||
content = { content }
|
||||
isOpen = { true } />
|
||||
|
||||
<InlineDialogFailure
|
||||
onRetry = { this._onRetry }
|
||||
showSupportLink = { this.props.showSupportLink } />
|
||||
);
|
||||
}
|
||||
|
||||
_sendQuery: (string) => void;
|
||||
|
||||
/**
|
||||
* Sends a query to the resourceClient.
|
||||
*
|
||||
* @param {string} filterValue - The string to use for the search.
|
||||
* @returns {void}
|
||||
*/
|
||||
_sendQuery(filterValue) {
|
||||
_sendQuery(filterValue: string) {
|
||||
if (!filterValue) {
|
||||
return;
|
||||
}
|
||||
@@ -299,11 +280,11 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
|
||||
const resourceClient = this.props.resourceClient || {
|
||||
makeQuery: () => Promise.resolve([]),
|
||||
parseResults: results => results
|
||||
parseResults: (results: any) => results
|
||||
};
|
||||
|
||||
resourceClient.makeQuery(filterValue)
|
||||
.then(results => {
|
||||
.then((results: any) => {
|
||||
if (this.state.filterValue !== filterValue) {
|
||||
this.setState({
|
||||
error: false
|
||||
@@ -311,20 +292,15 @@ class MultiSelectAutocomplete extends Component<Props, State> {
|
||||
|
||||
return;
|
||||
}
|
||||
const itemGroups = [
|
||||
{
|
||||
items: resourceClient.parseResults(results)
|
||||
}
|
||||
];
|
||||
|
||||
this.setState({
|
||||
items: itemGroups,
|
||||
items: resourceClient.parseResults(results),
|
||||
isOpen: true,
|
||||
loading: false,
|
||||
error: false
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error: Error) => {
|
||||
logger.error('MultiSelectAutocomplete error in query', error);
|
||||
|
||||
this.setState({
|
||||
@@ -52,6 +52,8 @@ export type Item = {
|
||||
*/
|
||||
title: string;
|
||||
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Item url.
|
||||
*/
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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 ?? {};
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* @flow */
|
||||
|
||||
import { SET_CONNECTION_STATE } from './actionTypes';
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description-complete-sentence
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -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.
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from '../config/functions.any';
|
||||
import { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, MediaType, VIDEO_TYPE } from '../media/constants';
|
||||
import { IMediaState } from '../media/reducer';
|
||||
import {
|
||||
getVirtualScreenshareParticipantOwnerId,
|
||||
isScreenShareParticipant
|
||||
@@ -359,7 +360,7 @@ export function isUserInteractionRequiredForUnmute(state: IReduxState) {
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function setTrackMuted(track: any, muted: boolean, state: IReduxState) {
|
||||
export function setTrackMuted(track: any, muted: boolean, state: IReduxState | IMediaState) {
|
||||
muted = Boolean(muted); // eslint-disable-line no-param-reassign
|
||||
|
||||
// Ignore the check for desktop track muted operation. When the screenshare is terminated by clicking on the
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user