Compare commits

..

1 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
8d53dce1eb chore(rn,versions) bump sdk and app versions 2023-04-24 15:34:58 +03:00
179 changed files with 1556 additions and 1746 deletions

View File

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

View File

@@ -26,5 +26,5 @@ android.useAndroidX=true
android.enableJetifier=true
android.bundle.enableUncompressedNativeLibs=false
appVersion=99.0.0
sdkVersion=99.0.0
appVersion=23.0.0
sdkVersion=8.0.0

2
app.js
View File

@@ -18,6 +18,7 @@ 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.
@@ -37,6 +38,7 @@ window.APP = {
'index.loaded': window.indexLoadedTime
},
keyboardshortcut,
translation,
UI
};

View File

@@ -140,7 +140,6 @@ 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';
@@ -2309,7 +2308,10 @@ export default {
APP.UI.initConference();
dispatch(initKeyboardShortcuts());
if (!config.disableShortcuts) {
APP.keyboardshortcut.init();
}
dispatch(conferenceJoined(room));
const jwt = APP.store.getState()['features/base/jwt'];

View File

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

View File

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

View File

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

View File

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

View File

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

6
globals.d.ts vendored
View File

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

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<string>23.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<string>23.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<string>23.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<string>23.0.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<string>8.0.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>99.0.0</string>
<string>8.0.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,261 @@
/* 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;

45
package-lock.json generated
View File

@@ -60,7 +60,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -12739,8 +12739,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"integrity": "sha512-F3vOUwD/ys5dGtswR3C5IK8JPDOG+71J+nhkEDLotkni/Zws8KHzf8Ye332UuetUjCTGJP3CbRFe9yJdYERe7g==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -12758,7 +12758,7 @@
"patch-package": "6.5.1",
"promise.allsettled": "1.0.4",
"sdp-transform": "2.3.0",
"strophe.js": "1.5.0",
"strophe.js": "1.6.0",
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "git+https://github.com/jitsi/strophejs-plugin-stream-management#679be5902097ed612fb5062b5549f3f32b6f5f47",
"uuid": "8.1.0",
@@ -17615,27 +17615,18 @@
}
},
"node_modules/strophe.js": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
"dependencies": {
"abab": "^2.0.3",
"karma-rollup-preprocessor": "^7.0.8"
},
"optionalDependencies": {
"@xmldom/xmldom": "0.8.2",
"@xmldom/xmldom": "0.8.3",
"ws": "^8.5.0"
}
},
"node_modules/strophe.js/node_modules/@xmldom/xmldom": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/strophe.js/node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
@@ -29117,8 +29108,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"integrity": "sha512-F3vOUwD/ys5dGtswR3C5IK8JPDOG+71J+nhkEDLotkni/Zws8KHzf8Ye332UuetUjCTGJP3CbRFe9yJdYERe7g==",
"requires": {
"@jitsi/js-utils": "2.0.0",
"@jitsi/logger": "2.0.0",
@@ -29134,7 +29125,7 @@
"patch-package": "6.5.1",
"promise.allsettled": "1.0.4",
"sdp-transform": "2.3.0",
"strophe.js": "1.5.0",
"strophe.js": "1.6.0",
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "git+https://github.com/jitsi/strophejs-plugin-stream-management#679be5902097ed612fb5062b5549f3f32b6f5f47",
"uuid": "8.1.0",
@@ -32845,22 +32836,16 @@
"dev": true
},
"strophe.js": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
"requires": {
"@xmldom/xmldom": "0.8.2",
"@xmldom/xmldom": "0.8.7",
"abab": "^2.0.3",
"karma-rollup-preprocessor": "^7.0.8",
"ws": "^8.5.0"
},
"dependencies": {
"@xmldom/xmldom": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
"optional": true
},
"ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",

View File

@@ -65,7 +65,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1620.0.0+7f0012f7/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -180,7 +180,7 @@
},
"overrides": {
"strophe.js@1.6.0": {
"@xmldom/xmldom": "0.8.7"
"@xmldom/xmldom": "0.8.7"
}
},
"engines": {

View File

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

View File

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

View File

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

View File

@@ -43,7 +43,6 @@ 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';
@@ -134,7 +133,6 @@ export interface IReduxState {
'features/google-api': IGoogleApiState;
'features/invite': IInviteState;
'features/jaas': IJaaSState;
'features/keyboard-shortcuts': IKeyboardShortcutsState;
'features/large-video': ILargeVideoState;
'features/lobby': ILobbyState;
'features/mobile/audio-mode': IMobileAudioModeState;

View File

@@ -3,7 +3,6 @@ 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';
@@ -451,12 +450,11 @@ export function conferenceUniqueIdSet(conference: IJitsiConference) {
*/
export function _conferenceWillJoin(conference: IJitsiConference) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const localTracks
= getLocalTracks(state['features/base/tracks'])
= getLocalTracks(getState()['features/base/tracks'])
.map(t => t.jitsiTrack);
if (localTracks.length && !iAmVisitor(state)) {
if (localTracks.length) {
_addLocalTracksToConference(conference, localTracks);
}
@@ -808,7 +806,7 @@ export function setStartReactionsMuted(muted: boolean, updateBackend = false) {
export function setPassword(
conference: IJitsiConference | undefined,
method: Function | undefined,
password?: string) {
password: string) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
if (!conference) {
return;

View File

@@ -308,7 +308,6 @@ 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,7 +338,6 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
},
focusUserJid: focusJid,
disableFocus: true, // This flag disables sending the initial conference request
disableLocalStats: true,
bosh: config.bosh && appendURLParam(config.bosh, 'vnode', vnode),
websocket: config.websocket && appendURLParam(config.websocket, 'vnode', vnode)
};

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,7 @@ export interface IProps {
* The style (as in stylesheet) to be applied to this
* {@code AbstractContainer}.
*/
style?: StyleType | StyleType[];
style?: StyleType;
tabIndex?: number;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ export interface IProps {
/**
* Implements an abstract class for the RaisedHandIndicator component.
*/
export default abstract class AbstractRaisedHandIndicator<P extends IProps>
export default class AbstractRaisedHandIndicator<P extends IProps>
extends Component<P> {
/**
@@ -41,7 +41,7 @@ export default abstract class AbstractRaisedHandIndicator<P extends IProps>
*
* @returns {React$Element<*>}
*/
abstract _renderIndicator(): React.ReactElement;
_renderIndicator: () => React.ReactElement;
}

View File

@@ -1,3 +1,5 @@
// @flow
import React, { Component } from 'react';
import { IconMicSlash } from '../../../base/icons/svg';

View File

@@ -1,9 +1,10 @@
// @flow
import React, { PureComponent } from 'react';
import { FlatList, ViewStyle, ViewToken } from 'react-native';
import { FlatList } from 'react-native';
import { SafeAreaView, withSafeAreaInsets } from 'react-native-safe-area-context';
import { connect } from 'react-redux';
import { IReduxState, IStore } from '../../../app/types';
import { getLocalParticipant } from '../../../base/participants/functions';
import Platform from '../../../base/react/Platform.native';
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
@@ -23,54 +24,54 @@ import styles from './styles';
// Immutable reference to avoid re-renders.
const NO_REMOTE_VIDEOS: any[] = [];
const NO_REMOTE_VIDEOS = [];
/**
* Filmstrip component's property types.
*/
interface IProps {
type Props = {
/**
* Application's aspect ratio.
*/
_aspectRatio: Symbol;
_aspectRatio: Symbol,
_clientHeight: number;
_clientWidth: number,
_clientWidth: number;
_clientHeight: number,
/**
* Whether or not to hide the self view.
*/
_disableSelfView: boolean;
_localParticipantId: string;
/**
* The participants in the conference.
*/
_participants: Array<any>;
_disableSelfView: boolean,
/**
* Whether or not the toolbox is displayed.
*/
_toolboxVisible: Boolean;
_toolboxVisible: Boolean,
_localParticipantId: string,
/**
* The participants in the conference.
*/
_participants: Array<any>,
/**
* The indicator which determines whether the filmstrip is visible.
*/
_visible: boolean;
_visible: boolean,
/**
* Invoked to trigger state changes in Redux.
*/
dispatch: IStore['dispatch'];
dispatch: Function,
/**
* Object containing the safe area insets.
*/
insets: Object;
}
insets: Object,
};
/**
* Implements a React {@link Component} which represents the filmstrip on
@@ -78,7 +79,7 @@ interface IProps {
*
* @augments Component
*/
class Filmstrip extends PureComponent<IProps> {
class Filmstrip extends PureComponent<Props> {
/**
* Whether the local participant should be rendered separately from the
* remote participants ie outside of their {@link ScrollView}.
@@ -95,7 +96,7 @@ class Filmstrip extends PureComponent<IProps> {
*
* @inheritdoc
*/
constructor(props: IProps) {
constructor(props) {
super(props);
// XXX Our current design is to have the local participant separate from
@@ -128,13 +129,15 @@ class Filmstrip extends PureComponent<IProps> {
this._renderThumbnail = this._renderThumbnail.bind(this);
}
_keyExtractor: string => string;
/**
* Returns a key for a passed item of the list.
*
* @param {string} item - The user ID.
* @returns {string} - The user ID.
*/
_keyExtractor(item: string) {
_keyExtractor(item) {
return item;
}
@@ -163,14 +166,16 @@ class Filmstrip extends PureComponent<IProps> {
});
}
_getItemLayout: (?Array<string>, number) => {length: number, offset: number, index: number};
/**
* Optimization for FlatList. Returns the length, offset and index for an item.
*
* @param {Array<string>} _data - The data array with user IDs.
* @param {Array<string>} data - The data array with user IDs.
* @param {number} index - The index number of the item.
* @returns {Object}
*/
_getItemLayout(_data: string[] | null | undefined, index: number) {
_getItemLayout(data, index) {
const { _aspectRatio } = this.props;
const isNarrowAspectRatio = _aspectRatio === ASPECT_RATIO_NARROW;
const length = isNarrowAspectRatio ? styles.thumbnail.width : styles.thumbnail.height;
@@ -182,6 +187,8 @@ class Filmstrip extends PureComponent<IProps> {
};
}
_onViewableItemsChanged: Object => void;
/**
* A handler for visible items changes.
*
@@ -189,7 +196,7 @@ class Filmstrip extends PureComponent<IProps> {
* @param {Array<Object>} data.viewableItems - The visible items array.
* @returns {void}
*/
_onViewableItemsChanged({ viewableItems = [] }: { viewableItems: ViewToken[]; }) {
_onViewableItemsChanged({ viewableItems = [] }) {
const { _disableSelfView } = this.props;
if (!this._separateLocalThumbnail && !_disableSelfView && viewableItems[0]?.index === 0) {
@@ -202,8 +209,8 @@ class Filmstrip extends PureComponent<IProps> {
return;
}
let startIndex = Number(viewableItems[0].index);
let endIndex = Number(viewableItems[viewableItems.length - 1].index);
let startIndex = viewableItems[0].index;
let endIndex = viewableItems[viewableItems.length - 1].index;
if (!this._separateLocalThumbnail && !_disableSelfView) {
// We are off by one in the remote participants array.
@@ -214,13 +221,15 @@ class Filmstrip extends PureComponent<IProps> {
this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
}
_renderThumbnail: Object => Object;
/**
* Creates React Element to display each participant in a thumbnail.
*
* @private
* @returns {ReactElement}
*/
_renderThumbnail({ item }: { item: string; }) {
_renderThumbnail({ item /* , index , separators */ }) {
return (
<Thumbnail
key = { item }
@@ -268,9 +277,9 @@ class Filmstrip extends PureComponent<IProps> {
}
return (
<SafeAreaView // @ts-ignore
<SafeAreaView
edges = { [ bottomEdge && 'bottom', 'left', 'right' ].filter(Boolean) }
style = { filmstripStyle as ViewStyle }>
style = { filmstripStyle }>
{
this._separateLocalThumbnail
&& !isNarrowAspectRatio
@@ -308,9 +317,9 @@ class Filmstrip extends PureComponent<IProps> {
*
* @param {Object} state - The redux state.
* @private
* @returns {IProps}
* @returns {Props}
*/
function _mapStateToProps(state: IReduxState) {
function _mapStateToProps(state) {
const { enabled, remoteParticipants } = state['features/filmstrip'];
const disableSelfView = getHideSelfView(state);
const showRemoteVideos = shouldRemoteVideosBeVisible(state);
@@ -321,7 +330,7 @@ function _mapStateToProps(state: IReduxState) {
_clientHeight: responsiveUI.clientHeight,
_clientWidth: responsiveUI.clientWidth,
_disableSelfView: disableSelfView,
_localParticipantId: getLocalParticipant(state)?.id ?? '',
_localParticipantId: getLocalParticipant(state)?.id,
_participants: showRemoteVideos ? remoteParticipants : NO_REMOTE_VIDEOS,
_toolboxVisible: isToolboxVisible(state),
_visible: enabled && isFilmstripVisible(state)

View File

@@ -1,5 +1,7 @@
// @flow
import React from 'react';
import { View, ViewStyle } from 'react-native';
import { View } from 'react-native';
import Thumbnail from './Thumbnail';
import styles from './styles';
@@ -12,7 +14,7 @@ import styles from './styles';
*/
export default function LocalThumbnail() {
return (
<View style = { styles.localThumbnail as ViewStyle }>
<View style = { styles.localThumbnail }>
<Thumbnail />
</View>
);

View File

@@ -1,3 +1,5 @@
// @flow
import React from 'react';
import { IconPin } from '../../../base/icons/svg';

View File

@@ -1,11 +1,12 @@
// @flow
import React from 'react';
import { View, ViewStyle } from 'react-native';
import { View } from 'react-native';
import { connect } from 'react-redux';
import { IconRaiseHand } from '../../../base/icons/svg';
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
import AbstractRaisedHandIndicator, {
IProps,
_mapStateToProps
} from '../AbstractRaisedHandIndicator';
@@ -17,7 +18,7 @@ import styles from './styles';
*
* @augments Component
*/
class RaisedHandIndicator extends AbstractRaisedHandIndicator<IProps> {
class RaisedHandIndicator extends AbstractRaisedHandIndicator<Props> {
/**
* Renders the platform specific indicator element.
*
@@ -25,7 +26,7 @@ class RaisedHandIndicator extends AbstractRaisedHandIndicator<IProps> {
*/
_renderIndicator() {
return (
<View style = { styles.raisedHandIndicator as ViewStyle }>
<View style = { styles.raisedHandIndicator }>
<BaseIndicator
icon = { IconRaiseHand }
iconStyle = { styles.raisedHandIcon } />

View File

@@ -1,3 +1,5 @@
// @flow
import React from 'react';
import { IconScreenshare } from '../../../base/icons/svg';

View File

@@ -1,8 +1,8 @@
import React, { PureComponent } from 'react';
import { Image, ImageStyle, View, ViewStyle } from 'react-native';
import { Image, View } from 'react-native';
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';
import { IReduxState, IStore } from '../../../app/types';
import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../base/media/constants';
import { pinParticipant } from '../../../base/participants/actions';
@@ -18,16 +18,14 @@ import {
} from '../../../base/participants/functions';
import { FakeParticipant } from '../../../base/participants/types';
import Container from '../../../base/react/components/native/Container';
import { StyleType } from '../../../base/styles/functions.any';
import { trackStreamingStatusChanged } from '../../../base/tracks/actions.native';
import {
getTrackByMediaTypeAndParticipant,
getVideoTrackByParticipant
} from '../../../base/tracks/functions.native';
import { ITrack } from '../../../base/tracks/types';
import ConnectionIndicator from '../../../connection-indicator/components/native/ConnectionIndicator';
import DisplayNameLabel from '../../../display-name/components/native/DisplayNameLabel';
import { getGifDisplayMode, getGifForParticipant } from '../../../gifs/functions.native';
import { getGifDisplayMode, getGifForParticipant } from '../../../gifs/functions';
import {
showConnectionStatus,
showContextMenuDetails,
@@ -46,42 +44,42 @@ import styles, { AVATAR_SIZE } from './styles';
/**
* Thumbnail component's property types.
*/
interface IProps {
type Props = {
/**
* Whether local audio (microphone) is muted or not.
*/
_audioMuted: boolean;
/**
* The type of participant if the participant is fake.
*/
_fakeParticipant?: FakeParticipant;
_audioMuted: boolean,
/**
* URL of GIF sent by this participant, null if there's none.
*/
_gifSrc?: string;
_gifSrc: ?string,
/**
* The type of participant if the participant is fake.
*/
_fakeParticipant?: FakeParticipant,
/**
* Indicates whether the participant is screen sharing.
*/
_isScreenShare: boolean;
_isScreenShare: boolean,
/**
* Indicates whether the thumbnail is for a virtual screenshare participant.
*/
_isVirtualScreenshare: boolean;
_isVirtualScreenshare: boolean,
/**
* Indicates whether the participant is local.
*/
_local?: boolean;
_local: boolean,
/**
* Shared video local participant owner.
*/
_localVideoOwner: boolean;
_localVideoOwner: boolean,
/**
* The ID of the participant obtain from the participant object in Redux.
@@ -89,71 +87,71 @@ interface IProps {
* NOTE: Generally it should be the same as the participantID prop except the case where the passed
* participantID doesn't correspond to any of the existing participants.
*/
_participantId: string;
_participantId: string,
/**
* Indicates whether the participant is pinned or not.
*/
_pinned?: boolean;
_pinned: boolean,
/**
* Whether or not the participant has the hand raised.
*/
_raisedHand: boolean;
_raisedHand: boolean,
/**
* Whether to show the dominant speaker indicator or not.
*/
_renderDominantSpeakerIndicator?: boolean;
_renderDominantSpeakerIndicator: boolean,
/**
* Whether to show the moderator indicator or not.
*/
_renderModeratorIndicator: boolean;
_renderModeratorIndicator: boolean,
/**
* The video track that will be displayed in the thumbnail.
*/
_videoTrack?: ITrack;
_videoTrack: ?Object,
/**
* Invoked to trigger state changes in Redux.
*/
dispatch: IStore['dispatch'];
dispatch: Dispatch<any>,
/**
* The height of the thumbnail.
* The height of the thumnail.
*/
height?: number;
height: ?number,
/**
* The ID of the participant related to the thumbnail.
*/
participantID?: string;
participantID: ?string,
/**
* Whether to display or hide the display name of the participant in the thumbnail.
*/
renderDisplayName?: boolean;
renderDisplayName: ?boolean,
/**
* If true, it tells the thumbnail that it needs to behave differently. E.g. React differently to a single tap.
*/
tileView?: boolean;
}
tileView?: boolean
};
/**
* React component for video thumbnail.
*/
class Thumbnail extends PureComponent<IProps> {
class Thumbnail extends PureComponent<Props> {
/**
* Creates new Thumbnail component.
*
* @param {IProps} props - The props of the component.
* @param {Props} props - The props of the component.
* @returns {Thumbnail}
*/
constructor(props: IProps) {
constructor(props: Props) {
super(props);
this._onClick = this._onClick.bind(this);
@@ -161,6 +159,8 @@ class Thumbnail extends PureComponent<IProps> {
this.handleTrackStreamingStatusChanged = this.handleTrackStreamingStatusChanged.bind(this);
}
_onClick: () => void;
/**
* Thumbnail click handler.
*
@@ -176,6 +176,8 @@ class Thumbnail extends PureComponent<IProps> {
}
}
_onThumbnailLongPress: () => void;
/**
* Thumbnail long press handler.
*
@@ -217,11 +219,11 @@ class Thumbnail extends PureComponent<IProps> {
if (!_fakeParticipant || _isVirtualScreenshare) {
indicators.push(<View
key = 'top-left-indicators'
style = { styles.thumbnailTopLeftIndicatorContainer as ViewStyle }>
style = { styles.thumbnailTopLeftIndicatorContainer }>
{ !_isVirtualScreenshare && <ConnectionIndicator participantId = { participantId } /> }
{ !_isVirtualScreenshare && <RaisedHandIndicator participantId = { participantId } /> }
{ tileView && (isScreenShare || _isVirtualScreenshare) && (
<View style = { styles.screenShareIndicatorContainer as ViewStyle }>
<View style = { styles.screenShareIndicatorContainer }>
<ScreenShareIndicator />
</View>
) }
@@ -229,9 +231,7 @@ class Thumbnail extends PureComponent<IProps> {
indicators.push(<Container
key = 'bottom-indicators'
style = { styles.thumbnailIndicatorContainer }>
<Container
style = { ((audioMuted || renderModeratorIndicator) && styles.bottomIndicatorsContainer
) as StyleType }>
<Container style = { (audioMuted || renderModeratorIndicator) && styles.bottomIndicatorsContainer }>
{ audioMuted && !_isVirtualScreenshare && <AudioMutedIndicator /> }
{ !tileView && _pinned && <PinnedIndicator />}
{ renderModeratorIndicator && !_isVirtualScreenshare && <ModeratorIndicator />}
@@ -275,7 +275,7 @@ class Thumbnail extends PureComponent<IProps> {
* @inheritdoc
* @returns {void}
*/
componentDidUpdate(prevProps: IProps) {
componentDidUpdate(prevProps: Props) {
// TODO: after converting this component to a react function component,
// use a custom hook to update local track streaming status.
const { _videoTrack, dispatch } = this.props;
@@ -323,7 +323,7 @@ class Thumbnail extends PureComponent<IProps> {
* @param {JitsiTrackStreamingStatus} streamingStatus - The updated track streaming status.
* @returns {void}
*/
handleTrackStreamingStatusChanged(jitsiTrack: any, streamingStatus: string) {
handleTrackStreamingStatusChanged(jitsiTrack, streamingStatus) {
this.props.dispatch(trackStreamingStatusChanged(jitsiTrack, streamingStatus));
}
@@ -363,11 +363,11 @@ class Thumbnail extends PureComponent<IProps> {
styleOverrides,
_raisedHand && !_isVirtualScreenshare ? styles.thumbnailRaisedHand : null,
_renderDominantSpeakerIndicator && !_isVirtualScreenshare ? styles.thumbnailDominantSpeaker : null
] as StyleType[] }
] }
touchFeedback = { false }>
{ _gifSrc ? <Image
source = {{ uri: _gifSrc }}
style = { styles.thumbnailGif as ImageStyle } />
style = { styles.thumbnailGif } />
: <>
<ParticipantView
avatarSize = { tileView ? AVATAR_SIZE * 1.5 : AVATAR_SIZE }
@@ -388,36 +388,36 @@ class Thumbnail extends PureComponent<IProps> {
* Function that maps parts of Redux state tree into component props.
*
* @param {Object} state - Redux state.
* @param {IProps} ownProps - Properties of component.
* @param {Props} ownProps - Properties of component.
* @returns {Object}
*/
function _mapStateToProps(state: IReduxState, ownProps: any) {
function _mapStateToProps(state, ownProps) {
const { ownerId } = state['features/shared-video'];
const tracks = state['features/base/tracks'];
const { participantID, tileView } = ownProps;
const participant = getParticipantByIdOrUndefined(state, participantID);
const localParticipantId = getLocalParticipant(state)?.id;
const localParticipantId = getLocalParticipant(state).id;
const id = participant?.id;
const audioTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
const videoTrack = getVideoTrackByParticipant(state, participant);
const isScreenShare = videoTrack?.videoType === VIDEO_TYPE.DESKTOP;
const participantCount = getParticipantCount(state);
const renderDominantSpeakerIndicator = participant?.dominantSpeaker && participantCount > 2;
const renderDominantSpeakerIndicator = participant && participant.dominantSpeaker && participantCount > 2;
const _isEveryoneModerator = isEveryoneModerator(state);
const renderModeratorIndicator = tileView && !_isEveryoneModerator
&& participant?.role === PARTICIPANT_ROLE.MODERATOR;
const { gifUrl: gifSrc } = getGifForParticipant(state, id ?? '');
const { gifUrl: gifSrc } = getGifForParticipant(state, id);
const mode = getGifDisplayMode(state);
return {
_audioMuted: audioTrack?.muted ?? true,
_fakeParticipant: participant?.fakeParticipant,
_gifSrc: mode === 'chat' ? undefined : gifSrc,
_gifSrc: mode === 'chat' ? null : gifSrc,
_isScreenShare: isScreenShare,
_isVirtualScreenshare: isScreenShareParticipant(participant),
_local: participant?.local,
_localVideoOwner: Boolean(ownerId === localParticipantId),
_participantId: id ?? '',
_participantId: id,
_pinned: participant?.pinned,
_raisedHand: hasRaisedHand(participant),
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,

View File

@@ -1,17 +1,16 @@
// @flow
import React, { PureComponent } from 'react';
import {
FlatList,
GestureResponderEvent,
SafeAreaView,
TouchableWithoutFeedback,
ViewToken
TouchableWithoutFeedback
} from 'react-native';
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';
import { IReduxState, IStore } from '../../../app/types';
import { getLocalParticipant, getParticipantCountWithFake } from '../../../base/participants/functions';
import { ILocalParticipant } from '../../../base/participants/types';
import { getHideSelfView } from '../../../base/settings/functions.any';
import { setVisibleRemoteParticipants } from '../../actions.web';
@@ -21,74 +20,74 @@ import styles from './styles';
/**
* The type of the React {@link Component} props of {@link TileView}.
*/
interface IProps {
type Props = {
/**
* Application's aspect ratio.
*/
_aspectRatio: Symbol;
_aspectRatio: Symbol,
/**
* The number of columns.
*/
_columns: number;
_columns: number,
/**
* Whether or not to hide the self view.
*/
_disableSelfView: boolean;
_disableSelfView: boolean,
/**
* Application's viewport height.
*/
_height: number;
_height: number,
/**
* The local participant.
*/
_localParticipant?: ILocalParticipant;
_localParticipant: Object,
/**
* The number of participants in the conference.
*/
_participantCount: number;
_participantCount: number,
/**
* An array with the IDs of the remote participants in the conference.
*/
_remoteParticipants: Array<string>;
_remoteParticipants: Array<string>,
/**
* The thumbnail height.
*/
_thumbnailHeight?: number;
_thumbnailHeight: number,
/**
* Application's viewport height.
*/
_width: number;
_width: number,
/**
* Invoked to update the receiver video quality.
*/
dispatch: IStore['dispatch'];
dispatch: Dispatch<any>,
/**
* Object containing the safe area insets.
*/
insets: EdgeInsets;
insets: Object,
/**
* Callback to invoke when tile view is tapped.
*/
onClick: (e?: GestureResponderEvent) => void;
}
onClick: Function
};
/**
* An empty array. The purpose of the constant is to use the same reference every time we need an empty array.
* This will prevent unnecessary re-renders.
*/
const EMPTY_ARRAY: any[] = [];
const EMPTY_ARRAY = [];
/**
* Implements a React {@link PureComponent} which displays thumbnails in a two
@@ -96,17 +95,17 @@ const EMPTY_ARRAY: any[] = [];
*
* @augments PureComponent
*/
class TileView extends PureComponent<IProps> {
class TileView extends PureComponent<Props> {
/**
* The styles for the content container of the FlatList.
*/
_contentContainerStyles: any;
_contentContainerStyles: Object;
/**
* The styles for the FlatList.
*/
_flatListStyles: any;
_flatListStyles: Object;
/**
* The FlatList's viewabilityConfig.
@@ -116,9 +115,9 @@ class TileView extends PureComponent<IProps> {
/**
* Creates new TileView component.
*
* @param {IProps} props - The props of the component.
* @param {Props} props - The props of the component.
*/
constructor(props: IProps) {
constructor(props: Props) {
super(props);
this._keyExtractor = this._keyExtractor.bind(this);
@@ -138,16 +137,20 @@ class TileView extends PureComponent<IProps> {
};
}
_keyExtractor: string => string;
/**
* Returns a key for a passed item of the list.
*
* @param {string} item - The user ID.
* @returns {string} - The user ID.
*/
_keyExtractor(item: string) {
_keyExtractor(item) {
return item;
}
_onViewableItemsChanged: Object => void;
/**
* A handler for visible items changes.
*
@@ -155,7 +158,7 @@ class TileView extends PureComponent<IProps> {
* @param {Array<Object>} data.viewableItems - The visible items array.
* @returns {void}
*/
_onViewableItemsChanged({ viewableItems = [] }: { viewableItems: ViewToken[]; }) {
_onViewableItemsChanged({ viewableItems = [] }: { viewableItems: Array<Object> }) {
const { _disableSelfView } = this.props;
if (viewableItems[0]?.index === 0 && !_disableSelfView) {
@@ -169,8 +172,8 @@ class TileView extends PureComponent<IProps> {
}
// We are off by one in the remote participants array.
const startIndex = Number(viewableItems[0].index) - (_disableSelfView ? 0 : 1);
const endIndex = Number(viewableItems[viewableItems.length - 1].index) - (_disableSelfView ? 0 : 1);
const startIndex = viewableItems[0].index - (_disableSelfView ? 0 : 1);
const endIndex = viewableItems[viewableItems.length - 1].index - (_disableSelfView ? 0 : 1);
this.props.dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
}
@@ -184,7 +187,7 @@ class TileView extends PureComponent<IProps> {
render() {
const { _columns, _height, _thumbnailHeight, _width, onClick } = this.props;
const participants = this._getSortedParticipants();
const initialRowsToRender = Math.ceil(_height / (Number(_thumbnailHeight) + (2 * styles.thumbnail.margin)));
const initialRowsToRender = Math.ceil(_height / (_thumbnailHeight + (2 * styles.thumbnail.margin)));
if (this._flatListStyles.minHeight !== _height || this._flatListStyles.minWidth !== _width) {
this._flatListStyles = {
@@ -247,13 +250,15 @@ class TileView extends PureComponent<IProps> {
return [ _localParticipant?.id, ..._remoteParticipants ];
}
_renderThumbnail: Object => Object;
/**
* Creates React Element to display each participant in a thumbnail.
*
* @private
* @returns {ReactElement}
*/
_renderThumbnail({ item }: { item: string; }) {
_renderThumbnail({ item/* , index , separators */ }) {
const { _thumbnailHeight } = this.props;
return (
@@ -273,18 +278,18 @@ class TileView extends PureComponent<IProps> {
* @param {Object} state - The redux state.
* @param {Object} ownProps - Component props.
* @private
* @returns {IProps}
* @returns {Props}
*/
function _mapStateToProps(state: IReduxState, ownProps: any) {
function _mapStateToProps(state, ownProps) {
const responsiveUi = state['features/base/responsive-ui'];
const { remoteParticipants, tileViewDimensions } = state['features/filmstrip'];
const disableSelfView = getHideSelfView(state);
const { height } = tileViewDimensions?.thumbnailSize ?? {};
const { columns } = tileViewDimensions ?? {};
const { height } = tileViewDimensions.thumbnailSize;
const { columns } = tileViewDimensions;
return {
_aspectRatio: responsiveUi.aspectRatio,
_columns: columns ?? 1,
_columns: columns,
_disableSelfView: disableSelfView,
_height: responsiveUi.clientHeight - (ownProps.insets?.top || 0),
_insets: ownProps.insets,

View File

@@ -15,7 +15,6 @@ import { translate } from '../../../base/i18n/functions';
import Icon from '../../../base/icons/components/Icon';
import { IconArrowDown, IconArrowUp } from '../../../base/icons/svg';
import { getHideSelfView } from '../../../base/settings/functions.any';
import { registerShortcut, unregisterShortcut } from '../../../keyboard-shortcuts/actions';
import { showToolbox } from '../../../toolbox/actions.web';
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
import { LAYOUTS } from '../../../video-layout/constants';
@@ -296,12 +295,12 @@ class Filmstrip extends PureComponent <IProps, IState> {
* @inheritdoc
*/
componentDidMount() {
this.props.dispatch(registerShortcut({
character: 'F',
helpDescription: 'keyboardShortcuts.toggleFilmstrip',
handler: this._onShortcutToggleFilmstrip
}));
APP.keyboardshortcut.registerShortcut(
'F',
'filmstripPopover',
this._onShortcutToggleFilmstrip,
'keyboardShortcuts.toggleFilmstrip'
);
document.addEventListener('mouseup', this._onDragMouseUp);
// @ts-ignore
@@ -314,8 +313,7 @@ class Filmstrip extends PureComponent <IProps, IState> {
* @inheritdoc
*/
componentWillUnmount() {
this.props.dispatch(unregisterShortcut('F'));
APP.keyboardshortcut.unregisterShortcut('F');
document.removeEventListener('mouseup', this._onDragMouseUp);
// @ts-ignore

View File

@@ -184,7 +184,6 @@ interface IDimensions {
}
interface IFilmstripDimensions {
columns?: number;
filmstripHeight?: number;
filmstripWidth?: number;
gridDimensions?: {

View File

@@ -1,4 +1,4 @@
import { GiphyContent, GiphyGridView, GiphyMediaType, GiphyRating } from '@giphy/react-native-sdk';
import { GiphyContent, GiphyGridView, GiphyMediaType } from '@giphy/react-native-sdk';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
@@ -9,7 +9,7 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import Input from '../../../base/ui/components/native/Input';
import { sendMessage } from '../../../chat/actions.any';
import { goBack } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { formatGifUrlMessage, getGifRating, getGifUrl, getGiphyProxyUrl } from '../../functions.native';
import { formatGifUrlMessage, getGifRating, getGifUrl, getGiphyProxyUrl } from '../../functions';
import GifsMenuFooter from './GifsMenuFooter';
import styles from './styles';
@@ -18,7 +18,7 @@ const GifsMenu = () => {
const [ searchQuery, setSearchQuery ] = useState('');
const dispatch = useDispatch();
const { t } = useTranslation();
const rating = useSelector(getGifRating) as GiphyRating;
const rating = useSelector(getGifRating);
const proxyUrl = useSelector(getGiphyProxyUrl);
const options = {

View File

@@ -1,7 +1,8 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Image, Text, TextStyle, View, ViewStyle } from 'react-native';
import { Image, Text, View } from 'react-native';
// @ts-ignore
import styles from './styles';
@@ -14,10 +15,9 @@ const GifsMenuFooter = (): JSX.Element => {
const { t } = useTranslation();
return (
<View style = { styles.credit as ViewStyle }>
<Text style = { styles.creditText as TextStyle }>
{ t('poweredby') }
</Text>
<View style = { styles.credit }>
<Text
style = { styles.creditText }>{ t('poweredby') }</Text>
<Image
source = { require('../../../../../images/GIPHY_logo.png') } />
</View>

View File

@@ -0,0 +1,33 @@
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
/**
* {@code AbstractGoogleSignInButton} Component's property types.
*/
interface IProps extends WithTranslation {
/**
* The callback to invoke when the button is clicked.
*/
onClick: (e?: React.MouseEvent) => void;
/**
* True if the user is signed in, so it needs to render a different label
* and maybe different style (for the future).
*/
signedIn?: boolean;
/**
* The text to display within {@code GoogleSignInButton}.
*/
text?: string;
}
/**
* Abstract class of the {@code GoogleSignInButton} to share platform
* independent code.
*
* @inheritdoc
*/
export default class AbstractGoogleSignInButton extends Component<IProps> {
}

View File

@@ -1,11 +1,13 @@
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { GestureResponderEvent, Image, ImageStyle, TouchableOpacity, ViewStyle } from 'react-native';
// @flow
import React from 'react';
import { Image, TouchableOpacity } from 'react-native';
import { translate } from '../../base/i18n/functions';
import Button from '../../base/ui/components/native/Button';
import { BUTTON_TYPES } from '../../base/ui/constants.native';
import AbstractGoogleSignInButton from './AbstractGoogleSignInButton';
import styles from './styles';
// eslint-disable-next-line
@@ -19,31 +21,12 @@ const GOOGLE_BRAND_IMAGE = require('../../../../images/btn_google_signin_dark_no
* this way), hence the custom button implementation.
*/
interface IProps extends WithTranslation {
/**
* The callback to invoke when the button is clicked.
*/
onClick: (e?: React.MouseEvent<HTMLButtonElement> | GestureResponderEvent) => void;
/**
* True if the user is signed in, so it needs to render a different label
* and maybe different style (for the future).
*/
signedIn?: boolean;
/**
* The text to display within {@code GoogleSignInButton}.
*/
text?: string;
}
/**
* A React Component showing a button to sign in with Google.
*
* @augments Component
*/
class GoogleSignInButton extends Component<IProps> {
class GoogleSignInButton extends AbstractGoogleSignInButton {
/**
* Implements React's {@link Component#render()}.
@@ -68,11 +51,11 @@ class GoogleSignInButton extends Component<IProps> {
return (
<TouchableOpacity
onPress = { onClick }
style = { styles.signInButton as ViewStyle } >
style = { styles.signInButton } >
<Image
resizeMode = { 'contain' }
source = { GOOGLE_BRAND_IMAGE }
style = { styles.signInImage as ImageStyle } />
style = { styles.signInImage } />
</TouchableOpacity>
);
}

View File

@@ -1,33 +1,15 @@
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import React from 'react';
import { translate } from '../../base/i18n/functions';
interface IProps extends WithTranslation {
/**
* The callback to invoke when the button is clicked.
*/
onClick: (e?: React.MouseEvent) => void;
/**
* True if the user is signed in, so it needs to render a different label
* and maybe different style (for the future).
*/
signedIn?: boolean;
/**
* The text to display within {@code GoogleSignInButton}.
*/
text?: string;
}
import AbstractGoogleSignInButton from './AbstractGoogleSignInButton';
/**
* A React Component showing a button to sign in with Google.
*
* @augments Component
*/
class GoogleSignInButton extends Component<IProps> {
class GoogleSignInButton extends AbstractGoogleSignInButton {
/**
* Implements React's {@link Component#render()}.

View File

@@ -1,3 +1,5 @@
// @flow
import { createStyleSheet } from '../../base/styles/functions.any';
/**

View File

@@ -68,7 +68,7 @@ export interface IProps {
/**
* Whether or not to allow sip invites.
*/
_sipInviteEnabled: boolean;
_sipInviteEnabled: boolean;
/**
* The Redux dispatch function.
@@ -92,7 +92,7 @@ export interface IState {
/**
* The list of invite items.
*/
inviteItems: Array<IInvitee | IInviteSelectItem>;
inviteItems: Array<IInviteSelectItem>;
}
/**

View File

@@ -1,6 +1,7 @@
/* eslint-disable lines-around-comment */
import _ from 'lodash';
import React, { ReactElement } from 'react';
import { WithTranslation } from 'react-i18next';
import {
ActivityIndicator,
FlatList,
@@ -22,6 +23,7 @@ import {
IconSearch,
IconShare
} from '../../../../base/icons/svg';
// @ts-ignore
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
import AvatarListItem from '../../../../base/react/components/native/AvatarListItem';
import { Item } from '../../../../base/react/types';
@@ -31,16 +33,19 @@ import HeaderNavigationButton
from '../../../../mobile/navigation/components/HeaderNavigationButton';
import { beginShareRoom } from '../../../../share-room/actions';
import { INVITE_TYPES } from '../../../constants';
import { IInviteSelectItem, IInvitee } from '../../../types';
import AbstractAddPeopleDialog, {
type IProps as AbstractProps,
type IState as AbstractState,
// @ts-ignore
type Props as AbstractProps,
// @ts-ignore
type State as AbstractState,
_mapStateToProps as _abstractMapStateToProps
} from '../AbstractAddPeopleDialog';
// @ts-ignore
import styles, { AVATAR_SIZE } from './styles';
interface IProps extends AbstractProps, WithTranslation {
interface IProps extends AbstractProps {
/**
* True if the invite dialog should be open, false otherwise.
@@ -50,7 +55,12 @@ interface IProps extends AbstractProps, WithTranslation {
/**
* Default prop for navigation between screen components(React Navigation).
*/
navigation: any;
navigation: Object;
/**
* Function used to translate i18n labels.
*/
t: Function;
/**
* Theme used for styles.
@@ -85,7 +95,9 @@ interface IState extends AbstractState {
/**
* Implements a special dialog to invite people from a directory service.
*/
class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
class AddPeopleDialog
// @ts-ignore
extends AbstractAddPeopleDialog<IProps, IState> {
/**
* Default state object to reset the state to when needed.
*/
@@ -107,7 +119,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
searchTimeout: number;
/**
* Constructor of the component.
* Contrustor of the component.
*
* @inheritdoc
*/
@@ -139,6 +151,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
componentDidMount() {
const { navigation, t } = this.props;
// @ts-ignore
navigation.setOptions({
headerRight: () => (
<HeaderNavigationButton
@@ -158,6 +171,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
componentDidUpdate(prevProps: IProps) {
const { navigation, t } = this.props;
// @ts-ignore
navigation.setOptions({
// eslint-disable-next-line react/no-multi-comp
headerRight: () => (
@@ -183,9 +197,12 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
*/
render() {
const {
// @ts-ignore
_addPeopleEnabled,
// @ts-ignore
_dialOutEnabled
} = this.props;
// @ts-ignore
const { inviteItems, selectableItems } = this.state;
let placeholderKey = 'searchPlaceholder';
@@ -285,6 +302,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
});
// Clear search results
// @ts-ignore
this._onTypeQuery('');
}
@@ -296,9 +314,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
_onInvite() {
// @ts-ignore
this._invite(this.state.inviteItems)
.then((invitesLeftToSend: IInvitee[]) => {
.then((invitesLeftToSend: ArrayLike<any>) => {
if (invitesLeftToSend.length) {
this.setState({
// @ts-ignore
inviteItems: invitesLeftToSend
});
this._showFailedInviteAlert();
@@ -314,22 +333,26 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
*/
_onPressItem(item: Item) {
return () => {
// @ts-ignore
const { inviteItems } = this.state;
const finderKey = item.type === INVITE_TYPES.PHONE ? 'number' : 'user_id';
if (inviteItems.find(
_.matchesProperty(finderKey, item[finderKey as keyof typeof item]))) {
// @ts-ignore
_.matchesProperty(finderKey, item[finderKey]))) {
// Item is already selected, need to unselect it.
this.setState({
// @ts-ignore
inviteItems: inviteItems.filter(
(element: any) => item[finderKey as keyof typeof item] !== element[finderKey])
// @ts-ignore
(element: any) => item[finderKey] !== element[finderKey])
});
} else {
// Item is not selected yet, need to add to the list.
// @ts-ignore
const items = inviteItems.concat(item);
const items: Array<Object> = inviteItems.concat(item);
this.setState({
// @ts-ignore
inviteItems: _.sortBy(items, [ 'name', 'number' ])
});
}
@@ -342,10 +365,12 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
* @returns {void}
*/
_onShareMeeting() {
// @ts-ignore
if (this.state.inviteItems.length > 0) {
// The use probably intended to invite people.
this._onInvite();
} else {
// @ts-ignore
this.props.dispatch(beginShareRoom());
}
}
@@ -363,6 +388,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
});
clearTimeout(this.searchTimeout);
// @ts-ignore
this.searchTimeout = setTimeout(() => {
this.setState({
searchInprogress: true
@@ -436,8 +462,9 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
*/
_renderItem(flatListItem: any, index: number): ReactElement | null {
const { item } = flatListItem;
// @ts-ignore
const { inviteItems } = this.state;
let selected: IInvitee | IInviteSelectItem | undefined | boolean = false;
let selected = false;
const renderableItem = this._getRenderableItem(flatListItem);
if (!renderableItem) {
@@ -543,6 +570,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
* @returns {void}
*/
_showFailedInviteAlert() {
// @ts-ignore
this.props.dispatch(openDialog(AlertDialog, {
contentKey: {
key: 'inviteDialog.alertText'
@@ -555,15 +583,14 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<IProps, IState> {
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {any} _ownProps - Component's own props.
* @returns {{
* _isVisible: boolean
* }}
*/
function _mapStateToProps(state: IReduxState, _ownProps: any) {
function _mapStateToProps(state: IReduxState) {
return {
..._abstractMapStateToProps(state)
};
}
// @ts-ignore
export default translate(connect(_mapStateToProps)(AddPeopleDialog));

View File

@@ -325,7 +325,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<IProps, IState> {
const userTypes = [ INVITE_TYPES.USER, INVITE_TYPES.VIDEO_ROOM, INVITE_TYPES.ROOM ];
const users = response.filter(item => userTypes.includes(item.type));
const userDisplayItems: any = [];
const userDisplayItems = [];
for (const user of users) {
const { name, phone } = user;

View File

@@ -1,11 +1,11 @@
import { Route } from '@react-navigation/native';
// @flow
import React, { PureComponent } from 'react';
import { WithTranslation } from 'react-i18next';
import { Linking, View, ViewStyle } from 'react-native';
import { Linking, View } from 'react-native';
import { WebView } from 'react-native-webview';
import { connect } from 'react-redux';
import { type Dispatch } from 'redux';
import { IStore } from '../../../../app/types';
import { openDialog } from '../../../../base/dialog/actions';
import { translate } from '../../../../base/i18n/functions';
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
@@ -15,32 +15,38 @@ import { getDialInfoPageURLForURIString } from '../../../functions';
import DialInSummaryErrorDialog from './DialInSummaryErrorDialog';
import styles, { INDICATOR_COLOR } from './styles';
interface IProps extends WithTranslation {
dispatch: IStore['dispatch'];
type Props = {
dispatch: Dispatch<any>,
/**
* Default prop for navigating between screen components(React Navigation).
*/
navigation: any;
navigation: Object,
/**
* Default prop for navigating between screen components(React Navigation).
*/
route: Route<'', { summaryUrl: string; }>;
}
route: Object,
/**
* Translation function.
*/
t: Function
};
/**
* Implements a React native component that displays the dial in info page for a specific room.
*/
class DialInSummary extends PureComponent<IProps> {
class DialInSummary extends PureComponent<Props> {
/**
* Initializes a new instance.
*
* @inheritdoc
*/
constructor(props: IProps) {
constructor(props: Props) {
super(props);
this._onError = this._onError.bind(this);
@@ -80,13 +86,15 @@ class DialInSummary extends PureComponent<IProps> {
onShouldStartLoadWithRequest = { this._onNavigate }
renderLoading = { this._renderLoading }
setSupportMultipleWindows = { false }
source = {{ uri: getDialInfoPageURLForURIString(summaryUrl) ?? '' }}
source = {{ uri: getDialInfoPageURLForURIString(summaryUrl) }}
startInLoadingState = { true }
style = { styles.webView } />
</JitsiScreen>
);
}
_onError: () => void;
/**
* Callback to handle the error if the page fails to load.
*
@@ -96,6 +104,8 @@ class DialInSummary extends PureComponent<IProps> {
this.props.dispatch(openDialog(DialInSummaryErrorDialog));
}
_onNavigate: Object => Boolean;
/**
* Callback to intercept navigation inside the webview and make the native app handle the dial requests.
*
@@ -104,7 +114,7 @@ class DialInSummary extends PureComponent<IProps> {
* @param {any} request - The request object.
* @returns {boolean}
*/
_onNavigate(request: { url: string; }) {
_onNavigate(request) {
const { url } = request;
const { route } = this.props;
const summaryUrl = route.params?.summaryUrl;
@@ -116,6 +126,8 @@ class DialInSummary extends PureComponent<IProps> {
return url === getDialInfoPageURLForURIString(summaryUrl);
}
_renderLoading: () => React$Component<any>;
/**
* Renders the loading indicator.
*
@@ -123,7 +135,7 @@ class DialInSummary extends PureComponent<IProps> {
*/
_renderLoading() {
return (
<View style = { styles.indicatorWrapper as ViewStyle }>
<View style = { styles.indicatorWrapper }>
<LoadingIndicator
color = { INDICATOR_COLOR }
size = 'large' />

View File

@@ -1,5 +1,6 @@
// @flow
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import AlertDialog from '../../../../base/dialog/components/native/AlertDialog';
@@ -8,7 +9,7 @@ import { translate } from '../../../../base/i18n/functions';
/**
* Dialog to inform the user that we couldn't fetch the dial-in info page.
*/
class DialInSummaryErrorDialog extends Component<WithTranslation> {
class DialInSummaryErrorDialog extends Component<{}> {
/**
* Implements React's {@link Component#render()}.
*
@@ -21,6 +22,8 @@ class DialInSummaryErrorDialog extends Component<WithTranslation> {
contentKey = 'info.dialInSummaryError' />
);
}
_onSubmit: () => boolean;
}
export default translate(connect()(DialInSummaryErrorDialog));

View File

@@ -1,3 +1,5 @@
// @flow
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
export const INDICATOR_COLOR = BaseTheme.palette.ui07;

View File

@@ -31,11 +31,7 @@ const useStyles = makeStyles()((theme: Theme) => {
textAlign: 'center',
display: 'flex',
flexDirection: 'column',
borderRadius: 6,
'& *': {
userSelect: 'text'
}
borderRadius: 6
},
confNameLabel: {
...withPixelLineHeight(theme.typography.heading6),

View File

@@ -1,24 +0,0 @@
/**
* The type of the action which signals that the keyboard shortcuts should be initialized.
*/
export const INIT_KEYBOARD_SHORTCUTS = 'INIT_KEYBOARD_SHORTCUTS';
/**
* The type of the action which signals that a keyboard shortcut should be registered.
*/
export const REGISTER_KEYBOARD_SHORTCUT = 'REGISTER_KEYBOARD_SHORTCUT';
/**
* The type of the action which signals that a keyboard shortcut should be unregistered.
*/
export const UNREGISTER_KEYBOARD_SHORTCUT = 'UNREGISTER_KEYBOARD_SHORTCUT';
/**
* The type of the action which signals that a keyboard shortcut should be enabled.
*/
export const ENABLE_KEYBOARD_SHORTCUTS = 'ENABLE_KEYBOARD_SHORTCUTS';
/**
* The type of the action which signals that a keyboard shortcut should be disabled.
*/
export const DISABLE_KEYBOARD_SHORTCUTS = 'DISABLE_KEYBOARD_SHORTCUTS';

View File

@@ -1,60 +0,0 @@
import { AnyAction } from 'redux';
import {
DISABLE_KEYBOARD_SHORTCUTS,
ENABLE_KEYBOARD_SHORTCUTS,
REGISTER_KEYBOARD_SHORTCUT,
UNREGISTER_KEYBOARD_SHORTCUT
} from './actionTypes';
import { IKeyboardShortcut } from './types';
/**
* Action to register a new shortcut.
*
* @param {IKeyboardShortcut} shortcut - The shortcut to register.
* @returns {AnyAction}
*/
export const registerShortcut = (shortcut: IKeyboardShortcut): AnyAction => {
return {
type: REGISTER_KEYBOARD_SHORTCUT,
shortcut
};
};
/**
* Action to unregister a shortcut.
*
* @param {string} character - The character of the shortcut to unregister.
* @param {boolean} altKey - Whether the shortcut used altKey.
* @returns {AnyAction}
*/
export const unregisterShortcut = (character: string, altKey = false): AnyAction => {
return {
altKey,
type: UNREGISTER_KEYBOARD_SHORTCUT,
character
};
};
/**
* Action to enable keyboard shortcuts.
*
* @returns {AnyAction}
*/
export const enableKeyboardShortcuts = (): AnyAction => {
return {
type: ENABLE_KEYBOARD_SHORTCUTS
};
};
/**
* Action to enable keyboard shortcuts.
*
* @returns {AnyAction}
*/
export const disableKeyboardShortcuts = (): AnyAction => {
return {
type: DISABLE_KEYBOARD_SHORTCUTS
};
};

View File

@@ -1 +0,0 @@
export * from './actions.any';

View File

@@ -1,119 +0,0 @@
import { batch } from 'react-redux';
import { ACTION_SHORTCUT_PRESSED, ACTION_SHORTCUT_RELEASED, createShortcutEvent } from '../analytics/AnalyticsEvents';
import { sendAnalytics } from '../analytics/functions';
import { IStore } from '../app/types';
import { clickOnVideo } from '../filmstrip/actions.web';
import { openSettingsDialog } from '../settings/actions.web';
import { SETTINGS_TABS } from '../settings/constants';
import { registerShortcut } from './actions.any';
import { areKeyboardShortcutsEnabled, getKeyboardShortcuts } from './functions';
import logger from './logger';
import { getKeyboardKey, getPriorityFocusedElement } from './utils';
export * from './actions.any';
/**
* 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.
*
* @returns {Function}
*/
const initGlobalKeyboardShortcuts = () =>
(dispatch: IStore['dispatch']) => {
batch(() => {
dispatch(registerShortcut({
character: '?',
helpDescription: 'keyboardShortcuts.toggleShortcuts',
handler: () => {
sendAnalytics(createShortcutEvent('help'));
dispatch(openSettingsDialog(SETTINGS_TABS.SHORTCUTS, false));
}
}));
// register SPACE shortcut in two steps to insure visibility of help message
dispatch(registerShortcut({
character: ' ',
helpCharacter: 'SPACE',
helpDescription: 'keyboardShortcuts.pushToTalk',
handler: () => {
sendAnalytics(createShortcutEvent('push.to.talk', ACTION_SHORTCUT_RELEASED));
logger.log('Talk shortcut released');
APP.conference.muteAudio(true);
}
}));
dispatch(registerShortcut({
character: '0',
helpDescription: 'keyboardShortcuts.focusLocal',
handler: () => {
dispatch(clickOnVideo(0));
}
}));
Array(9).fill(1)
.forEach((_, index) => {
const num = index + 1;
dispatch(registerShortcut({
character: `${num}`,
// only show help hint for the first shortcut
helpCharacter: num === 1 ? '1-9' : undefined,
helpDescription: num === 1 ? 'keyboardShortcuts.focusRemote' : undefined,
handler: () => {
dispatch(clickOnVideo(num));
}
}));
});
});
};
/**
* Initializes keyboard shortcuts.
*
* @returns {Function}
*/
export const initKeyboardShortcuts = () =>
(dispatch: IStore['dispatch'], getState: IStore['getState']) => {
dispatch(initGlobalKeyboardShortcuts());
window.onkeyup = (e: KeyboardEvent) => {
const state = getState();
const enabled = areKeyboardShortcutsEnabled(state);
const shortcuts = getKeyboardShortcuts(state);
if (!enabled || getPriorityFocusedElement()) {
return;
}
const key = getKeyboardKey(e).toUpperCase();
if (shortcuts.has(key)) {
shortcuts.get(key)?.handler(e);
}
};
window.onkeydown = (e: KeyboardEvent) => {
const state = getState();
const enabled = areKeyboardShortcutsEnabled(state);
if (!enabled) {
return;
}
const focusedElement = getPriorityFocusedElement();
const key = getKeyboardKey(e).toUpperCase();
if (key === ' ' && !focusedElement) {
sendAnalytics(createShortcutEvent('push.to.talk', ACTION_SHORTCUT_PRESSED));
logger.log('Talk shortcut pressed');
APP.conference.muteAudio(false);
} else if (key === 'ESCAPE') {
focusedElement?.blur();
}
};
};

View File

@@ -1,31 +0,0 @@
import { IReduxState } from '../app/types';
/**
* Returns whether or not the keyboard shortcuts are enabled.
*
* @param {Object} state - The redux state.
* @returns {boolean} - Whether or not the keyboard shortcuts are enabled.
*/
export function areKeyboardShortcutsEnabled(state: IReduxState) {
return state['features/keyboard-shortcuts'].enabled;
}
/**
* Returns the keyboard shortcuts map.
*
* @param {Object} state - The redux state.
* @returns {Map} - The keyboard shortcuts map.
*/
export function getKeyboardShortcuts(state: IReduxState) {
return state['features/keyboard-shortcuts'].shortcuts;
}
/**
* Returns the keyboard shortcuts help descriptions.
*
* @param {Object} state - The redux state.
* @returns {Map} - The keyboard shortcuts help descriptions.
*/
export function getKeyboardShortcutsHelpDescriptions(state: IReduxState) {
return state['features/keyboard-shortcuts'].shortcutsHelp;
}

View File

@@ -1,3 +0,0 @@
import { getLogger } from '../base/logging/functions';
export default getLogger('features/keyboard-shortcuts');

View File

@@ -1,39 +0,0 @@
import { IStore } from '../app/types';
import { SET_CONFIG } from '../base/config/actionTypes';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { CAPTURE_EVENTS } from '../remote-control/actionTypes';
import { disableKeyboardShortcuts, enableKeyboardShortcuts } from './actions';
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: any) => {
const { dispatch } = store;
switch (action.type) {
case CAPTURE_EVENTS:
if (action.isCapturingEvents) {
dispatch(disableKeyboardShortcuts());
} else {
dispatch(enableKeyboardShortcuts());
}
return next(action);
case SET_CONFIG: {
const result = next(action);
const state = store.getState();
const { disableShortcuts } = state['features/base/config'];
if (disableShortcuts !== undefined) {
if (disableShortcuts) {
dispatch(disableKeyboardShortcuts());
} else {
dispatch(enableKeyboardShortcuts());
}
}
return result;
}
}
return next(action);
});

View File

@@ -1,72 +0,0 @@
import PersistenceRegistry from '../base/redux/PersistenceRegistry';
import ReducerRegistry from '../base/redux/ReducerRegistry';
import {
DISABLE_KEYBOARD_SHORTCUTS,
ENABLE_KEYBOARD_SHORTCUTS,
REGISTER_KEYBOARD_SHORTCUT,
UNREGISTER_KEYBOARD_SHORTCUT
} from './actionTypes';
import { IKeyboardShortcutsState } from './types';
/**
* The redux subtree of this feature.
*/
const STORE_NAME = 'features/keyboard-shortcuts';
const defaultState = {
enabled: true,
shortcuts: new Map(),
shortcutsHelp: new Map()
};
PersistenceRegistry.register(STORE_NAME, {
enabled: true
});
ReducerRegistry.register<IKeyboardShortcutsState>(STORE_NAME,
(state = defaultState, action): IKeyboardShortcutsState => {
switch (action.type) {
case ENABLE_KEYBOARD_SHORTCUTS:
return {
...state,
enabled: true
};
case DISABLE_KEYBOARD_SHORTCUTS:
return {
...state,
enabled: false
};
case REGISTER_KEYBOARD_SHORTCUT: {
const shortcutKey = action.shortcut.alt ? `:${action.shortcut.character}` : action.shortcut.character;
return {
...state,
shortcuts: new Map(state.shortcuts)
.set(shortcutKey, action.shortcut),
shortcutsHelp: action.shortcut.helpDescription
? new Map(state.shortcutsHelp)
.set(action.shortcut.helpCharacter ?? shortcutKey, action.shortcut.helpDescription)
: state.shortcutsHelp
};
}
case UNREGISTER_KEYBOARD_SHORTCUT: {
const shortcutKey = action.alt ? `:${action.character}` : action.character;
const shortcuts = new Map(state.shortcuts);
shortcuts.delete(shortcutKey);
const shortcutsHelp = new Map(state.shortcutsHelp);
shortcutsHelp.delete(shortcutKey);
return {
...state,
shortcuts,
shortcutsHelp
};
}
}
return state;
});

View File

@@ -1,23 +0,0 @@
export interface IKeyboardShortcut {
// whether or not the alt key must be pressed
alt?: boolean;
// the character to be pressed that triggers the action
character: string;
// the function to be executed when the shortcut is pressed
handler: Function;
// character to be displayed in the help dialog shortcuts list
helpCharacter?: string;
// help description of the shortcut, to be displayed in the help dialog
helpDescription?: string;
}
export interface IKeyboardShortcutsState {
enabled: boolean;
shortcuts: Map<string, IKeyboardShortcut>;
shortcutsHelp: Map<string, string>;
}

View File

@@ -1,75 +0,0 @@
/**
* 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]'
];
/**
* Returns the currently focused element if it is not blacklisted.
*
* @returns {HTMLElement|null} - The currently focused element.
*/
export const getPriorityFocusedElement = (): HTMLElement | null =>
document.querySelector(`:focus:is(${_elementsBlacklist.join(',')})`);
/**
* Returns the keyboard key from a KeyboardEvent.
*
* @param {KeyboardEvent} e - The KeyboardEvent.
* @returns {string} - The keyboard key.
*/
export const getKeyboardKey = (e: KeyboardEvent): string => {
// @ts-ignore
const { altKey, code, key, shiftKey, type, which } = 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 (altKey) {
const replacedKey = code.replace('Key', '');
return `:${replacedKey}`;
}
// 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 key === 'string' && key !== 'Unidentified') {
return key;
}
if (type === 'keypress'
&& ((which >= 32 && which <= 126)
|| (which >= 160 && which <= 255))) {
return String.fromCharCode(which);
}
// try to fallback (0-9A-Za-z and QWERTY keyboard)
switch (which) {
case 27:
return 'Escape';
case 191:
return shiftKey ? '?' : '/';
}
if (shiftKey || type === 'keypress') {
return String.fromCharCode(which);
}
return String.fromCharCode(which).toLowerCase();
};

View File

@@ -1,76 +1,77 @@
// @flow
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';
import { IReduxState, IStore } from '../../app/types';
import { JitsiTrackEvents } from '../../base/lib-jitsi-meet';
import ParticipantView from '../../base/participants/components/ParticipantView.native';
import { getParticipantById, isLocalScreenshareParticipant } from '../../base/participants/functions';
import { trackStreamingStatusChanged } from '../../base/tracks/actions.native';
import { getVideoTrackByParticipant, isLocalVideoTrackDesktop } from '../../base/tracks/functions.native';
import { ITrack } from '../../base/tracks/types';
import { AVATAR_SIZE } from './styles';
/**
* The type of the React {@link Component} props of {@link LargeVideo}.
*/
interface IProps {
type Props = {
/**
* Whether video should be disabled.
*/
_disableVideo: boolean;
_disableVideo: boolean,
/**
* Application's viewport height.
*/
_height: number;
_height: number,
/**
* The ID of the participant (to be) depicted by LargeVideo.
*
* @private
*/
_participantId: string;
_participantId: string,
/**
* The video track that will be displayed in the thumbnail.
*/
_videoTrack?: ITrack;
_videoTrack: ?Object,
/**
* Application's viewport height.
*/
_width: number;
_width: number,
/**
* Invoked to trigger state changes in Redux.
*/
dispatch: IStore['dispatch'];
dispatch: Dispatch<any>,
/**
* Callback to invoke when the {@code LargeVideo} is clicked/pressed.
*/
onClick?: Function;
}
onClick: Function,
};
/**
* The type of the React {@link Component} state of {@link LargeVideo}.
*/
interface IState {
type State = {
/**
* Size for the Avatar. It will be dynamically adjusted based on the
* available size.
*/
avatarSize: number;
avatarSize: number,
/**
* Whether the connectivity indicator will be shown or not. It will be true
* by default, but it may be turned off if there is not enough space.
*/
useConnectivityInfoLabel: boolean;
}
useConnectivityInfoLabel: boolean
};
const DEFAULT_STATE = {
avatarSize: AVATAR_SIZE,
@@ -83,14 +84,14 @@ const DEFAULT_STATE = {
*
* @augments Component
*/
class LargeVideo extends PureComponent<IProps, IState> {
class LargeVideo extends PureComponent<Props, State> {
/**
* Creates new LargeVideo component.
*
* @param {IProps} props - The props of the component.
* @param {Props} props - The props of the component.
* @returns {LargeVideo}
*/
constructor(props: IProps) {
constructor(props: Props) {
super(props);
this.handleTrackStreamingStatusChanged = this.handleTrackStreamingStatusChanged.bind(this);
@@ -107,7 +108,7 @@ class LargeVideo extends PureComponent<IProps, IState> {
*
* @inheritdoc
*/
static getDerivedStateFromProps(props: IProps) {
static getDerivedStateFromProps(props: Props) {
const { _height, _width } = props;
// Get the size, rounded to the nearest even number.
@@ -151,7 +152,7 @@ class LargeVideo extends PureComponent<IProps, IState> {
* @inheritdoc
* @returns {void}
*/
componentDidUpdate(prevProps: IProps) {
componentDidUpdate(prevProps: Props) {
// TODO: after converting this component to a react function component,
// use a custom hook to update local track streaming status.
const { _videoTrack, dispatch } = this.props;
@@ -199,7 +200,7 @@ class LargeVideo extends PureComponent<IProps, IState> {
* @param {JitsiTrackStreamingStatus} streamingStatus - The updated track streaming status.
* @returns {void}
*/
handleTrackStreamingStatusChanged(jitsiTrack: any, streamingStatus: string) {
handleTrackStreamingStatusChanged(jitsiTrack, streamingStatus) {
this.props.dispatch(trackStreamingStatusChanged(jitsiTrack, streamingStatus));
}
@@ -239,11 +240,11 @@ class LargeVideo extends PureComponent<IProps, IState> {
*
* @param {Object} state - Redux state.
* @private
* @returns {IProps}
* @returns {Props}
*/
function _mapStateToProps(state: IReduxState) {
function _mapStateToProps(state) {
const { participantId } = state['features/large-video'];
const participant = getParticipantById(state, participantId ?? '');
const participant = getParticipantById(state, participantId);
const { clientHeight: height, clientWidth: width } = state['features/base/responsive-ui'];
const videoTrack = getVideoTrackByParticipant(state, participant);
let disableVideo = false;
@@ -257,7 +258,7 @@ function _mapStateToProps(state: IReduxState) {
return {
_disableVideo: disableVideo,
_height: height,
_participantId: participantId ?? '',
_participantId: participantId,
_videoTrack: videoTrack,
_width: width
};

View File

@@ -6,7 +6,7 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import ChatInputBar from '../../../chat/components/native/ChatInputBar';
import MessageContainer from '../../../chat/components/native/MessageContainer';
import AbstractLobbyScreen, {
IProps as AbstractProps,
Props as AbstractProps,
_mapStateToProps as abstractMapStateToProps
} from '../AbstractLobbyScreen';
@@ -30,7 +30,6 @@ class LobbyChatScreen extends
return (
<JitsiScreen style = { styles.lobbyChatWrapper }>
{/* @ts-ignore */}
<MessageContainer messages = { _lobbyChatMessages } />
<ChatInputBar onSend = { this._onSendMessage } />
</JitsiScreen>

View File

@@ -1,8 +1,7 @@
import React from 'react';
import { Text, TextStyle, View, ViewStyle } from 'react-native';
import React, { ReactElement } from 'react';
import { Text, View } from 'react-native';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { getConferenceName } from '../../../base/conference/functions';
import { translate } from '../../../base/i18n/functions';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
@@ -21,28 +20,29 @@ import { preJoinStyles } from '../../../prejoin/components/native/styles';
import AudioMuteButton from '../../../toolbox/components/AudioMuteButton';
import VideoMuteButton from '../../../toolbox/components/VideoMuteButton';
import AbstractLobbyScreen, {
IProps as AbstractProps,
Props as AbstractProps,
_mapStateToProps as abstractMapStateToProps } from '../AbstractLobbyScreen';
import styles from './styles';
interface IProps extends AbstractProps {
type Props = AbstractProps & {
/**
* The current aspect ratio of the screen.
*/
_aspectRatio: Symbol;
_aspectRatio: Symbol,
/**
* The room name.
*/
_roomName: string;
_roomName: string
}
/**
* Implements a waiting screen that represents the participant being in the lobby.
*/
class LobbyScreen extends AbstractLobbyScreen<IProps> {
class LobbyScreen extends AbstractLobbyScreen<Props> {
/**
* Implements {@code PureComponent#render}.
*
@@ -70,7 +70,7 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
style = { contentWrapperStyles }>
<BrandingImageBackground />
<View style = { largeVideoContainerStyles }>
<View style = { preJoinStyles.displayRoomNameBackdrop as ViewStyle }>
<View style = { preJoinStyles.displayRoomNameBackdrop }>
<Text
numberOfLines = { 1 }
style = { preJoinStyles.preJoinRoomName }>
@@ -79,7 +79,7 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
</View>
<LargeVideo />
</View>
<View style = { contentContainerStyles as ViewStyle }>
<View style = { contentContainerStyles }>
{ this._renderToolbarButtons() }
{ this._renderContent() }
</View>
@@ -87,6 +87,32 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
);
}
_getScreenTitleKey: () => string;
_onAskToJoin: () => void;
_onCancel: () => boolean;
_onChangeDisplayName: Object => void;
_onChangeEmail: Object => void;
_onChangePassword: Object => void;
_onEnableEdit: () => void;
_onJoinWithPassword: () => void;
_onSwitchToKnockMode: () => void;
_onSwitchToPasswordMode: () => void;
_renderContent: () => ReactElement;
_renderToolbarButtons: () => ReactElement;
_onNavigateToLobbyChat: () => void;
/**
* Navigates to the lobby chat screen.
*
@@ -111,7 +137,7 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
<LoadingIndicator
color = { BaseTheme.palette.icon01 }
style = { styles.loadingIndicator } />
<Text style = { styles.joiningMessage as TextStyle }>
<Text style = { styles.joiningMessage }>
{ this.props.t('lobby.joiningMessage') }
</Text>
{ this._renderStandardButtons() }
@@ -157,6 +183,7 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
return (
<Input
autoCapitalize = 'none'
autoCompleteType = 'off'
customStyles = {{ input: styles.customInput }}
error = { _passwordJoinFailed }
onChange = { this._onChangePassword }
@@ -198,7 +225,7 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
*/
_renderToolbarButtons() {
return (
<View style = { preJoinStyles.toolboxContainer as ViewStyle }>
<View style = { preJoinStyles.toolboxContainer }>
<AudioMuteButton
styles = { preJoinStyles.buttonStylesBorderless } />
<VideoMuteButton
@@ -217,7 +244,7 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
const { displayName } = this.state;
return (
<View style = { styles.formWrapper as ViewStyle }>
<View style = { styles.formWrapper }>
{
_knocking && _isLobbyChatActive
&& <Button
@@ -255,14 +282,14 @@ class LobbyScreen extends AbstractLobbyScreen<IProps> {
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {IProps} ownProps - The own props of the component.
* @param {Props} ownProps - The own props of the component.
* @returns {{
* _aspectRatio: Symbol
* }}
*/
function _mapStateToProps(state: IReduxState) {
function _mapStateToProps(state: Object, ownProps: Props) {
return {
...abstractMapStateToProps(state),
...abstractMapStateToProps(state, ownProps),
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
_roomName: getConferenceName(state)
};

View File

@@ -1,5 +1,6 @@
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export default {
lobbyChatWrapper: {

View File

@@ -82,7 +82,6 @@ const RootNavigationContainer = ({ dispatch, isWelcomePageAvailable }: IProps) =
name = { screen.welcome.main }
options = { welcomeScreenOptions } />
<RootStack.Screen
// @ts-ignore
component = { DialInSummary }
name = { screen.dialInSummary }
options = { dialInSummaryScreenOptions } />

View File

@@ -10,7 +10,7 @@ export const lobbyNavigationContainerRef = React.createRef<NavigationContainerRe
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function navigate(name: string, params?: Object) {
export function navigate(name: string, params: Object) {
return lobbyNavigationContainerRef.current?.navigate(name, params);
}

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