Compare commits

...

72 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
ff78bfb62f fix(ios) fix node path in scripts 2025-01-16 17:11:36 +02:00
Saúl Ibarra Corretgé
1827610fc1 fix(subtitles) fix skipping transcription messages
If we are not requesting any transcription, `language` will be `null` so
take that into consideration when checking if we should stop processing
a transcription message after firing the API event.

Fixes: https://github.com/jitsi/docker-jitsi-meet/issues/1997
2025-01-16 13:21:51 +01:00
Hristo Terezov
0ae8051fb4 fix(config): Allow only enableMediaOnPromote from visitors config to be overriden. 2025-01-15 15:50:48 -06:00
damencho
1b7668bbdc feat(dialog): Adds a print when opening and hiding dialogs. 2025-01-15 10:49:20 -06:00
damencho
4c37e0d761 feat(speakerstats): Shows a tooltip for participants count. 2025-01-15 10:49:20 -06:00
damencho
a6ff652a03 feat(visitors): Start shortening the number above 1000. 2025-01-15 10:49:20 -06:00
damencho
17f34878db feat(visitors): Renames visitors/obeserver to viewers.
Changes only strings visible in the UI.
2025-01-15 10:49:20 -06:00
Saúl Ibarra Corretgé
37856e0fdb fix(external_api) drop legacy constructor arguments
The new format (using an options object) was introduced 7 years ago.
It's about time.
2025-01-14 15:38:11 +01:00
Edgars Voroboks
7c1de52f6a fix(lang): Update Latvian language translation 2025-01-14 06:20:12 -06:00
Jaya Allamsetty
9e3438696b chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1895.0.0+7de7d9aa...v1897.0.0+283af7ac
2025-01-13 12:46:47 -05:00
Andrei Gavrilescu
9c23cc0b70 feat(rnnoise): update rnnoise-wasm version (#15478)
* update rnnoise-wasm version

* update expected worklet size
2025-01-13 14:11:32 +02:00
Hristo Terezov
af8157bc06 fix(configWhitelist): Add p2p.mobileScreenshareCodec 2025-01-10 17:38:12 -06:00
Дамян Минков
c21f84c15a feat(tests): Adds follow-me and invite dialog test. (#15476)
* feat(tests): Adds follow-me test.

* feat(tests): Adds invite dialog test.

* squash: fix lint.
2025-01-10 16:47:25 -06:00
Saúl Ibarra Corretgé
1edf88e744 fix(lint) tame linter 2025-01-10 20:09:11 +01:00
Saúl Ibarra Corretgé
bad75b488e chore(deps) update eslint
Required for compatibility with new TypeScript plugins.
2025-01-10 20:09:11 +01:00
Saúl Ibarra Corretgé
5bb3ba71d0 chore(helpers) drop custom createDeferred() for Promise.withResolvers() 2025-01-10 20:09:11 +01:00
Saúl Ibarra Corretgé
4e0001c9af fix(polls) limit maximum amount of answers 2025-01-10 13:51:30 +01:00
Дамян Минков
ada6150971 feat(tests): Adds dial-in test. (#15470)
* feat(tests): Adds dial-in test.

* feat(tests): Adds fake dial-in test.

* squash: switch to performance.now().
2025-01-10 06:28:53 -06:00
damencho
62d1ee606a feat(tests): Adds desktop sharing test. 2025-01-09 18:29:47 -06:00
simplify123
1e1de6acf0 lang: Update main-zhCN.json (#15426)
main-zhCN.json 
line 413 missing:
"sessTerminatedReason": "会议已经结束",
2025-01-09 15:04:23 -06:00
damencho
e1e0fba4ff fix(readme): Fixes badge icon.
Fixes #15464
2025-01-09 15:04:02 -06:00
Jaya Allamsetty
97be3fa399 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1893.0.0+496b64a7...v1895.0.0+7de7d9aa
2025-01-09 15:22:18 -05:00
damencho
fbdee85c4b fix(readme): Fixes badge icon and f-droid link.
Fixes #15464
Fixes #15465
2025-01-09 12:38:11 -06:00
Bhavik Arora
b71b4c8c45 fix(video-menu): Hide "Mute all" option when all participants are moderators (#15461)
* Fixed exception for visibility state of mute all button

* Updated changes as per review: shifted conditional to the toggle

* chore: fix eslint warnings and errors

---------

Co-authored-by: Bhavik Arora <arorabhavik1001>
2025-01-09 10:21:28 -06:00
Florian
a8d123ede4 fix(lang) fix typo in unsecure room name warning (#15468) 2025-01-09 17:01:23 +01:00
Hristo Terezov
fb8e451e2e feat(customParticipantButton): metrics 2025-01-08 11:11:21 -06:00
damencho
5dd7944bdb fix(connection): Adds default tenant value on error.
Avoids undefined error.
2025-01-08 08:36:35 -06:00
Jaya Allamsetty
48732c6784 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1890.0.0+144b0cab...v1893.0.0+496b64a7
2025-01-07 21:45:03 -06:00
Hristo Terezov
97146ed8a7 fix(configWhitelist): Remove some props. 2025-01-07 15:32:08 -06:00
Дамян Минков
78b17c8d17 feat(tests): Adds self view, display name and end conference tests. (#15432)
* feat(tests): Use shorter display names for screenshots.

* feat(tests): Adds self view tests.

* feat(tests): Adds display name test.

* feat(tests): Adds end conference test.
2025-01-07 14:55:43 -06:00
Saúl Ibarra Corretgé
9732d1be86 fix(polls) improve resilience to bogus data 2025-01-07 16:06:10 +01:00
Hristo Terezov
a836187620 feat(URL-overrides): Add metrics. 2025-01-06 09:35:36 -06:00
Wilson Furtado
a0cad4c595 fix(notification) add tabIndex to close Icon in the notification Item 2025-01-06 10:13:06 +01:00
Wilson Furtado
19b9eac84a fix(input) fix content overlapping when clearable icon is present 2025-01-06 10:12:14 +01:00
Wilson Furtado
068e33fc0f fix(toolbar): Removed unnecessary re render of the reactions component (#15433)
Added usememo to remove unnecessary re render. Fixes #15434.
2024-12-31 10:04:47 -06:00
Wilson Furtado
00c6bee2fd feat(welcome-page): added hover fill to trash icon (#15431)
Fixes #15430.
2024-12-30 16:11:43 -06:00
Wilson Furtado
748ead7e13 feat: Added hover styles and added tabIndex to the meeting list container instead of the meeting item header (#15429)
* added hover styles and added tabIndex to the container instead of the meeting heading

* lint error fix
2024-12-30 14:05:56 -06:00
damencho
174c4418fb fix(prosody): Adds another condition to the filter. 2024-12-26 12:12:53 -06:00
Damien Fetis
64494cab81 lang: Update missing french labels (#15422) 2024-12-23 18:28:35 -06:00
damencho
50d0092e30 fix(shared-video): Remove disable button action from web. 2024-12-23 07:46:25 -06:00
damencho
fdbd7239ff fix(shared-video): Gets from info from the incoming presence.
Ignore using from field send in attributes of the command.
2024-12-23 07:46:25 -06:00
Дамян Минков
a30958ab23 feat(tests): Adds chat panel and codec selection tests. (#15416)
* fix(tests): Another attempt to fix Firefox excludes.

Drawback is that it will be a little bit slow.

* feat(tests): Adds chatPanel tests.

* feat(tests): Adds codec selection tests.
2024-12-20 17:04:07 -06:00
damencho
aca55172e4 fix(config): Fixes wrong file location. 2024-12-20 11:37:48 -06:00
Дамян Минков
c6cce9253c feat(tests): Adds breakout tests. (#15414)
* feat(tests): Introduces BasePageObject.

* fix(tests): Use wdio aria selector where possible.

* fix(tests): Correct test exclusion for Firefox.

* fix(tests): Rearrange code.

* feat(tests): Adds breakout tests.
2024-12-20 06:17:49 -06:00
Saúl Ibarra Corretgé
c23684e11c fix(devices) filter out Zoom audio device
I haven't seen any particular problem with it, but it's of no use
anyway...
2024-12-20 07:58:48 +01:00
luzpaz
30595584a3 chore(typos) fix various typos
Found via `codespell -q 3 -S "*.svg,./lang" -L anser,bu,dialin,goup,miliseconds,nd,vew`
2024-12-20 07:58:31 +01:00
Saúl Ibarra Corretgé
3d30489cc4 chore(deps) update TypeScript 2024-12-19 15:04:16 +01:00
Дамян Минков
7288a6dce4 feat(tests): AVModeration tests. (#15408)
* feat(tests): Adds option to skip suite.

* fix(tests): Rename context to ctx to avoid clashing mocha's one.

* feat(tests): Moves room name generation in hooks.

Move also the proxy connection in the hooks.

* fix(tests): Avatar checks when using a token.

Token has its avatar so we skip the token for avatar tests.

* feat(tests): Renames avatars to drop Test from name.

* feat(tests): Updates dependencies.

* feat(tests): Fix end test log.

* feat(tests): AVModeration tests.
2024-12-19 07:12:14 -06:00
Hristo Terezov
697c41bffa fix(config-whitelist): remove p2p.stunServers 2024-12-18 18:39:22 -06:00
teamogy-team
0361830122 fix(lang) update Czech translation 2024-12-18 12:17:11 +01:00
damencho
6bb984acc8 fix(polls): Returns an error on duplicate poll. 2024-12-17 16:44:12 -06:00
damencho
938d112f6b fix(visitors): Fix a check that can result missing main participants. 2024-12-17 10:59:26 -06:00
Дамян Минков
d8cca36181 fix(shared-video): Removes disable pointer for non moderators. (#15399)
* fix(shared-video): Removes disable pointer for non moderators.

It fixes an issue where people can see ads from YouTube, allowing them to click the Skip Ad button. If you by mistake pause, the next time sync will unpuase it.

* squash: Remove unused.
2024-12-17 06:20:09 -06:00
Jaya Allamsetty
c9add0a9ef ref(tracks) Remove gUM option that is no longer supported 2024-12-16 22:41:54 -05:00
Saúl Ibarra Corretgé
aabc50da60 fix(rnsdk) specify the RN version we support
Our SDK is only really tested with a single RN version, make sure to put
that in peerDependencies.

In addition, drop the peer dependency on @react-native/metro-config
since our project doesn't use it directly.
2024-12-16 17:53:14 +02:00
Calin-Teodor
4aa4382d2b dep(@react-native-clipboard/clipboard): update to 1.14.3 2024-12-16 10:48:32 +02:00
Calin-Teodor
6bf1b46609 chore(react-native): update CocoaPods 2024-12-16 10:47:54 +02:00
shooding
9d7237dc24 fix(transcript) fix matching languages with variants
* fixtranscript results from whisper may have json.language="zh-TW" also requesting transcriptions language could be "zh-CN". Use _getPrimaryLanguageCode func to compare only the main lang code.

* fix: lint issue
2024-12-16 09:02:57 +01:00
Saúl Ibarra Corretgé
3745c19857 feat(overlay) drop permissions overlay
It covers the entire meeting and it's not helpful. A different version
of the same concept might be implemented using less intrusive UX, like a
modal.
2024-12-13 10:55:10 -06:00
damencho
cd6419d826 feat(ga): Adds a syntax check for config.js. 2024-12-13 10:34:39 -06:00
Saúl Ibarra Corretgé
a05e8fd2ef feat(build) use Node 22, the new LTS 2024-12-13 15:55:58 +01:00
Mejans
239e5743dd fix(lang) update Occitan locale (#15389) 2024-12-13 09:35:57 +01:00
Robert Oanta
01a731ab4d feat(prosody): extend jigasi kick endpoint to work for any participant (#15387)
* feat(prosody): extend jigasi kick endpoint to work for any participant

* apply review

* squash: Fix UI when there is no actor of the kick.

---------

Co-authored-by: damencho <damencho@jitsi.org>
2024-12-12 11:03:26 -06:00
Edgars Voroboks
3d56538c69 fix(lang): Update Latvian language translation 2024-12-12 10:52:59 -06:00
Дамян Минков
2dc135b80f feat(tests): Adds avatar test. (#15382)
* feat(tests): Adds join options.

* fix(tests): Fix opening tests by default with tenant.

* fix(tests): Renames a method.

* fix(tests): Moves a method from filmstrip to participants pane.

* fix(tests): Adds ok button to base dialog.

* fix(tests): Adds missing checks for using iframe API.

* feat(tests): Prettify the result html on error.

* fix(tests): Fixes checking when not in room.

* fix(tests): Adds profile button to toolbar.

* fix(tests): Adds avatar test.

* fix(tests): Fix all execute methods and await.

* fix(tests): Fix avatar checks.
2024-12-12 08:29:15 -06:00
simon
4e81d4461b fix(lang) update German translation 2024-12-12 13:52:46 +01:00
Mihaela Dumitru
68b16d7bc8 fix(config) hide ui label for the recording start notification and send event (#15385) 2024-12-12 11:31:38 +02:00
Hristo Terezov
45f6550d30 fix(configWhitelist): Remove hiddenDomain and restrict dropbox options to key only. (#15381) 2024-12-11 13:08:12 -06:00
Calin-Teodor
5c65446ef4 feat(toolbox/native): update CustomOptionButton props 2024-12-11 17:44:01 +02:00
Saúl Ibarra Corretgé
eee58b2945 fix(whiteboard) fix disabling button 2024-12-11 13:30:21 +01:00
Jaya Allamsetty
c7cfb5218d fix(config): Add missing config options 2024-12-10 16:34:53 -05:00
Дамян Минков
b9017176a8 feat(tests): Grid, FF and iframeAPI tests. (#15372)
* fix(tests): Fix include/excludes of tests based on participant count.

* feat(tests): Define context globally.

The context is being created on every new suite in before hook.

* feat(tests): Drop extra char in room name.

* feat(tests): Adds an option to load iframeAPI.

* feat(tests): Adds some ts types.

* fix(tests): Fix iframeAPI helper with events received too early.

* fix(tests): Fix iframeAPI helper detecting own role changed.

* feat(tests): Adds run script to start tests with local dev server.

* feat(tests): Adds participants iframeAPI tests.

* feat(tests): Updates wdio dependencies.

* feat: Adds grid config.

* feat: Simplify iframeAPI.

Drop URL params.

* feat: Adds tenant to iframeAPI.

* feat: Adds firefox target.

Certain tests are disable as not supported on FF. Missing upload file function for iframeAPI helper. Missing option to set audio file as a mic source.

* fix: Fix using tenant from baseUrl.

* feat: Adds audio only tests.

* feat: Adds option to generate tokens for the moderator.

* feat: Adds option to test and webhooks.

* fix: Improve error stack trace on error.

* fix: Address comments.

* fix: Fix test exclusion for FF.

* squash: Revert the strophe change and add a comment.
2024-12-10 11:22:44 -06:00
207 changed files with 11280 additions and 3409 deletions

View File

@@ -53,6 +53,8 @@ jobs:
npm -v
- run: npm install
- run: make
- name: Check config.js syntax
run: node config.js
android-rn-bundle-build:
name: Build mobile bundle (Android)
runs-on: macos-15

2
.nvmrc
View File

@@ -1 +1 @@
20
22

View File

@@ -34,7 +34,7 @@ mobile apps:
| Android | Android (F-Droid) | iOS |
|:-:|:-:|:-:|
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
If you are feeling adventurous and want to get an early scoop of the features as they are being
developed you can also sign up for our open beta testing here:

View File

@@ -42,7 +42,7 @@ public class JitsiMeetActivityDelegate {
/**
* Tells whether or not the permissions request is currently in progress.
*
* @return {@code true} if the permssions are being requested or {@code false} otherwise.
* @return {@code true} if the permissions are being requested or {@code false} otherwise.
*/
static boolean arePermissionsBeingRequested() {
return permissionListener != null;

View File

@@ -155,7 +155,6 @@ import {
NOTIFICATION_TIMEOUT_TYPE
} from './react/features/notifications/constants';
import { isModerationNotificationDisplayed } from './react/features/notifications/functions';
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay/actions';
import { suspendDetected } from './react/features/power-monitor/actions';
import { initPrejoin, isPrejoinPageVisible } from './react/features/prejoin/functions';
import { disableReceiver, stopReceiver } from './react/features/remote-control/actions';
@@ -435,15 +434,6 @@ export default {
requestedVideo = true;
}
if (!config.disableInitialGUM) {
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
browserName =>
APP.store.dispatch(
mediaPermissionPromptVisibilityChanged(true, browserName))
);
}
let tryCreateLocalTracks = Promise.resolve([]);
// On Electron there is no permission prompt for granting permissions. That's why we don't need to
@@ -452,8 +442,7 @@ export default {
const timeout = browser.isElectron() ? 15000 : 60000;
const audioOptions = {
devices: [ MEDIA_TYPE.AUDIO ],
timeout,
firePermissionPromptIsShownEvent: true
timeout
};
// Spot uses the _desktopSharingSourceDevice config option to use an external video input device label as
@@ -488,8 +477,7 @@ export default {
} else if (requestedAudio || requestedVideo) {
tryCreateLocalTracks = APP.store.dispatch(createInitialAVTracks({
devices: initialDevices,
timeout,
firePermissionPromptIsShownEvent: true
timeout
}, recordTimeMetrics)).then(({ tracks, errors: pErrors }) => {
Object.assign(errors, pErrors);
@@ -497,15 +485,6 @@ export default {
});
}
// Hide the permissions prompt/overlay as soon as the tracks are created. Don't wait for the connection to
// be established, as in some cases like when auth is required, connection won't be established until the user
// inputs their credentials, but the dialog would be overshadowed by the overlay.
tryCreateLocalTracks.then(tracks => {
APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false));
return tracks;
});
return {
tryCreateLocalTracks,
errors

View File

@@ -459,7 +459,7 @@ var config = {
// // Translation languages.
// // Available languages can be found in
// // ./src/react/features/transcribing/translation-languages.json.
// // ./lang/translation-languages.json.
// translationLanguages: ['en', 'es', 'fr', 'ro'],
// // Important languages to show on the top of the language list.
@@ -509,12 +509,17 @@ var config = {
// videoQuality: {
//
// // Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264' ],
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264', 'AV1' ],
//
// // Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',
// mobileScreenshareCodec: 'VP8',
//
// // Enables the adaptive mode in the client that will make runtime adjustments to selected codecs and received
// // videos for a better user experience. This mode will kick in only when CPU overuse is reported in the
// // WebRTC statistics for the outbound video streams.
// enableAdaptiveMode: false,
//
// // Codec specific settings for scalability modes and max bitrates.
// av1: {
// maxBitratesVideo: {
@@ -902,7 +907,7 @@ var config = {
// Overrides the buttons displayed in the main toolbar. Depending on the screen size the number of displayed
// buttons varies from 2 buttons to 8 buttons. Every array in the mainToolbarButtons array will replace the
// corresponding default buttons configuration matched by the number of buttons specified in the array. Arrays with
// more than 8 buttons or less then 2 buttons will be ignored. When there there isn't an override for a cerain
// more than 8 buttons or less then 2 buttons will be ignored. When there there isn't an override for a certain
// configuration (for example when 3 buttons are displayed) the default jitsi-meet configuration will be used.
// The order of the buttons in the array is preserved.
// mainToolbarButtons: [
@@ -1615,6 +1620,16 @@ var config = {
// For external entities (e. g. email), the localStorage key holding the token value for directory authentication
// peopleSearchTokenLocation: "mytoken",
// Options related to visitors.
// visitors: {
// // Starts audio/video when the participant is promoted from visitor.
// enableMediaOnPromote: {
// audio: true,
// video: true
// },
// },
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold
@@ -1730,7 +1745,7 @@ var config = {
// 'notify.participantsWantToJoin', // shown when lobby is enabled and participants request to join meeting
// 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
// 'notify.passwordSetRemotely', // shown when a password has been set remotely
// 'notify.raisedHand', // shown when a partcipant used raise hand,
// 'notify.raisedHand', // shown when a participant used raise hand,
// 'notify.screenShareNoAudio', // shown when the audio could not be shared for the selected screen
// 'notify.screenSharingAudioOnlyTitle', // shown when the best performance has been affected by screen sharing
// 'notify.selfViewTitle', // show "You can always un-hide the self-view from settings"
@@ -1761,7 +1776,7 @@ var config = {
// disableFilmstripAutohiding: false,
// filmstrip: {
// // Disable the vertical/horizonal filmstrip.
// // Disable the vertical/horizontal filmstrip.
// disabled: false,
// // Disables user resizable filmstrip. Also, allows configuration of the filmstrip
// // (width, tiles aspect ratios) through the interfaceConfig options.

View File

@@ -19,11 +19,11 @@
flex-direction: column;
.description {
color: #2f3237;
font-size: 14px;
line-height: 18px;
margin-bottom: 16px;
max-width: 436px;
color: #2f3237;
font-size: 14px;
line-height: 18px;
margin-bottom: 16px;
max-width: 436px;
}
}
@@ -127,7 +127,8 @@
cursor: pointer;
}
&.with-click-handler:hover {
&.with-click-handler:hover,
&.with-click-handler:focus {
background-color: #c7ddff;
}
@@ -155,14 +156,22 @@
margin-right: 16px;
position: absolute;
&> svg {
&>svg {
fill: #0074e0;
}
}
.item:hover, .item:focus, .item:focus-within {
.item:hover,
.item:focus,
.item:focus-within {
.delete-meeting {
display: block;
}
.delete-meeting:hover {
&>svg {
fill: #4687ED;
}
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -227,7 +227,7 @@ var interfaceConfig = {
/**
* Specify custom URL for downloading f droid app.
*/
// MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/',
// MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/packages/org.jitsi.meet/',
// Connection indicators (
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,

View File

@@ -1,2 +1,6 @@
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
. ${THIS_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh
export NODE_BINARY=$(command -v node)
export ENTRY_FILE="${PROJECT_DIR}/../../index.ios.js"

View File

@@ -1726,7 +1726,7 @@ PODS:
- React
- RNCAsyncStorage (1.23.1):
- React-Core
- RNCClipboard (1.14.1):
- RNCClipboard (1.14.3):
- React-Core
- RNDefaultPreference (1.4.4):
- React-Core
@@ -2197,7 +2197,7 @@ SPEC CHECKSUMS:
ReactCommon: 6a952e50c2a4b694731d7682aaa6c79bc156e4ad
RNCalendarEvents: 7e65eb4a94f53c1744d1e275f7fafcfaa619f7a3
RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c
RNCClipboard: 0a720adef5ec193aa0e3de24c3977222c7e52a37
RNCClipboard: 2821ac938ef46f736a8de0c8814845dde2dcbdfb
RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31
RNDeviceInfo: 02ea8b23e2280fa18e00a06d7e62804d74028579
RNGestureHandler: 939f21fabf5d45a725c0bf175eb819dd25cf2e70
@@ -2211,4 +2211,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 7ed908101076ca2c595b633c34648bcfc7e98614
COCOAPODS: 1.16.1
COCOAPODS: 1.16.2

View File

@@ -69,7 +69,7 @@ platform :ios do
end
)
# Inrement the build number by 1
# Increment the build number by 1
increment_build_number(
build_number: Time.now.to_i,
xcodeproj: "app/app.xcodeproj"

File diff suppressed because it is too large Load Diff

View File

@@ -1388,7 +1388,7 @@
"tileViewToggle": "Kachelansicht ein-/ausschalten",
"toggleCamera": "Kamera wechseln",
"unmute": "Stummschaltung aufheben",
"videoSettings": "Kameraeinstellungen",
"videoSettings": "Kamera-Einstellungen",
"videomute": "Kamera stoppen",
"videomuteGUMPending": "Verbinde Ihre Kamera",
"videounmute": "Kamera einschalten"

View File

@@ -334,6 +334,7 @@
"kickParticipantButton": "Expulser",
"kickParticipantDialog": "Êtes-vous sûr(e) de vouloir expulser ce participant ?",
"kickParticipantTitle": "Expulser ce participant ?",
"kickSystemTitle": "Oups ! Vous avez été expulsé de la réunion",
"kickTitle": "Oups ! vous avez été expulsé(e) par {{participantDisplayName}}",
"linkMeeting": "Relier la conférence",
"linkMeetingTitle": "Relier la conférence à Salesforce",
@@ -439,7 +440,10 @@
"shareScreenWarningD2": "vous devez arrêter le partage d'audio, démarrer le partage d'écran et cocher l'option \"Partager l'audio\".",
"shareScreenWarningH1": "Si vous voulez partager uniquement votre écran:",
"shareScreenWarningTitle": "Vous devez cesser de partager votre audio avant de partager votre écran",
"shareVideoConfirmPlay": "Vous êtes sur le point d'ouvrir un site web externe. Voulez-vous continuer ?",
"shareVideoConfirmPlayTitle": "{{name}} a partagé une vidéo avec vous.",
"shareVideoLinkError": "Veuillez renseigner un lien de diffusion vidéo fonctionnel.",
"shareVideoLinkStopped": "La vidéo de {{name}} a été arrêtée.",
"shareVideoTitle": "Partager une vidéo",
"shareYourScreen": "Partager votre écran",
"shareYourScreenDisabled": "Le partage d'écran est désactivé.",
@@ -638,6 +642,7 @@
"on": "En direct",
"onBy": "{{name}} a démarré la diffusion en direct",
"pending": "Lancement du direct…",
"policyError": "Vous avez essayé de démarrer une diffusion en direct trop rapidement. Veuillez réessayer plus tard !",
"serviceName": "Service de diffusion en direct",
"sessionAlreadyActive": "Cette session est déjà en cours d'enregistrement ou de diffusion.",
"signIn": "Se connecter avec Google",
@@ -736,6 +741,7 @@
"connectedOneMember": "{{name}} a rejoint la réunion",
"connectedThreePlusMembers": "{{name}} et {{count}} autres personnes ont rejoint la réunion",
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
"connectionFailed": "Connexion échouée. Veuillez réessayer plus tard !",
"dataChannelClosed": "Qualité vidéo dégradée",
"dataChannelClosedDescription": "Le canal de communication avec le Bridge a été interrompu, la qualité vidéo se trouve limitée à sa valeur la plus faible.",
"dataChannelClosedDescriptionWithAudio": "Le canal de pont est fermé, ce qui peut entraîner des perturbations de l'audio et de la vidéo.",
@@ -750,6 +756,9 @@
"gifsMenu": "GIPHY",
"groupTitle": "Notifications",
"hostAskedUnmute": "Le modérateur souhaite vous donner la parole",
"invalidTenant": "Tenant invalide",
"invalidTenantHyphenDescription": "Le tenant que vous utilisez est invalide (commence ou se termine par '-').",
"invalidTenantLengthDescription": "Le tenant que vous utilisez est trop long.",
"invitedOneMember": "{{name}} a été invité(e)",
"invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités",
"invitedTwoMembers": "{{first}} et {{second}} ont été invités",
@@ -786,6 +795,7 @@
"newDeviceAction": "Utiliser",
"newDeviceAudioTitle": "Nouveau périphérique audio détecté",
"newDeviceCameraTitle": "Nouvelle caméra détectée",
"nextToSpeak": "Vous êtes le prochain à prendre la parole",
"noiseSuppressionDesktopAudioDescription": "La suppression de bruit ne peut pas être activée en même temps que la partage audio du système, veuillez le désactiver et réessayer.",
"noiseSuppressionFailedTitle": "Échec du démarrage de la suppression de bruit",
"noiseSuppressionStereoDescription": "La suppression de bruit d’une source stéréo n’est pas encore supportée.",
@@ -820,6 +830,7 @@
"videoUnmuteBlockedDescription": "Le rétablissement de la vidéo a été bloqué temporairement en raison de limites système.",
"videoUnmuteBlockedTitle": "Rétablissement de la caméra bloqué !",
"viewLobby": "Voir la salle d'attente",
"viewParticipants": "Voir les participants",
"viewVisitors": "Voir les visiteurs",
"waitingParticipants": "{{waitingParticipants}} personnes",
"waitingVisitors": "Visiteurs en attente dans la file : {{waitingVisitors}}",
@@ -839,6 +850,8 @@
"breakoutRooms": "Salles annexes",
"goLive": "Passer en direct",
"invite": "Inviter quelqu'un",
"lowerAllHands": "Abaisser toutes les mains",
"lowerHand": "Abaisser la main",
"moreModerationActions": "Options de modération supplémentaires",
"moreModerationControls": "Options de modération supplémentaires",
"moreParticipantOptions": "Options supplémentaires pour les participants",
@@ -875,6 +888,7 @@
"submit": "Envoyer"
},
"by": "Par {{ name }}",
"closeButton": "Fermer le sondage",
"create": {
"addOption": "Ajouter une option",
"answerPlaceholder": "Option {{index}}",
@@ -914,9 +928,11 @@
"configuringDevices": "Configuration des appareils…",
"connectedWithAudioQ": "Êtes-vous connecté avec le microphone ?",
"connection": {
"failed": "Le test de connexion a échoué !",
"good": "Votre connexion Internet est bonne !",
"nonOptimal": "Votre connexion n'est pas optimale",
"poor": "Vous avez une mauvaise connexion"
"poor": "Vous avez une mauvaise connexion",
"running": "Exécution du test de connexion…"
},
"connectionDetails": {
"audioClipping": "Attendez vous à ce que votre son soit coupé.",
@@ -925,6 +941,7 @@
"goodQuality": "Impressionnant ! La qualité de vos médias sera excellente",
"noMediaConnectivity": "Nous n'avons pas pu trouver un moyen d'établir une connectivité multimédia pour ce test. Cela est généralement causé par un pare-feu ou un NAT.",
"noVideo": "Attendez vous à ce que votre qualité vidéo soit très mauvaise.",
"testFailed": "Le test de connexion a rencontré des problèmes inattendus, mais cela pourrait ne pas affecter votre expérience.",
"undetectable": "Si vous ne parvenez toujours pas à passer des appels dans le navigateur, nous vous recommandons de vous assurer que vos haut-parleurs, microphone et caméra sont correctement configurés, que vous avez accordé à votre navigateur les droits d'utiliser votre microphone et votre caméra et que la version de votre navigateur est à jour. Si vous rencontrez toujours des difficultés pour appeler, vous devez contacter le développeur de l'application Web.",
"veryPoorConnection": "Attendez vous à ce que la qualité de votre appel soit très mauvaise",
"videoFreezing": "Attendez vous à ce que votre vidéo saute, soit noire, et pixelisée.",
@@ -1040,6 +1057,7 @@
"onBy": "{{name}} a démarré l'enregistrement",
"onlyRecordSelf": "Enregistrer seulement mon audio et ma vidéo.",
"pending": "Préparation de l'enregistrement de la réunion…",
"policyError": "Vous avez essayé de démarrer un enregistrement trop rapidement. Veuillez réessayer plus tard !",
"recordAudioAndVideo": "Enregistrer l'audio et la vidéo",
"recordTranscription": "Enregistrer la transcription",
"saveLocalRecording": "Sauvegarder l’enregistrement local (Beta)",
@@ -1088,6 +1106,7 @@
"desktopShareWarning": "Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
"devices": "Périphériques",
"followMe": "Tout le monde me suit",
"followMeRecorder": "L'enregistreur me suit",
"framesPerSecond": "images par seconde",
"incomingMessage": "un message arrive",
"language": "Langue",
@@ -1234,6 +1253,7 @@
"lobbyButton": "Activer / Désactiver le mode salle d'attente",
"localRecording": "Activer / Désactiver les contrôles d'enregistrement local",
"lockRoom": "Activer / Désactiver le mot de passe de la réunion",
"love": "Cœur",
"lowerHand": "Baisser la main",
"moreActions": "Activer / Désactiver le menu d'actions supplémentaires",
"moreActionsMenu": "Menu d'actions supplémentaires",
@@ -1251,6 +1271,7 @@
"privateMessage": "Envoyer un message privé",
"profile": "Éditer votre profil",
"raiseHand": "Lever la main",
"react": "Réactions aux messages",
"reactions": "Réactions",
"reactionsMenu": "Ouvrir / fermer le menu réactions",
"recording": "Activer / Désactiver l'enregistrement",
@@ -1322,6 +1343,7 @@
"lobbyButtonEnable": "Activer le mode salle d'attente / contrôle des participant(e)s",
"login": "Connexion",
"logout": "Déconnexion",
"love": "Cœur",
"lowerYourHand": "Baisser la main",
"moreActions": "Plus d'actions",
"moreOptions": "Plus d'options",
@@ -1347,6 +1369,7 @@
"raiseYourHand": "Lever la main",
"reactionBoo": "Envoyer réaction huer",
"reactionClap": "Envoyer réaction applaudir",
"reactionHeart": "Envoyer une réaction en forme de cœur",
"reactionLaugh": "Envoyer réaction rire",
"reactionLike": "Envoyer réaction approuver",
"reactionSilence": "Envoyer réaction silence",
@@ -1380,7 +1403,7 @@
"transcribing": {
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
"expandedLabel": "La transcription est actuellement activée",
"failedToStart": "Échec de démarrage de la transcription",
"failed": "La transcription a échoué",
"labelToolTip": "La transcription de la réunion est en cours",
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
"sourceLanguageHere": "ici",
@@ -1481,10 +1504,15 @@
},
"visitors": {
"chatIndicator": "(visiteur)",
"labelTooltip": "Nombre de Visiteurs",
"joinMeeting": {
"description": "Vous êtes actuellement un observateur dans cette conférence.",
"raiseHand": "Levez la main",
"title": "Rejoindre la réunion",
"wishToSpeak": "Si vous souhaitez prendre la parole, veuillez lever la main ci-dessous et attendre l'approbation du modérateur."
},
"labelTooltip": "Nombre de Visiteurs: {{count}}",
"notification": {
"demoteDescription": "Envoyé ici par {{actor}}, levez la main pour participer",
"description": "Pour participer lever la main.",
"noMainParticipantsDescription": "Un participant doit démarrer la réunion. Veuillez réessayer dans un moment.",
"noMainParticipantsTitle": "Cette réunion n'a pas encore commencé.",
"noVisitorLobby": "Vous ne pouvez pas rejoindre tant qu'une salle d'attente est activée pour la réunion.",

View File

@@ -334,6 +334,7 @@
"kickParticipantButton": "Izraidīt",
"kickParticipantDialog": "Vai esat pārliecināti, ka vēlaties izraidīt šo dalībnieku?",
"kickParticipantTitle": "Izraidīt šo dalībnieku?",
"kickSystemTitle": "Ak! Jūs izraidīja no sapulces",
"kickTitle": "Ak! {{participantDisplayName}} izraidīja jūs no sapulces",
"linkMeeting": "Sasaistīt sapulci",
"linkMeetingTitle": "Sasaistīt sapulci ar Salesforce",
@@ -483,8 +484,8 @@
"transcribing": "Notiek transkripcija",
"unlockRoom": "Noņemt $t(lockRoomPassword)",
"user": "Lietotājs",
"userIdentifier": "Lietotāja identifikators",
"userPassword": "lietotāja parole",
"userIdentifier": "Lietotājvārds",
"userPassword": "Parole",
"verifyParticipantConfirm": "Sakrīt",
"verifyParticipantDismiss": "Nesakrīt",
"verifyParticipantQuestion": "EKSPERIMENTĀLS: pajautājiet dalībniekam {{participantName}}, vai viņš redz to pašu saturu tādā pašā secībā.",
@@ -641,6 +642,7 @@
"on": "Tiešraide ieslēgta",
"onBy": "{{name}} ieslēdza tiešraidi",
"pending": "Sākam tiešraidi…",
"policyError": "Jūs mēģinājāt pārāk ātri sākt tiešraides straumi. Lūdzu, vēlāk mēģiniet vēlreiz!",
"serviceName": "Tiešsaistes pakalpojums",
"sessionAlreadyActive": "Šī sesija jau tiek ierakstīta vai straumēta tiešraidē.",
"signIn": "Ierakstīties ar Google kontu",
@@ -739,6 +741,7 @@
"connectedOneMember": "{{name}} ir pievienojies sapulcei",
"connectedThreePlusMembers": "{{name}} un {{count}} citi ir pievienojušies sapulcei",
"connectedTwoMembers": "{{first}} un {{second}} ir pievienojušies sapulcei",
"connectionFailed": "Savienojums neizdevās. Lūdzu, vēlāk mēģiniet vēlreiz!",
"dataChannelClosed": "Video kvalitāte var būt traucēta",
"dataChannelClosedDescription": "Savienojuma kanāls nedarbojas, tāpēc video kvalitāte var būt ierobežota līdz zemākajam iestatījumam.",
"dataChannelClosedDescriptionWithAudio": "Savienojuma kanāls nedarbojas, tāpēc var rasties audio un video traucējumi.",
@@ -753,6 +756,9 @@
"gifsMenu": "GIPHY",
"groupTitle": "Paziņojumi",
"hostAskedUnmute": "Moderators vēlas, lai jūs runātu",
"invalidTenant": "Nederīgs tenants",
"invalidTenantHyphenDescription": "Jūsu izmantotais tenants nav derīgs (sākas vai beidzas ar '-').",
"invalidTenantLengthDescription": "Jūsu izmantotais tenants ir pārāk garš.",
"invitedOneMember": "{{displayName}} tika uzaicināts",
"invitedThreePlusMembers": "Uzaicināts {{name}} un {{count}} citi lietotāji",
"invitedTwoMembers": "{{first}} un {{second}} tika uzaicināti uz sapulci",
@@ -1051,6 +1057,7 @@
"onBy": "{{name}} ieslēdza ierakstu",
"onlyRecordSelf": "Ierakstīt tikai manas audio un video straumes",
"pending": "Gatavojas ierakstīt sapulci…",
"policyError": "Jūs mēģinājāt pārāk ātri sākt ierakstīšanu. Lūdzu, vēlāk mēģiniet vēlreiz!",
"recordAudioAndVideo": "Ierakstīt audio un video",
"recordTranscription": "Ierakstīt transkripciju",
"saveLocalRecording": "Ieraksta faila saglabāšana lokāli (beta)",
@@ -1246,6 +1253,7 @@
"lobbyButton": "Iespējot/atspējot vestibila režīmu",
"localRecording": "Lokālā ieraksta vadības rīki (iesl./izsl.)",
"lockRoom": "Telpas slēgšana ar paroli (iesl./izsl.)",
"love": "Sirds",
"lowerHand": "Nolaist roku",
"moreActions": "Papildus iestatījumu izvēlne (rādīt/nerādīt)",
"moreActionsMenu": "Papildus iestatījumu izvēlne",
@@ -1335,6 +1343,7 @@
"lobbyButtonEnable": "Iespējot vestibila režīmu",
"login": "Ierakstīties",
"logout": "Izrakstīties",
"love": "Sirds",
"lowerYourHand": "Nolaist roku",
"moreActions": "Vairāk rīcību",
"moreOptions": "Vairāk opciju",
@@ -1360,6 +1369,7 @@
"raiseYourHand": "Pacelt roku",
"reactionBoo": "Nosūtīt būū reakciju",
"reactionClap": "Nosūtīt aplausu reakciju",
"reactionHeart": "Nosūtīt sirds reakciju",
"reactionLaugh": "Nosūtīt smieklu reakciju",
"reactionLike": "Nosūtīt īkšķi augšup reakciju",
"reactionSilence": "Nosūtīt klusuma reakciju",

File diff suppressed because it is too large Load Diff

View File

@@ -410,6 +410,7 @@
"sendPrivateMessageTitle": "私信回复?",
"serviceUnavailable": "服务不可用",
"sessTerminated": "通话已结束",
"sessTerminatedReason": "会议已经结束",
"sessionRestarted": "由于连接问题,呼叫重新启动。",
"shareAudio": "继续",
"shareAudioTitle": "如何分享音频",

View File

@@ -307,8 +307,8 @@
"contactSupport": "Contact support",
"copied": "Copied",
"copy": "Copy",
"demoteParticipantDialog": "Are you sure you want to move this participant to visitor?",
"demoteParticipantTitle": "Move to visitor",
"demoteParticipantDialog": "Are you sure you want to move this participant to viewer?",
"demoteParticipantTitle": "Move to viewer",
"dismiss": "Dismiss",
"displayNameRequired": "Hi! What’s your name?",
"done": "Done",
@@ -334,6 +334,7 @@
"kickParticipantButton": "Kick",
"kickParticipantDialog": "Are you sure you want to kick this participant?",
"kickParticipantTitle": "Kick this participant?",
"kickSystemTitle": "Ouch! You were kicked out of the meeting",
"kickTitle": "Ouch! {{participantDisplayName}} kicked you out of the meeting",
"linkMeeting": "Link meeting",
"linkMeetingTitle": "Link meeting to Salesforce",
@@ -830,9 +831,9 @@
"videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!",
"viewLobby": "View lobby",
"viewParticipants": "View participants",
"viewVisitors": "View visitors",
"viewVisitors": "View viewers",
"waitingParticipants": "{{waitingParticipants}} people",
"waitingVisitors": "Visitors waiting in queue: {{waitingVisitors}}",
"waitingVisitors": "Viewers waiting in queue: {{waitingVisitors}}",
"waitingVisitorsTitle": "The meeting is not live yet!",
"whiteboardLimitDescription": "Please save your progress, as the user limit will soon be reached and the whiteboard will close.",
"whiteboardLimitTitle": "Whiteboard usage"
@@ -869,7 +870,7 @@
"participantsList": "Meeting participants ({{count}})",
"visitorInQueue": " (waiting {{count}})",
"visitorRequests": " (requests {{count}})",
"visitors": "Visitors {{count}}",
"visitors": "Viewers {{count}}",
"waitingLobby": "Waiting in lobby ({{count}})"
},
"search": "Search participants",
@@ -1082,7 +1083,7 @@
"about": "You can add a $t(lockRoomPassword) to your meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
"aboutReadOnly": "Moderator participants can add a $t(lockRoomPassword) to the meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
"insecureRoomNameWarningNative": "The room name is unsafe. Unwanted participants may join your meeting. {{recommendAction}} Learn more about securing your meeting ",
"insecureRoomNameWarningWeb": "The room name is unsafe. Unwanted participants may join your meeting. {{recommendAction}} Learn more about securing you meeting <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">here</a>.",
"insecureRoomNameWarningWeb": "The room name is unsafe. Unwanted participants may join your meeting. {{recommendAction}} Learn more about securing your meeting <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">here</a>.",
"title": "Security Options",
"unsafeRoomActions": {
"meeting": "Consider securing your meeting using the security button.",
@@ -1182,6 +1183,7 @@
"fearful": "Fearful",
"happy": "Happy",
"hours": "{{count}}h",
"labelTooltip": "Number of participants: {{count}}",
"minutes": "{{count}}m",
"name": "Name",
"neutral": "Neutral",
@@ -1450,7 +1452,7 @@
},
"videothumbnail": {
"connectionInfo": "Connection Info",
"demote": "Move to visitor",
"demote": "Move to viewer",
"domute": "Mute",
"domuteOthers": "Mute everyone else",
"domuteVideo": "Disable camera",
@@ -1502,21 +1504,21 @@
"webAssemblyWarningDescription": "WebAssembly disabled or not supported by this browser"
},
"visitors": {
"chatIndicator": "(visitor)",
"chatIndicator": "(viewer)",
"joinMeeting": {
"description": "You're currently an observer in this conference.",
"description": "You're currently a viewer in this conference.",
"raiseHand": "Raise your hand",
"title": "Joining meeting",
"wishToSpeak": "If you wish to speak, please raise your hand below and wait for the moderator's approval."
},
"labelTooltip": "Number of visitors: {{count}}",
"labelTooltip": "Number of viewers: {{count}}",
"notification": {
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
"noMainParticipantsDescription": "A participant needs to start the meeting. Please try again in a bit.",
"noMainParticipantsTitle": "This meeting hasn’t started yet.",
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
"notAllowedPromotion": "A participant needs to allow your request first.",
"title": "You are a visitor in the meeting"
"title": "You are a viewer in the meeting"
},
"waitingMessage": "You'll join the meeting as soon as it is live!"
},

View File

@@ -115,7 +115,7 @@ import { toggleScreenshotCaptureSummary } from '../../react/features/screenshot-
import { isScreenshotCaptureEnabled } from '../../react/features/screenshot-capture/functions';
import SettingsDialog from '../../react/features/settings/components/web/SettingsDialog';
import { SETTINGS_TABS } from '../../react/features/settings/constants';
import { playSharedVideo, stopSharedVideo } from '../../react/features/shared-video/actions.any';
import { playSharedVideo, stopSharedVideo } from '../../react/features/shared-video/actions';
import { extractYoutubeIdOrURL } from '../../react/features/shared-video/functions';
import { setRequestingSubtitles, toggleRequestingSubtitles } from '../../react/features/subtitles/actions';
import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/functions';
@@ -1571,8 +1571,8 @@ class API {
formattedArgument += `${arg.toString()}: ${arg.stack}`;
} else if (typeof arg === 'object') {
// NOTE: The non-enumerable properties of the objects wouldn't be included in the string after
// JSON.strigify. For example Map instance will be translated to '{}'. So I think we have to eventually
// do something better for parsing the arguments. But since this option for strigify is part of the
// JSON.stringify. For example Map instance will be translated to '{}'. So I think we have to eventually
// do something better for parsing the arguments. But since this option for stringify is part of the
// public interface and I think it could be useful in some cases I will it for now.
try {
formattedArgument += JSON.stringify(arg);

View File

@@ -194,80 +194,6 @@ function changeParticipantNumber(APIInstance, number) {
APIInstance._numberOfParticipants += number;
}
/**
* Generates the URL for the iframe.
*
* @param {string} domain - The domain name of the server that hosts the
* conference.
* @param {string} [options] - Another optional parameters.
* @param {Object} [options.configOverwrite] - Object containing configuration
* options defined in config.js to be overridden.
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
* configuration options defined in interface_config.js to be overridden.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication.
* @param {string} [options.lang] - The meeting's default language.
* @param {string} [options.roomName] - The name of the room to join.
* @returns {string} The URL.
*/
function generateURL(domain, options = {}) {
return urlObjectToString({
...options,
url: `https://${domain}/#jitsi_meet_external_api_id=${id}`
});
}
/**
* Parses the arguments passed to the constructor. If the old format is used
* the function translates the arguments to the new format.
*
* @param {Array} args - The arguments to be parsed.
* @returns {Object} JS object with properties.
*/
function parseArguments(args) {
if (!args.length) {
return {};
}
const firstArg = args[0];
switch (typeof firstArg) {
case 'string': // old arguments format
case 'undefined': {
// Not sure which format but we are trying to parse the old
// format because if the new format is used everything will be undefined
// anyway.
const [
roomName,
width,
height,
parentNode,
configOverwrite,
interfaceConfigOverwrite,
jwt,
onload,
lang
] = args;
return {
roomName,
width,
height,
parentNode,
configOverwrite,
interfaceConfigOverwrite,
jwt,
onload,
lang
};
}
case 'object': // new arguments format
return args[0];
default:
throw new Error('Can\'t parse the arguments!');
}
}
/**
* Compute valid values for height and width. If a number is specified it's
* treated as pixel units. If the value is expressed in px, em, pt or
@@ -336,7 +262,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* @param {string} [options.release] - The key used for specifying release if enabled on the backend.
* @param {string} [options.sandbox] - Sandbox directive for the created iframe, if desired.
*/
constructor(domain, ...args) {
constructor(domain, options = {}) {
super();
const {
roomName = '',
@@ -345,21 +271,22 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
parentNode = document.body,
configOverwrite = {},
interfaceConfigOverwrite = {},
jwt = undefined,
lang = undefined,
onload = undefined,
jwt,
lang,
onload,
invitees,
iceServers,
devices,
userInfo,
e2eeKey,
release,
sandbox = ''
} = parseArguments(args);
sandbox
} = options;
const localStorageContent = jitsiLocalStorage.getItem('jitsiLocalStorage');
this._parentNode = parentNode;
this._url = generateURL(domain, {
this._url = urlObjectToString({
configOverwrite,
iceServers,
interfaceConfigOverwrite,
@@ -371,7 +298,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
appData: {
localStorageContent
},
release
release,
url: `https://${domain}/#jitsi_meet_external_api_id=${id}`
});
this._createIFrame(height, width, sandbox);

6584
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,13 +26,13 @@
"@jitsi/excalidraw": "https://github.com/jitsi/excalidraw/releases/download/v0.0.19/jitsi-excalidraw-0.0.19.tgz",
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
"@jitsi/rnnoise-wasm": "0.1.0",
"@jitsi/rnnoise-wasm": "0.2.0",
"@jitsi/rtcstats": "9.5.1",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@microsoft/microsoft-graph-client": "3.0.1",
"@mui/material": "5.12.1",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-clipboard/clipboard": "1.14.1",
"@react-native-clipboard/clipboard": "1.14.3",
"@react-native-community/netinfo": "11.1.0",
"@react-native-community/slider": "4.4.3",
"@react-native-google-signin/google-signin": "10.1.0",
@@ -68,7 +68,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/v1890.0.0+144b0cab/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1897.0.0+283af7ac/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -76,6 +76,7 @@
"optional-require": "1.0.3",
"pixelmatch": "5.3.0",
"promise.allsettled": "1.0.4",
"promise.withresolvers": "1.0.3",
"punycode": "2.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
@@ -134,17 +135,21 @@
"@babel/plugin-transform-private-methods": "7.25.9",
"@babel/preset-env": "7.25.9",
"@babel/preset-react": "7.25.9",
"@jitsi/eslint-config": "4.1.10",
"@jitsi/eslint-config": "5.0.9",
"@react-native/metro-config": "0.75.4",
"@stylistic/eslint-plugin": "2.12.1",
"@types/amplitude-js": "8.16.5",
"@types/audioworklet": "0.0.29",
"@types/dom-screen-wake-lock": "1.0.1",
"@types/jasmine": "5.1.4",
"@types/js-md5": "0.4.3",
"@types/jsonwebtoken": "9.0.7",
"@types/lodash-es": "4.17.12",
"@types/minimatch": "5.1.2",
"@types/mocha": "10.0.10",
"@types/moment-duration-format": "2.2.6",
"@types/offscreencanvas": "2019.7.2",
"@types/pixelmatch": "5.2.5",
"@types/pretty": "2.0.3",
"@types/punycode": "2.1.0",
"@types/react": "17.0.14",
"@types/react-dom": "17.0.14",
@@ -158,36 +163,38 @@
"@types/w3c-image-capture": "1.0.6",
"@types/w3c-web-hid": "1.0.3",
"@types/zxcvbn": "4.4.1",
"@typescript-eslint/eslint-plugin": "5.59.5",
"@typescript-eslint/parser": "5.59.5",
"@wdio/allure-reporter": "9.2.14",
"@wdio/cli": "9.2.14",
"@wdio/globals": "9.2.14",
"@wdio/jasmine-framework": "9.2.14",
"@wdio/junit-reporter": "9.2.14",
"@wdio/local-runner": "9.2.15",
"@typescript-eslint/eslint-plugin": "8.19.1",
"@typescript-eslint/parser": "8.19.1",
"@wdio/allure-reporter": "9.4.3",
"@wdio/cli": "9.4.3",
"@wdio/globals": "9.4.3",
"@wdio/junit-reporter": "9.4.3",
"@wdio/local-runner": "9.4.3",
"@wdio/mocha-framework": "9.4.3",
"babel-loader": "9.1.0",
"babel-plugin-optional-require": "0.3.1",
"circular-dependency-plugin": "5.2.0",
"clean-css-cli": "4.3.0",
"css-loader": "6.8.1",
"eslint": "8.40.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jsdoc": "46.2.6",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-native": "4.0.0",
"eslint-plugin-typescript-sort-keys": "2.3.0",
"eslint": "8.57.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsdoc": "50.6.1",
"eslint-plugin-react": "7.37.3",
"eslint-plugin-react-native": "5.0.0",
"eslint-plugin-typescript-sort-keys": "3.3.0",
"jetifier": "1.6.4",
"jsonwebtoken": "9.0.2",
"metro-react-native-babel-preset": "0.77.0",
"patch-package": "6.4.7",
"pretty": "2.0.0",
"process": "0.11.10",
"sass": "1.26.8",
"style-loader": "3.3.1",
"traverse": "0.6.6",
"ts-loader": "9.4.2",
"typescript": "5.0.4",
"typescript": "5.7.2",
"unorm": "1.6.0",
"webdriverio": "9.2.14",
"webdriverio": "9.4.3",
"webpack": "5.95.0",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "5.1.4",
@@ -197,7 +204,7 @@
"@xmldom/xmldom": "0.8.7"
},
"engines": {
"node": ">=20.0.0",
"node": ">=22.0.0",
"npm": ">=10.0.0"
},
"license": "Apache-2.0",
@@ -215,7 +222,12 @@
"tsc-test:web": "tsc --project tsconfig.web.json --listFilesOnly | grep -v node_modules | grep native",
"tsc-test:native": "tsc --project tsconfig.native.json --listFilesOnly | grep -v node_modules | grep web",
"start": "make dev",
"test": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts"
"test": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts",
"test-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.conf.ts --spec",
"test-ff": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.firefox.conf.ts",
"test-dev": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.dev.conf.ts",
"test-grid": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts",
"test-grid-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts --spec"
},
"resolutions": {
"@types/react": "17.0.14",

View File

@@ -58,7 +58,6 @@
"peerDependencies": {
"@amplitude/react-native": "0.0.0",
"@giphy/react-native-sdk": "0.0.0",
"@react-native/metro-config": "*",
"@react-native-async-storage/async-storage": "0.0.0",
"@react-native-clipboard/clipboard": "0.0.0",
"@react-native-community/netinfo": "0.0.0",

View File

@@ -1,9 +1,13 @@
const fs = require('fs');
const semver = require('semver');
const packageJSON = require('../package.json');
const SDKPackageJSON = require('./package.json');
// Skip checking these.
const skipDeps = [ 'react', 'react-native' ];
/**
* Merges the dependency versions from the root package.json with the dependencies of the SDK package.json.
*/
@@ -18,15 +22,21 @@ function mergeDependencyVersions() {
// Updates SDK peer dependencies.
for (const key in packageJSON.dependencies) {
if (SDKPackageJSON.peerDependencies.hasOwnProperty(key)) {
// Updates all peer dependencies except react and react-native.
if (key !== 'react' && key !== 'react-native') {
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
}
if (SDKPackageJSON.peerDependencies.hasOwnProperty(key) && !skipDeps.includes(key)) {
SDKPackageJSON.peerDependencies[key] = packageJSON.dependencies[key];
}
}
// Set RN peer dependency.
const rnVersion = semver.parse(packageJSON.dependencies['react-native']);
if (!rnVersion) {
throw new Error('failed to parse React Native version');
}
// In RN the "major" version is the Semver minor.
SDKPackageJSON.peerDependencies['react-native'] = `~0.${rnVersion.minor}.0`;
// Updates SDK overrides dependencies.
for (const key in packageJSON.overrides) {
if (SDKPackageJSON.overrides.hasOwnProperty(key)) {

View File

@@ -14,24 +14,14 @@ module.exports = {
project: [ './tsconfig.web.json', './tsconfig.native.json' ]
},
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
'selector': 'interface',
'format': [ 'PascalCase' ],
'custom': {
'regex': '^I[A-Z]',
'match': true
}
}
]
// TODO: Remove these and fix the warnings
'@typescript-eslint/no-unsafe-function-type': 0,
'@typescript-eslint/no-wrapper-object-types': 0,
'@typescript-eslint/no-require-imports': 0
}
}
],
'rules': {
// XXX remove this eventually.
'react/jsx-indent-props': 0
},
'settings': {
'react': {
'version': 'detect'

View File

@@ -4,8 +4,10 @@ import ReactDOM from 'react-dom';
import AlwaysOnTop from './AlwaysOnTop';
// Render the main/root Component.
/* eslint-disable-next-line react/no-deprecated */
ReactDOM.render(<AlwaysOnTop />, document.getElementById('react'));
window.addEventListener(
'beforeunload',
/* eslint-disable-next-line react/no-deprecated */
() => ReactDOM.unmountComponentAtNode(document.getElementById('react') ?? document.body));

View File

@@ -311,7 +311,7 @@ export function createInviteDialogEvent(
* @returns {Object}
*/
export function createNetworkInfoEvent({ isOnline, networkType, details }:
{ details?: Object; isOnline: boolean; networkType?: string; }) {
{ details?: Object; isOnline: boolean; networkType?: string; }) {
const attributes: {
details?: Object;
isOnline: boolean;

View File

@@ -154,6 +154,30 @@ export async function createHandlers({ getState }: IStore) {
return handlers;
}
/**
* Checks whether a url is a data URL or not.
*
* @param {string} url - The URL to be checked.
* @returns {boolean}
*/
function isDataURL(url?: string): boolean {
if (typeof url !== 'string') { // The icon will be ignored
return false;
}
try {
const urlObject = new URL(url);
if (urlObject.protocol === 'data:') {
return false;
}
} catch {
return false;
}
return true;
}
/**
* Inits JitsiMeetJS.analytics by setting permanent properties and setting the handlers from the loaded scripts.
* NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be null.
@@ -185,13 +209,21 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
inIframe?: boolean;
isPromotedFromVisitor?: boolean;
isVisitor?: boolean;
overwritesCustomButtonsWithURL?: boolean;
overwritesCustomParticipantButtonsWithURL?: boolean;
overwritesDefaultLogoUrl?: boolean;
overwritesDeploymentUrls?: boolean;
overwritesEtherpadBase?: boolean;
overwritesHosts?: boolean;
overwritesIceServers?: boolean;
overwritesLiveStreamingUrls?: boolean;
overwritesPeopleSearchUrl?: boolean;
overwritesPrejoinConfigICEUrl?: boolean;
overwritesSalesforceUrl?: boolean;
overwritesSupportUrl?: boolean;
overwritesWatchRTCConfigParams?: boolean;
overwritesWatchRTCProxyUrl?: boolean;
overwritesWatchRTCWSUrl?: boolean;
server?: string;
tenant?: string;
wasLobbyVisible?: boolean;
@@ -235,6 +267,21 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
permanentProperties.overwritesSalesforceUrl = 'config.salesforceUrl' in params;
permanentProperties.overwritesPeopleSearchUrl = 'config.peopleSearchUrl' in params;
permanentProperties.overwritesDefaultLogoUrl = 'config.defaultLogoUrl' in params;
permanentProperties.overwritesEtherpadBase = 'config.etherpad_base' in params;
const hosts = params['config.hosts'] ?? {};
const hostsProps = [ 'anonymousdomain', 'authdomain', 'domain', 'focus', 'muc', 'visitorFocus' ];
permanentProperties.overwritesHosts = 'config.hosts' in params
|| Boolean(hostsProps.find(p => `config.hosts.${p}` in params || (typeof hosts === 'object' && p in hosts)));
permanentProperties.overwritesWatchRTCConfigParams = 'config.watchRTCConfigParams' in params;
const watchRTCConfigParams = params['config.watchRTCConfigParams'] ?? {};
permanentProperties.overwritesWatchRTCProxyUrl = ('config.watchRTCConfigParams.proxyUrl' in params)
|| (typeof watchRTCConfigParams === 'object' && 'proxyUrl' in watchRTCConfigParams);
permanentProperties.overwritesWatchRTCWSUrl = ('config.watchRTCConfigParams.wsUrl' in params)
|| (typeof watchRTCConfigParams === 'object' && 'wsUrl' in watchRTCConfigParams);
const prejoinConfig = params['config.prejoinConfig'] ?? {};
permanentProperties.overwritesPrejoinConfigICEUrl = ('config.prejoinConfig.preCallTestICEUrl' in params)
@@ -260,6 +307,18 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
)
);
permanentProperties.overwritesIceServers = Boolean(Object.keys(params).find(k => k.startsWith('iceServers')));
const customToolbarButtons = params['config.customToolbarButtons'] ?? [];
permanentProperties.overwritesCustomButtonsWithURL = Boolean(
customToolbarButtons.find(({ icon }: { icon: string; }) => isDataURL(icon)));
const customParticipantMenuButtons = params['config.customParticipantMenuButtons'] ?? [];
permanentProperties.overwritesCustomParticipantButtonsWithURL = Boolean(
customParticipantMenuButtons.find(({ icon }: { icon: string; }) => isDataURL(icon)));
// Optionally, include local deployment information based on the
// contents of window.config.deploymentInfo.
if (deploymentInfo) {

View File

@@ -158,4 +158,3 @@ export function maybeRedirectToTokenAuthUrl(
return false;
}

View File

@@ -27,11 +27,9 @@ export interface IProps {
*/
export class AbstractApp<P extends IProps = IProps> extends BaseApp<P> {
/**
* The deferred for the initialisation {{promise, resolve, reject}}.
* The deferred for the initialization {{promise, resolve, reject}}.
*/
_init: {
promise: Promise<any>;
};
_init: PromiseWithResolvers<any>;
/**
* Initializes the app.

View File

@@ -140,10 +140,7 @@ function _getWebWelcomePageRoute(state: IReduxState) {
*
* @returns {Object}
*/
function _getEmptyRoute(): {
component: React.ReactNode;
href?: string;
} {
function _getEmptyRoute(): { component: React.ReactNode; href?: string; } {
return {
component: BlankPage,
href: undefined

View File

@@ -113,8 +113,7 @@ function _isMaybeSplitBrainError(getState: IStore['getState'], action: AnyAction
const { error } = action;
const isShardChangedError = error
&& error.message === 'item-not-found'
&& error.details
&& error.details.shard_changed;
&& error.details?.shard_changed;
if (isShardChangedError) {
const state = getState();

View File

@@ -38,7 +38,6 @@ import '../jaas/reducer';
import '../large-video/reducer';
import '../lobby/reducer';
import '../notifications/reducer';
import '../overlay/reducer';
import '../participants-pane/reducer';
import '../polls/reducer';
import '../polls-history/reducer';

View File

@@ -57,7 +57,6 @@ import { INoAudioSignalState } from '../no-audio-signal/reducer';
import { INoiseDetectionState } from '../noise-detection/reducer';
import { INoiseSuppressionState } from '../noise-suppression/reducer';
import { INotificationsState } from '../notifications/reducer';
import { IOverlayState } from '../overlay/reducer';
import { IParticipantsPaneState } from '../participants-pane/reducer';
import { IPollsState } from '../polls/reducer';
import { IPollsHistoryState } from '../polls-history/reducer';
@@ -147,7 +146,6 @@ export interface IReduxState {
'features/noise-detection': INoiseDetectionState;
'features/noise-suppression': INoiseSuppressionState;
'features/notifications': INotificationsState;
'features/overlay': IOverlayState;
'features/participants-pane': IParticipantsPaneState;
'features/polls': IPollsState;
'features/polls-history': IPollsHistoryState;

View File

@@ -121,7 +121,7 @@ MiddlewareRegistry.register(store => next => action => {
if (isTokenAuthEnabled(config)
&& config.tokenAuthUrlAutoRedirect
&& state['features/base/jwt'].jwt) {
// auto redirect is turned on and we have succesfully logged in
// auto redirect is turned on and we have successfully logged in
// let's mark that
dispatch(setTokenAuthUrlSuccess(true));
}

View File

@@ -14,7 +14,6 @@ import PersistenceRegistry from '../../redux/PersistenceRegistry';
import ReducerRegistry from '../../redux/ReducerRegistry';
import StateListenerRegistry from '../../redux/StateListenerRegistry';
import SoundCollection from '../../sounds/components/SoundCollection';
import { createDeferred } from '../../util/helpers';
import { appWillMount, appWillUnmount } from '../actions';
import logger from '../logger';
@@ -46,9 +45,7 @@ export default class BaseApp<P> extends Component<P, IState> {
/**
* The deferred for the initialisation {{promise, resolve, reject}}.
*/
_init: {
promise: Promise<any>;
};
_init: PromiseWithResolvers<any>;
/**
* Initializes a new {@code BaseApp} instance.
@@ -79,7 +76,7 @@ export default class BaseApp<P> extends Component<P, IState> {
* @see {@link #_initStorage}
* @type {Promise}
*/
this._init = createDeferred<void>();
this._init = Promise.withResolvers();
try {
await this._initStorage();

View File

@@ -248,8 +248,7 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
}
!error.recoverable
&& conference
&& conference.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch((reason: Error) => {
&& conference?.leave(CONFERENCE_LEAVE_REASONS.UNRECOVERABLE_ERROR).catch((reason: Error) => {
// Even though we don't care too much about the failure, it may be
// good to know that it happen, so log it (on the info level).
logger.info('JitsiConference.leave() rejected with:', reason);
@@ -430,7 +429,7 @@ function _connectionFailed({ dispatch, getState }: IStore, next: Function, actio
} as INotificationProps;
const { locationURL = { href: '' } as URL } = getState()['features/base/connection'];
const { tenant } = parseURIString(locationURL.href) || {};
const { tenant = '' } = parseURIString(locationURL.href) || {};
if (tenant.startsWith('-') || tenant.endsWith('-')) {
notificationProps.descriptionKey = 'notify.invalidTenantHyphenDescription';
@@ -551,7 +550,7 @@ function _pinParticipant({ getState }: IStore, next: Function, action: AnyAction
const actionName = id ? ACTION_PINNED : ACTION_UNPINNED;
const local
= participantById?.local
|| (!id && pinnedParticipant && pinnedParticipant.local);
|| (!id && pinnedParticipant?.local);
let participantIdForEvent;
if (local) {

View File

@@ -495,7 +495,7 @@ function _conferenceJoined(state: IConferenceState, { conference }: { conference
* reduction of the specified action.
*/
function _conferenceLeftOrWillLeave(state: IConferenceState, { conference, type }:
{ conference: IJitsiConference; type: string; }) {
{ conference: IJitsiConference; type: string; }) {
const nextState = { ...state };
// The redux action CONFERENCE_LEFT is the last time that we should be

View File

@@ -404,6 +404,7 @@ export interface IConfig {
guestDialOutStatusUrl?: string;
guestDialOutUrl?: string;
helpCentreURL?: string;
hiddenDomain?: string;
hiddenPremeetingButtons?: Array<'microphone' | 'camera' | 'select-background' | 'invite' | 'settings'>;
hideAddRoomButton?: boolean;
hideConferenceSubject?: boolean;

View File

@@ -76,7 +76,6 @@ export default [
'channelLastN',
'connectionIndicators',
'constraints',
'customToolbarButtons',
'brandingRoomAlias',
'debug',
'debugAudioLevels',
@@ -131,7 +130,7 @@ export default [
'displayJids',
'doNotStoreRoom',
'doNotFlipLocalVideo',
'dropbox',
'dropbox.appKey',
'e2eeLabels',
'e2ee',
'e2eping',
@@ -149,7 +148,6 @@ export default [
'enableNoisyMicDetection',
'enableTcc',
'enableAutomaticUrlCopy',
'etherpad_base',
'faceLandmarks',
'failICE',
'feedbackPercentage',
@@ -169,14 +167,11 @@ export default [
'hideRecordingLabel',
'hideParticipantsStats',
'hideConferenceTimer',
'hiddenDomain',
'hideAddRoomButton',
'hideEmailInSettings',
'hideLobbyButton',
'hosts',
'iAmRecorder',
'iAmSipGateway',
'iceTransportPolicy',
'ignoreStartMuted',
'inviteAppName',
'liveStreaming.enabled',
@@ -192,7 +187,12 @@ export default [
'notificationTimeouts',
'openSharedDocumentOnJoin',
'opusMaxAverageBitrate',
'p2p',
'p2p.backToP2PDelay',
'p2p.codecPreferenceOrder',
'p2p.enabled',
'p2p.iceTransportPolicy',
'p2p.mobileCodecPreferenceOrder',
'p2p.mobileScreenshareCodec',
'participantMenuButtonsWithNotifyClick',
'participantsPane',
'pcStatsInterval',
@@ -233,8 +233,18 @@ export default [
'useHostPageLocalStorage',
'useTurnUdp',
'videoQuality',
'visitors',
'watchRTCConfigParams',
'visitors.enableMediaOnPromote',
'watchRTCConfigParams.console',
'watchRTCConfigParams.allowBrowserLogCollection',
'watchRTCConfigParams.collectionInterval',
'watchRTCConfigParams.debug',
'watchRTCConfigParams.keys',
'watchRTCConfigParams.logGetStats',
'watchRTCConfigParams.rtcApiKey',
'watchRTCConfigParams.rtcPeerId',
'watchRTCConfigParams.rtcRoomId',
'watchRTCConfigParams.rtcTags',
'watchRTCConfigParams.rtcToken',
'webrtcIceTcpDisable',
'webrtcIceUdpDisable',
'whiteboard.enabled'

View File

@@ -89,7 +89,7 @@ export function _setDeeplinkingDefaults(deeplinking: IDeeplinkingConfig) {
android.downloadLink = android.downloadLink
|| 'https://play.google.com/store/apps/details?id=org.jitsi.meet';
android.appPackage = android.appPackage || 'org.jitsi.meet';
android.fDroidUrl = android.fDroidUrl || 'https://f-droid.org/en/packages/org.jitsi.meet/';
android.fDroidUrl = android.fDroidUrl || 'https://f-droid.org/packages/org.jitsi.meet/';
if (android.dynamicLink) {
android.dynamicLink.apn = android.dynamicLink.apn || 'org.jitsi.meet';
android.dynamicLink.appCode = android.dynamicLink.appCode || 'w2atb';

View File

@@ -1,8 +1,11 @@
/**
* Prefixes of devices that will be filtered from the device list.
*
* NOTE: Currently we filter only 'Microsoft Teams Audio Device' virtual device. It seems that it can't be set
* NOTE: It seems that the filtered devices can't be set
* as default device on the OS level and this use case is not handled in the code. If we add more device prefixes that
* can be default devices we should make sure to handle the default device use case.
*/
export const DEVICE_LABEL_PREFIXES_TO_IGNORE = [ 'Microsoft Teams Audio Device' ];
export const DEVICE_LABEL_PREFIXES_TO_IGNORE = [
'Microsoft Teams Audio Device',
'ZoomAudioDevice'
];

View File

@@ -9,6 +9,7 @@ import {
OPEN_SHEET
} from './actionTypes';
import { isDialogOpen } from './functions';
import logger from './logger';
/**
* Signals Dialog to close its dialog.
@@ -23,6 +24,8 @@ import { isDialogOpen } from './functions';
* }}
*/
export function hideDialog(component?: ComponentType<any>) {
logger.info(`Hide dialog: ${getComponentDisplayName(component)}`);
return {
type: HIDE_DIALOG,
component
@@ -55,6 +58,8 @@ export function hideSheet() {
* }}
*/
export function openDialog(component: ComponentType<any>, componentProps?: Object) {
logger.info(`Open dialog: ${getComponentDisplayName(component)}`);
return {
type: OPEN_DIALOG,
component,
@@ -101,3 +106,21 @@ export function toggleDialog(component: ComponentType<any>, componentProps?: Obj
}
};
}
/**
* Extracts a printable name for a dialog component.
*
* @param {Object} component - The component to extract the name for.
*
* @returns {string} The display name.
*/
function getComponentDisplayName(component?: ComponentType<any>) {
if (!component) {
return '';
}
const name = component.displayName ?? component.name ?? 'Component';
return name.replace('withI18nextTranslation(Connect(', '') // dialogs with translations
.replace('))', ''); // dialogs with translations suffix
}

View File

@@ -23,7 +23,7 @@ MiddlewareRegistry.register(store => next => action => {
? action.value
: store.getState()['features/dynamic-branding'];
if (language && labels && labels[language]) {
if (language && labels?.[language]) {
changeLanguageBundle(language, labels[language])
.catch(err => {
logger.log('Error setting dynamic language bundle', err);

View File

@@ -237,7 +237,7 @@ function _undoOverwriteLocalParticipant(
* }}
*/
function _user2participant({ avatar, avatarUrl, email, id, name, 'hidden-from-recorder': hiddenFromRecorder }:
{ avatar?: string; avatarUrl?: string; email: string; 'hidden-from-recorder': string | boolean;
{ avatar?: string; avatarUrl?: string; email: string; 'hidden-from-recorder': string | boolean;
id: string; name: string; }) {
const participant: {
avatarURL?: string;

View File

@@ -228,7 +228,7 @@ class AudioTrack extends Component<IProps> {
* @returns {void}
*/
_detachTrack(track?: ITrack) {
if (this._ref?.current && track && track.jitsiTrack) {
if (this._ref?.current && track?.jitsiTrack) {
clearTimeout(this._playTimeout);
this._playTimeout = undefined;
track.jitsiTrack.detach(this._ref.current);

View File

@@ -358,7 +358,7 @@ class Video extends Component<IProps> {
* @returns {void}
*/
_detachTrack(videoTrack?: Partial<ITrack>) {
if (this._videoElement && videoTrack && videoTrack.jitsiTrack) {
if (this._videoElement && videoTrack?.jitsiTrack) {
videoTrack.jitsiTrack.detach(this._videoElement);
}
}

View File

@@ -19,11 +19,11 @@ export const MEDIA_TYPE: {
AUDIO: MediaType;
SCREENSHARE: MediaType;
VIDEO: MediaType;
} = {
AUDIO: 'audio',
SCREENSHARE: 'screenshare',
VIDEO: 'video'
};
} = {
AUDIO: 'audio',
SCREENSHARE: 'screenshare',
VIDEO: 'video'
};
/* eslint-disable no-bitwise */

View File

@@ -3,7 +3,6 @@ import { AnyAction, combineReducers } from 'redux';
import { CONFERENCE_FAILED, CONFERENCE_LEFT } from '../conference/actionTypes';
import ReducerRegistry from '../redux/ReducerRegistry';
import { TRACK_REMOVED } from '../tracks/actionTypes';
import { DefferedPromise, createDeferred } from '../util/helpers';
import {
GUM_PENDING,
@@ -93,17 +92,18 @@ function _audio(state: IAudioState = _AUDIO_INITIAL_MEDIA_STATE, action: AnyActi
// initial track creation haven't been started we would wait for it to finish before starting to join the room.
// NOTE: The previous implementation was using the GUM promise from conference.init. But it turned out that connect
// may finish even before conference.init is executed.
const DEFAULT_INITIAL_PROMISE_STATE = createDeferred<IInitialGUMPromiseResult>();
const DEFAULT_INITIAL_PROMISE_STATE = Promise.withResolvers<IInitialGUMPromiseResult>();
/**
* Reducer fot the common properties in media state.
* Reducer for the common properties in media state.
*
* @param {ICommonState} state - Common media state.
* @param {Object} action - Action object.
* @param {string} action.type - Type of action.
* @returns {ICommonState}
*/
function _initialGUMPromise(state: DefferedPromise<IInitialGUMPromiseResult> | null = DEFAULT_INITIAL_PROMISE_STATE,
function _initialGUMPromise(
state: PromiseWithResolvers<IInitialGUMPromiseResult> | null = DEFAULT_INITIAL_PROMISE_STATE,
action: AnyAction) {
if (action.type === SET_INITIAL_GUM_PROMISE) {
return action.promise ?? null;
@@ -294,7 +294,7 @@ interface IVideoState {
export interface IMediaState {
audio: IAudioState;
initialGUMPromise: DefferedPromise<IInitialGUMPromiseResult> | null;
initialGUMPromise: PromiseWithResolvers<IInitialGUMPromiseResult> | null;
screenshare: IScreenshareState;
video: IVideoState;
}

View File

@@ -240,7 +240,7 @@ export function participantJoined(participant: IParticipant) {
// conference. The following check is really necessary because a
// JitsiConference may have moved into leaving but may still manage to
// sneak a PARTICIPANT_JOINED in if its leave is delayed for any purpose
// (which is not outragous given that leaving involves network
// (which is not outrageous given that leaving involves network
// requests.)
const stateFeaturesBaseConference
= getState()['features/base/conference'];

View File

@@ -15,7 +15,6 @@ import i18next from '../i18n/i18next';
import { MEDIA_TYPE, MediaType, VIDEO_TYPE } from '../media/constants';
import { toState } from '../redux/functions';
import { getScreenShareTrack, isLocalTrackMuted } from '../tracks/functions.any';
import { createDeferred } from '../util/helpers';
import {
JIGASI_PARTICIPANT_ICON,
@@ -127,7 +126,7 @@ export function getActiveSpeakersToBeDisplayed(stateful: IStateful) {
* @returns {Promise}
*/
export function getFirstLoadableAvatarUrl(participant: IParticipant, store: IStore) {
const deferred: any = createDeferred();
const deferred: any = Promise.withResolvers();
const fullPromise = deferred.promise
.then(() => _getFirstLoadableAvatarUrl(participant, store))
.then((result: any) => {

View File

@@ -52,7 +52,7 @@ StateListenerRegistry.register(
/**
* Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
* tiles accodingly.
* tiles accordingly.
*
* @param {Array<string>} oldScreenshareSourceNames - List of old screenshare source names.
* @param {Array<string>} newScreenshareSourceNames - Current list of screenshare source names.

View File

@@ -340,9 +340,7 @@ class Popover extends Component<IProps, IState> {
_onTouchStart(event: TouchEvent) {
if (this.props.visible
&& !this.props.overflowDrawer
&& this._contextMenuRef
&& this._contextMenuRef.contains
&& !this._contextMenuRef.contains(event.target as Node)
&& !this._contextMenuRef?.contains?.(event.target as Node)
&& !this._containerRef?.current?.contains(event.target as Node)) {
this._onHideDialog();
}

View File

@@ -227,14 +227,14 @@ class MeetingsList extends Component<IProps> {
<Container
className = { rootClassName }
key = { index }
onClick = { onPress }>
onClick = { onPress }
tabIndex = { 0 }>
<Container className = 'right-column'>
<Text
className = 'title'
onClick = { onPress }
onKeyPress = { onKeyPress }
role = 'button'
tabIndex = { 0 }>
role = 'button'>
{ title }
</Text>
{

View File

@@ -41,8 +41,8 @@ export function formatURLText(text = '') {
}
if (!result) {
// This will be the case for invalid URLs or URLs without a host (emails for example). In this case beacuse
// of the issue with PunycodeJS that truncates parts of the text when there is '@' we split the text by '@'
// This will be the case for invalid URLs or URLs without a host (emails for example). In this case due to
// the issue with PunycodeJS that truncates parts of the text when there is '@' we split the text by '@'
// and use punycode for every separate part to prevent homograph attacks.
result = text.split('@').map(punycode.toASCII)
.join('@');
@@ -59,7 +59,7 @@ export function formatURLText(text = '') {
*/
export function getSupportUrl(stateful: IStateful) {
// TODO: Once overwriting trough interface config is completelly gone we should think of a way to be able to set
// TODO: Once overwriting through interface config is completely gone we should think of a way to be able to set
// the value in the branding and not return the default value from interface config.
return toState(stateful)['features/dynamic-branding'].supportUrl || interfaceConfig?.SUPPORT_URL;
}

View File

@@ -12,7 +12,7 @@ declare let __DEV__: any;
/**
* Mixed type of the element (subtree) config. If it's a {@code boolean} (and is
* {@code true}), we persist the entire subtree. If it's an {@code Object}, we
* perist a filtered subtree based on the properties of the config object.
* persist a filtered subtree based on the properties of the config object.
*/
declare type ElementConfig = boolean | Object;

View File

@@ -96,8 +96,8 @@ class StateListenerRegistry {
* @returns {void}
*/
_listener({ prevSelections, store }: {
prevSelections: Map<SelectorListener, any>;
store: Store<any, any>;
prevSelections: Map<SelectorListener, any>;
store: Store<any, any>;
}) {
for (const selectorListener of this._selectorListeners) {
const prevSelection = prevSelections.get(selectorListener);

View File

@@ -5,7 +5,7 @@ import { setAspectRatio, setReducedUI } from './actions';
/**
* Middleware that handles widnow dimension changes and updates the aspect ratio and
* Middleware that handles window dimension changes and updates the aspect ratio and
* reduced UI modes accordingly.
*
* @param {Store} store - The redux store.

View File

@@ -375,7 +375,7 @@ export default class AbstractButton<P extends IProps, S = any> extends Component
// blur after click to release focus from button to allow PTT.
// @ts-ignore
e?.currentTarget?.blur && e.currentTarget.blur();
e?.currentTarget?.blur?.();
}
/**

View File

@@ -359,8 +359,7 @@ export function createInitialAVTracks(options: ICreateInitialTracksOptions, reco
return (dispatch: IStore['dispatch'], _getState: IStore['getState']) => {
const {
devices,
timeout,
firePermissionPromptIsShownEvent
timeout
} = options;
dispatch(gumPending(devices, IGUMPendingState.PENDING_UNMUTE));
@@ -400,16 +399,14 @@ export function createInitialAVTracks(options: ICreateInitialTracksOptions, reco
if (devices.includes(MEDIA_TYPE.AUDIO)) {
gUMPromises.push(createLocalTracksF({
devices: [ MEDIA_TYPE.AUDIO ],
timeout,
firePermissionPromptIsShownEvent
timeout
}));
}
if (devices.includes(MEDIA_TYPE.VIDEO)) {
gUMPromises.push(createLocalTracksF({
devices: [ MEDIA_TYPE.VIDEO ],
timeout,
firePermissionPromptIsShownEvent
timeout
}));
}

View File

@@ -17,10 +17,7 @@ export * from './functions.any';
* and/or 'video'.
* @param {string|null} [options.micDeviceId] - Microphone device id or
* {@code undefined} to use app's settings.
* @param {number|undefined} [oprions.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
* @param {boolean} [options.firePermissionPromptIsShownEvent] - Whether lib-jitsi-meet
* should check for a {@code getUserMedia} permission prompt and fire a
* corresponding event.
* @param {number|undefined} [options.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
* @param {IStore} store - The redux store in the context of which the function
* is to execute and from which state such as {@code config} is to be retrieved.
* @returns {Promise<JitsiLocalTrack[]>}

View File

@@ -31,10 +31,7 @@ export * from './functions.any';
* and/or 'video'.
* @param {string|null} [options.micDeviceId] - Microphone device id or
* {@code undefined} to use app's settings.
* @param {number|undefined} [oprions.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
* @param {boolean} [options.firePermissionPromptIsShownEvent] - Whether lib-jitsi-meet
* should check for a {@code getUserMedia} permission prompt and fire a
* corresponding event.
* @param {number|undefined} [options.timeout] - A timeout for JitsiMeetJS.createLocalTracks used to create the tracks.
* @param {IStore} store - The redux store in the context of which the function
* is to execute and from which state such as {@code config} is to be retrieved.
* @param {boolean} recordTimeMetrics - If true time metrics will be recorded.
@@ -45,7 +42,6 @@ export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore,
const {
desktopSharingSourceDevice,
desktopSharingSources,
firePermissionPromptIsShownEvent,
timeout
} = options;
@@ -91,7 +87,6 @@ export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore,
effects,
facingMode: options.facingMode || getCameraFacingMode(state),
firefox_fake_device, // eslint-disable-line camelcase
firePermissionPromptIsShownEvent,
micDeviceId,
resolution,
timeout
@@ -146,7 +141,6 @@ export function createPrejoinTracks() {
if (requestedAudio || requestedVideo) {
tryCreateLocalTracks = createLocalTracksF({
devices: initialDevices,
firePermissionPromptIsShownEvent: true,
timeout
}, APP.store)
.catch(async (err: Error) => {
@@ -163,7 +157,6 @@ export function createPrejoinTracks() {
if (requestedAudio) {
gUMPromises.push(createLocalTracksF({
devices: [ MEDIA_TYPE.AUDIO ],
firePermissionPromptIsShownEvent: true,
timeout
}));
}
@@ -171,7 +164,6 @@ export function createPrejoinTracks() {
if (requestedVideo) {
gUMPromises.push(createLocalTracksF({
devices: [ MEDIA_TYPE.VIDEO ],
firePermissionPromptIsShownEvent: true,
timeout
}));
}

View File

@@ -15,7 +15,6 @@ export interface ITrackOptions {
desktopSharingSources?: string[];
devices?: string[];
facingMode?: string;
firePermissionPromptIsShownEvent?: boolean;
micDeviceId?: string | null;
timeout?: number;
}
@@ -76,7 +75,6 @@ export interface IShareOptions {
export interface ICreateInitialTracksOptions {
devices: Array<MediaType>;
firePermissionPromptIsShownEvent?: boolean;
timeout?: number;
}

View File

@@ -62,7 +62,7 @@ const IconButton: React.FC<IIconButtonProps> = ({
underlayColor = { underlayColor }>
<Icon
color = { color }
size = { 20 || size }
size = { size ?? 20 }
src = { src } />
</TouchableHighlight>
);

View File

@@ -244,9 +244,9 @@ const ContextMenu = ({
list: Element | null,
currentFocus: Element | null,
traversalFunction: (
list: Element | null,
currentFocus: Element | null
) => Element | null
list: Element | null,
currentFocus: Element | null
) => Element | null
) => {
let wrappedOnce = false;
let nextFocus = traversalFunction(list, currentFocus);

View File

@@ -141,7 +141,7 @@ const useStyles = makeStyles()(theme => {
});
interface IObject {
[key: string]: string | string[] | boolean | number | number[] | {} | undefined;
[key: string]: unknown;
}
export interface IDialogTab<P> {

View File

@@ -98,6 +98,9 @@ const useStyles = makeStyles()(theme => {
'&.error': {
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
},
'&.clearable-input': {
paddingRight: '46px'
}
},
@@ -121,10 +124,6 @@ const useStyles = makeStyles()(theme => {
cursor: 'pointer'
},
clearableInput: {
paddingRight: '46px'
},
clearButton: {
position: 'absolute',
right: '16px',
@@ -185,6 +184,7 @@ const Input = React.forwardRef<any, IProps>(({
}: IProps, ref) => {
const { classes: styles, cx } = useStyles();
const isMobile = isMobileBrowser();
const showClearIcon = clearable && value !== '' && !disabled;
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
onChange?.(e.target.value), []);
@@ -211,7 +211,7 @@ const Input = React.forwardRef<any, IProps>(({
autoComplete = { autoComplete }
autoFocus = { autoFocus }
className = { cx(styles.input, isMobile && 'is-mobile',
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
error && 'error', showClearIcon && 'clearable-input', icon && 'icon-input') }
disabled = { disabled }
id = { id }
maxLength = { maxLength }
@@ -232,7 +232,7 @@ const Input = React.forwardRef<any, IProps>(({
autoComplete = { autoComplete }
autoFocus = { autoFocus }
className = { cx(styles.input, isMobile && 'is-mobile',
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
error && 'error', showClearIcon && 'clearable-input', icon && 'icon-input') }
data-testid = { testId }
disabled = { disabled }
id = { id }
@@ -252,7 +252,7 @@ const Input = React.forwardRef<any, IProps>(({
type = { type }
value = { value } />
)}
{clearable && !disabled && value !== '' && <button className = { styles.clearButton }>
{showClearIcon && <button className = { styles.clearButton }>
<Icon
onClick = { clearInput }
size = { 20 }

View File

@@ -19,12 +19,12 @@ interface ISelectProps {
className?: string;
/**
* Wether or not the select is disabled.
* Whether or not the select is disabled.
*/
disabled?: boolean;
/**
* Wether or not the select is in the error state.
* Whether or not the select is in the error state.
*/
error?: boolean;

View File

@@ -5,9 +5,9 @@ import { makeStyles } from 'tss-react/mui';
import { TEXT_OVERFLOW_TYPES } from '../../constants.web';
interface ITextWithOverflowProps {
children: ReactNode;
className?: string;
overflowType?: TEXT_OVERFLOW_TYPES;
children: ReactNode;
className?: string;
overflowType?: TEXT_OVERFLOW_TYPES;
}
const useStyles = makeStyles<{ translateDiff: number; }>()((_, { translateDiff }) => {

View File

@@ -13,7 +13,7 @@ import { SECURITY_URL } from './contants';
*
* @param {IReduxState} state - The redux state.
* @param {Function} t - The translation function.
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warining.
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warning.
* @returns {Text}
*/
export default function getUnsafeRoomText(state: IReduxState, t: Function, context: 'meeting' | 'prejoin' | 'welcome') {

View File

@@ -6,7 +6,7 @@ import { SECURITY_URL } from './contants';
* Gets the unsafe room text for the given context.
*
* @param {Function} t - The translation function.
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warining.
* @param {'meeting'|'prejoin'|'welcome'} context - The given context of the warning.
* @returns {string}
*/
export default function getUnsafeRoomText(t: Function, context: 'meeting' | 'prejoin' | 'welcome') {

View File

@@ -22,27 +22,6 @@ export function assignIfDefined(target: Object, source: Object) {
return to;
}
export type DefferedPromise<T> = {
promise: Promise<T>;
reject: (reason?: any) => void;
resolve: (value: T) => void;
};
/**
* Creates a deferred object.
*
* @returns {{promise, resolve, reject}}
*/
export function createDeferred<T>() {
const deferred = {} as DefferedPromise<T>;
deferred.promise = new Promise<T>((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
const MATCH_OPERATOR_REGEXP = /[|\\{}()[\]^$+*?.-]/g;

View File

@@ -18,7 +18,7 @@ import CalendarListContent from './CalendarListContent.native';
import styles from './styles';
/**
* The tyoe of the React {@code Component} props of {@link CalendarList}.
* The type of the React {@code Component} props of {@link CalendarList}.
*/
interface IProps extends WithTranslation {

View File

@@ -152,7 +152,7 @@ export default createStyleSheet({
},
/**
* The container for all the lines if the norification.
* The container for all the lines if the notification.
*/
notificationTextContainer: {
flexDirection: 'column',

View File

@@ -5,8 +5,6 @@ import { v4 as uuidV4 } from 'uuid';
import { findWindows } from 'windows-iana';
import { IanaName } from 'windows-iana/dist/enums';
// @ts-expect-error
import { createDeferred } from '../../../../modules/util/helpers';
import { IStore } from '../../app/types';
import { parseURLParams } from '../../base/util/parseURLParams';
import { parseStandardURIString } from '../../base/util/uri';
@@ -153,8 +151,7 @@ export const microsoftCalendarApi = {
return Promise.reject('Sign in already in progress.');
}
const signInDeferred = createDeferred();
const signInDeferred = Promise.withResolvers();
const guids = {
authState: uuidV4(),
authNonce: uuidV4()
@@ -229,7 +226,7 @@ export const microsoftCalendarApi = {
userSigninName: tokenParts.userSigninName
}));
signInDeferred.resolve();
signInDeferred.resolve(undefined);
}
window.addEventListener('message', handleAuth);

View File

@@ -1,5 +1,5 @@
import { Theme } from '@mui/material';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { connect } from 'react-redux';
import { makeStyles } from 'tss-react/mui';
@@ -264,7 +264,7 @@ const ChatMessage = ({
*
* @returns {React$Element<*>}
*/
const renderReactions = () => {
const renderReactions = useMemo(() => {
if (!message.reactions || message.reactions.size === 0) {
return null;
}
@@ -321,7 +321,7 @@ const ChatMessage = ({
</div>
</Popover>
);
};
}, [ message?.reactions, isHovered, isReactionsOpen ]);
return (
<div
@@ -366,7 +366,7 @@ const ChatMessage = ({
<div className = { classes.chatMessageFooterLeft }>
{message.reactions && message.reactions.size > 0 && (
<>
{renderReactions()}
{renderReactions}
</>
)}
</div>

View File

@@ -117,7 +117,7 @@ class DisplayNameForm extends Component<IProps, IState> {
* @returns {void}
*/
_onSubmit(event: any) {
event?.preventDefault && event.preventDefault();
event?.preventDefault?.();
// Store display name in settings
this.props.dispatch(updateSettings({

View File

@@ -349,8 +349,8 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
*/
function _onConferenceMessageReceived(store: IStore,
{ displayName, isGuest, message, messageId, participantId, privateMessage, timestamp }: {
displayName?: string; isGuest?: boolean; message: string; messageId?: string;
participantId: string; privateMessage: boolean; timestamp: number; }
displayName?: string; isGuest?: boolean; message: string; messageId?: string;
participantId: string; privateMessage: boolean; timestamp: number; }
) {
const isGif = isGifEnabled(store.getState()) && isGifMessage(message);
@@ -479,8 +479,8 @@ function getLobbyChatDisplayName(state: IReduxState, participantId: string) {
*/
function _handleReceivedMessage({ dispatch, getState }: IStore,
{ displayName, isGuest, lobbyChat, message, messageId, participantId, privateMessage, timestamp }: {
displayName?: string; isGuest?: boolean; lobbyChat: boolean; message: string;
messageId?: string; participantId: string; privateMessage: boolean; timestamp: number; },
displayName?: string; isGuest?: boolean; lobbyChat: boolean; message: string;
messageId?: string; participantId: string; privateMessage: boolean; timestamp: number; },
shouldPlaySound = true,
isReaction = false
) {

View File

@@ -11,12 +11,12 @@ import { DISMISS_CALENDAR_NOTIFICATION } from './actionTypes';
*
* @param {JitsiParticipant} participant - The {@link JitsiParticipant}
* instance which initiated the kick event.
* @param {?Function} submit - The function to execute after submiting the dialog.
* @param {?Function} submit - The function to execute after submitting the dialog.
* @returns {Function}
*/
export function notifyKickedOut(participant: any, submit?: Function) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
if (!participant || participant?.isReplaced?.()) {
if (participant?.isReplaced?.()) {
submit?.();
return;
@@ -24,9 +24,9 @@ export function notifyKickedOut(participant: any, submit?: Function) {
dispatch(openDialog(AlertDialog, {
contentKey: {
key: 'dialog.kickTitle',
key: participant ? 'dialog.kickTitle' : 'dialog.kickSystemTitle',
params: {
participantDisplayName: getParticipantDisplayName(getState, participant.getId())
participantDisplayName: participant && getParticipantDisplayName(getState, participant.getId())
}
},
onSubmit: submit
@@ -38,7 +38,7 @@ export function notifyKickedOut(participant: any, submit?: Function) {
* Notify that we've been kicked out of the conference.
*
* @param {string} reasonKey - The translation key for the reason why the conference failed.
* @param {?Function} submit - The function to execute after submiting the dialog.
* @param {?Function} submit - The function to execute after submitting the dialog.
* @returns {Function}
*/
export function notifyConferenceFailed(reasonKey: string, submit?: Function) {

View File

@@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
@@ -7,6 +8,7 @@ import { IconUsers } from '../../../base/icons/svg';
import Label from '../../../base/label/components/web/Label';
import { COLORS } from '../../../base/label/constants';
import { getParticipantCount } from '../../../base/participants/functions';
import Tooltip from '../../../base/tooltip/components/Tooltip';
import SpeakerStats from '../../../speaker-stats/components/web/SpeakerStats';
import { isSpeakerStatsDisabled } from '../../../speaker-stats/functions';
import { iAmVisitor } from '../../../visitors/functions';
@@ -23,6 +25,7 @@ function SpeakerStatsLabel() {
const iAmVisitorState = useSelector(iAmVisitor);
const _isSpeakerStatsDisabled = useSelector(isSpeakerStatsDisabled);
const dispatch = useDispatch();
const { t } = useTranslation();
// visitor has hidden its own video and should not count itself
if (iAmVisitorState) {
@@ -38,13 +41,17 @@ function SpeakerStatsLabel() {
}
return (
<Label
color = { COLORS.white }
icon = { IconUsers }
iconColor = '#fff'
// eslint-disable-next-line react/jsx-no-bind
onClick = { onClick }
text = { `${count}` } />
<Tooltip
content = { t('speakerStats.labelTooltip', { count }) }
position = { 'bottom' }>
<Label
color = { COLORS.white }
icon = { IconUsers }
iconColor = '#fff'
// eslint-disable-next-line react/jsx-no-bind
onClick = { onClick }
text = { `${count}` } />
</Tooltip>
);
}

View File

@@ -8,5 +8,5 @@ export const IFRAME_DISABLED_TIMEOUT_MINUTES = 5;
/**
* A list of allowed location to embed iframe.
*/
/* eslint-disable-next-line no-extra-parens*/
/* eslint-disable-next-line no-extra-parens */
export const IFRAME_EMBED_ALLOWED_LOCATIONS = ([] as string[]).concat(ADDITIONAL_LOCATIONS);

View File

@@ -27,12 +27,17 @@ MiddlewareRegistry.register(store => next => action => {
const { dispatch } = store;
const { participant } = action;
// we need to first finish dispatching or the notification can be cleared out
const result = next(action);
const participantDisplayName
= getParticipantDisplayName(store.getState, participant.getId());
= participant && getParticipantDisplayName(store.getState, participant.getId());
dispatch(hangup(true, i18next.t('dialog.kickTitle', { participantDisplayName })));
dispatch(hangup(true,
participantDisplayName ? i18next.t('dialog.kickTitle', { participantDisplayName })
: i18next.t('dialog.kickSystemTitle')));
break;
return result;
}
}

View File

@@ -99,7 +99,7 @@ interface IProps extends AbstractDialogTabProps, WithTranslation {
iAmVisitor: boolean;
/**
* Wether noise suppression is on or not.
* Whether noise suppression is on or not.
*/
noiseSuppressionEnabled: boolean;

View File

@@ -51,7 +51,7 @@ class DisplayNameLabel extends React.Component<IProps> {
return (
<View
style = { (this.props.contained ? styles.displayNamePadding : styles.displayNameBackdrop
) as ViewStyle }>
) as ViewStyle }>
<Text
numberOfLines = { 1 }
style = { styles.displayNameText as TextStyle }>

View File

@@ -106,7 +106,8 @@ const StageParticipantNameLabel = () => {
classes.badgeContainer,
toolboxVisible && classes.containerElevated,
_isScreenShareParticipant && classes.screenSharing
) }>
) }
data-testid = 'stage-display-name' >
<DisplayNameBadge name = { nameToDisplay } />
</div>
);

View File

@@ -50,7 +50,7 @@ export function setMediaEncryptionKey(keyInfo: Object) {
}
/**
* Dispatches an action to start participant e2ee verficiation process.
* Dispatches an action to start participant e2ee verification process.
*
* @param {string} pId - The participant id.
* @returns {{
@@ -69,7 +69,7 @@ export function startVerification(pId: string) {
* Dispatches an action to set participant e2ee verification status.
*
* @param {string} pId - The participant id.
* @param {boolean} isVerified - The verifcation status.
* @param {boolean} isVerified - The verification status.
* @returns {{
* type: PARTICIPANT_VERIFIED,
* pId: string,

View File

@@ -153,8 +153,8 @@ MiddlewareRegistry.register(store => next => action => {
local: true
},
{
id: actor.getId(),
name: actor.getDisplayName()
id: actor?.getId(),
name: actor?.getDisplayName()
}
);
break;

View File

@@ -150,7 +150,7 @@ class ThumbnailWrapper extends Component<IProps> {
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState, ownProps: { columnIndex: number;
data: { filmstripType: string; }; index?: number; rowIndex: number; }) {
data: { filmstripType: string; }; index?: number; rowIndex: number; }) {
const _currentLayout = getCurrentLayout(state);
const { remoteParticipants: remote } = state['features/filmstrip'];
const activeParticipants = getActiveParticipantsIds(state);

View File

@@ -57,7 +57,7 @@ export function updateRemoteParticipants(store: IStore, force?: boolean, partici
remoteParticipants.delete(speaker);
}
// Always update the order of the thumnails.
// Always update the order of the thubmnails.
const participantsWithScreenShare = screenShareParticipants.reduce<string[]>((acc, screenshare) => {
const ownerId = getVirtualScreenshareParticipantOwnerId(screenshare);

View File

@@ -254,7 +254,7 @@ export function getFilmstripDimensions({
/**
* Returns true if the local thumbnail should be displayed separately and false otherwise.
*
* @returns {boolean} - True if the local thumbnail should be displayed separately and flase otherwise.
* @returns {boolean} - True if the local thumbnail should be displayed separately and false otherwise.
*/
export function shouldDisplayLocalThumbnailSeparately() {
// XXX Our current design is to have the local participant separate from
@@ -287,4 +287,3 @@ export function getScreenshareFilmstripParticipantId(_state: any) {
return undefined;
}

View File

@@ -322,7 +322,7 @@ export function calculateResponsiveTileViewDimensions({
for (let c = 1; c <= Math.min(maxColumns, numberOfParticipants, desiredNumberOfVisibleTiles); c++) {
const r = Math.ceil(numberOfParticipants / c);
// we want to display as much as possible tumbnails up to desiredNumberOfVisibleTiles
// we want to display as much as possible thumbnails up to desiredNumberOfVisibleTiles
const visibleRows
= numberOfParticipants <= desiredNumberOfVisibleTiles ? r : Math.floor(desiredNumberOfVisibleTiles / c);

View File

@@ -20,6 +20,7 @@ document.addEventListener('DOMContentLoaded', () => {
const ix = href.indexOf(DIAL_IN_INFO_PAGE_PATH_NAME);
const url = (ix > 0 ? href.substring(0, ix) : href) + room;
/* eslint-disable-next-line react/no-deprecated */
ReactDOM.render(
<I18nextProvider i18n = { i18next }>
{ room
@@ -34,6 +35,7 @@ document.addEventListener('DOMContentLoaded', () => {
);
});
window.addEventListener('beforeunload', () => { // @ts-ignore
ReactDOM.unmountComponentAtNode(document.getElementById('react'));
window.addEventListener('beforeunload', () => {
/* eslint-disable-next-line react/no-deprecated */
ReactDOM.unmountComponentAtNode(document.getElementById('react')!);
});

View File

@@ -494,7 +494,7 @@ export function isDialOutEnabled(state: IReduxState): boolean {
const isModerator = isLocalParticipantModerator(state);
return isJwtFeatureEnabled(state, 'outbound-call', isModerator, false)
&& conference && conference.isSIPCallingSupported();
&& conference?.isSIPCallingSupported();
}
/**

View File

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

View File

@@ -84,7 +84,7 @@ export function updateKnownLargeVideoResolution(resolution: number) {
}
/**
* Sets the dimenstions of the large video in redux.
* Sets the dimensions of the large video in redux.
*
* @param {number} height - The height of the large video.
* @param {number} width - The width of the large video.

View File

@@ -37,8 +37,8 @@ export function captureLargeVideoScreenshot() {
}
// Get the video element for the large video, cast HTMLElement to HTMLVideoElement to make flow happy.
/* eslint-disable-next-line no-extra-parens*/
const videoElement = (document.getElementById('largeVideo') as any);
/* eslint-disable-next-line no-extra-parens */
const videoElement = document.getElementById('largeVideo') as any;
if (!videoElement) {
return promise;

View File

@@ -214,8 +214,7 @@ export class LargeVideoBackground extends Component<IProps> {
// because we don't have a good way to do it.
// All other cases when the canvas is not visible are handled through the component props
// (hidden, _shouldDisplayTileView).
if (!this._canvasEl || this._canvasEl.offsetParent === null
|| window.innerHeight === 0 || window.innerWidth === 0) {
if (!this._canvasEl?.offsetParent || window.innerHeight === 0 || window.innerWidth === 0) {
return;
}

View File

@@ -486,7 +486,7 @@ function _syncTrackState({ getState }: IStore, next: Function, action: AnyAction
const state = getState();
const conference = getCurrentConference(state);
if (jitsiTrack.isLocal() && conference && conference.callUUID) {
if (jitsiTrack.isLocal() && conference?.callUUID) {
switch (jitsiTrack.getType()) {
case 'audio': {
_updateCallIntegrationMuted(conference, state);

View File

@@ -9,7 +9,7 @@ import { isLocalVideoTrackDesktop } from '../../base/tracks/functions.any';
* Checks whether full-screen state should be used or not.
*
* @param {IReduxState} state - The redux state.
* @returns {boolean} - Whether full-screen state shuld be used or not.
* @returns {boolean} - Whether full-screen state should be used or not.
*/
export function shouldUseFullScreen(state: IReduxState) {
const { enabled: audioOnly } = state['features/base/audio-only'];

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