Compare commits

...

107 Commits

Author SHA1 Message Date
Hristo Terezov
ba95ea3ac8 feat(conference): Add init debug log 2024-02-20 09:42:07 -06:00
Besnik Bleta
7dcd9c21bc fix(lang) update for sq translation (#14387) 2024-02-20 15:23:14 +01:00
damencho
990d21038e fix: Fixes initializing cached keys. 2024-02-16 11:36:07 -06:00
Mihaela Dumitru
a4d53f271f fix(ui) center conference info (#14379) 2024-02-16 14:37:12 +02:00
bgrozev
2a1f472873 Add a second shared secret to allow rotation if necessary. (#14378) 2024-02-15 15:00:30 -08:00
damencho
8679119677 fix: Change whitelist to support only domains. 2024-02-15 16:36:37 -06:00
damencho
b02c072ba7 fix: Handles few more cases for updating cached tokens. 2024-02-15 12:20:02 -06:00
Saúl Ibarra Corretgé
e8317fccfe fix(local-recordings) fix on Chrome
WebM + VP8 seems to be the one thing that works, requesting H.264 starts
the recorder in Matroska + AV1 format (WAT) but no data is received at
all.
2024-02-15 17:49:45 +01:00
Saúl Ibarra Corretgé
bbc9c64978 fix(recording) only show advanced section for cloud recordings 2024-02-15 17:49:45 +01:00
Saúl Ibarra Corretgé
8d2b8bc772 fix(recording) fix checking if we can skip the recording dialog 2024-02-15 15:15:38 +01:00
damencho
52c2911350 feat: Adds stats around room token validation. 2024-02-15 07:13:12 -06:00
damencho
a1ebcd559b feat: Adds a print for timing out http requests after the retry. 2024-02-15 07:13:12 -06:00
damencho
5bc47ec16a feat: Adds stat when a rayo IQ is filtered. 2024-02-15 07:13:12 -06:00
damencho
e1ac7d1609 feat: Adds stats around token validation. 2024-02-15 07:13:12 -06:00
damencho
8a596f1ba2 fix: Drops unused token validation. 2024-02-15 07:13:12 -06:00
damencho
38e7c65836 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1776.0.0+f5f8a137...v1777.0.0+3898d7aa
2024-02-14 22:52:49 -05:00
Jaya Allamsetty
9602a939d8 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1775.0.0+c7495280...v1776.0.0+f5f8a137
2024-02-14 12:32:39 -05:00
Calinteodor
07b01b1371 feat(conference/recordin): Fix always on labels (#14368)
* feat(conference/recording): condition live stream label visibility and fix labels tooltip translations
2024-02-14 13:11:57 +02:00
damencho
a1549086aa chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1773.0.0+cc9b0e60...v1775.0.0+c7495280
2024-02-13 14:02:28 -06:00
Saúl Ibarra Corretgé
7e0b00ba5f feat(prejoin) default to enabled (#14236)
* feat(prejoin) default to enabled

* squash: Fixes is prejoin config check.

---------

Co-authored-by: damencho <damencho@jitsi.org>
2024-02-13 12:47:59 -06:00
Calin-Teodor
3016853d81 feat(android): replaced NotificationChannels with NotificationUtils 2024-02-13 20:03:36 +02:00
Calinteodor
ead27ace30 feat(android): update media projection notification build (#14365)
* feat(android): update media projection notification build
2024-02-13 19:25:46 +02:00
Дамян Минков
067bb653e6 feat(лоббъ): Admin set function for multiple items.
* squash: Initial copy of the function.

* squash: Invokes the admin set function.

* feat: Admin set function for multiple items.
2024-02-13 10:29:41 -06:00
Calinteodor
ba20fc71a8 feat(android): separate MediaProjection and OngoingConference notifications (#14363)
* feat(android): separate MediaProjection and OngoingConference notifications
2024-02-13 17:54:01 +02:00
Mihaela Dumitru
a7b2726ebe fix(prejoin) adjust recording warning (#14361) 2024-02-13 13:12:10 +02:00
Calinteodor
a98eef7eb3 feat(android): fixed screen sharing for Android 33 (#14359)
* feat(android): fixed screen sharing for Android 33
2024-02-13 12:33:37 +02:00
damencho
895afbab65 fix: Fixes polls parsing. 2024-02-12 13:21:06 -06:00
Saúl Ibarra Corretgé
1d6529af65 feat(transcript) add ability to dump transcript for debugging 2024-02-12 13:07:19 +01:00
Saúl Ibarra Corretgé
4d5fb719d2 fix(whiteboard) handle metadata being null 2024-02-12 13:05:53 +01:00
Jaya Allamsetty
4061a77af8 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1770.0.0+2d430a18...v1773.0.0+cc9b0e60
2024-02-09 11:20:51 -05:00
Mihaela Dumitru
7f889b2028 feat(prejoin) display recording warning (#14347) 2024-02-09 11:00:28 +02:00
bgrozev
6f49041d80 feat: Notify jicofo when room_metadata changes the config form. (#14346) 2024-02-08 14:58:02 -08:00
Avram Tudor
e73c3b6697 feat(recordings) send recordings metadata
* ref: centralise conference metadata updates

* feat(recordings) send recordings metadata
2024-02-08 21:38:06 +02:00
Avram Tudor
5a6b1d0b47 code review 2024-02-08 17:41:55 +02:00
Avram Tudor
da9cded75b fix(recording) ensure recording label is shown when transcription is on 2024-02-08 17:41:55 +02:00
Avram Tudor
9b61ad3616 fix(recording) do not show advanced options for users with local recording rights 2024-02-08 17:41:55 +02:00
Avram Tudor
583725bf31 fix(recording) do not show stop recording option in some wrong cases 2024-02-08 17:41:55 +02:00
Avram Tudor
9e2244210d ref: add selector to check if meeting is transcribed 2024-02-08 17:41:55 +02:00
Avram Tudor
361b82a1ed ref: add selector for checking cloud recording running status 2024-02-08 17:41:55 +02:00
Saúl Ibarra Corretgé
0ed25cda7e fix(ci) use the right CocoaPods version 2024-02-08 15:08:15 +01:00
Calinteodor
73ee1205eb feat(premeeting/prejoin): Control room name visibility (#14339)
feat(premeeting/prejoin): Control room name visibility (#14339)
2024-02-08 15:56:05 +02:00
damencho
21f2c60638 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1769.0.0+ff9f57c5...v1770.0.0+2d430a18
2024-02-07 14:53:46 -06:00
damencho
fd062c40fb fix: Use same format for anonymous jids as token module. 2024-02-07 14:53:36 -06:00
damencho
b87e6abc11 feat: Sends event on not allowed error.
Updates amplitude to include the update about sending events on exiting the page.
2024-02-07 13:08:35 -06:00
Avram Tudor
9f25726706 fix(recording) fix recording suggestion not being shown in some cases (#14333)
Initial implementation did not account for cases where participants become moderators
2024-02-07 15:36:43 +02:00
Calinteodor
d5ee7f3069 feat(mobile/polyfills): strophe.js for react native (#14329)
* chore(deps, abab): polyfill using dependency for strophe in react native
2024-02-06 18:28:35 +02:00
damencho
ba1102100a feat(visitors): Adds conference duration for visitors. 2024-02-06 10:16:30 -06:00
damencho
e1ce83d0c3 feat: Use room data form for conference duration.
Deprecates current component for conference duration. Will wait a bit for mobile to catch up and then we can delete the code.

chore(deps) lib-jitsi-meet@latest
jitsi/lib-jitsi-meet@v1767.0.0+178f9bbf...v1769.0.0+ff9f57c5
2024-02-06 10:16:30 -06:00
damencho
78cf510c0b feat: Moves common process_host_module to util. 2024-02-06 10:16:30 -06:00
Mihaela Dumitru
3f657c3ded feat(whiteboard) add native implementation (#14327) 2024-02-06 16:22:53 +02:00
Saúl Ibarra Corretgé
2035cd7e62 fix(recording) avoid having duplicate notification sounds 2024-02-06 11:14:12 +01:00
Saúl Ibarra Corretgé
6207e95cad fix(recording) fix default value to auto-transcribe-on-record 2024-02-06 11:14:12 +01:00
Calinteodor
40b63a187a feat(conference): remove one2one check for diplaying participants name (#14324)
* feat(conference): remove one2one check for diplaying participants name
2024-02-06 10:54:27 +02:00
Saúl Ibarra Corretgé
6c40250e18 fix(conference) avoid adding extra endpoint message listeners 2024-02-06 07:40:40 +01:00
Saúl Ibarra Corretgé
0268374b88 fix(conference) move endpointMessageReceived action
It's used for a myrias of things, not just subtitles.
2024-02-06 07:40:40 +01:00
Jaya Allamsetty
b1b60ec143 fix(config): Fix the typo in Av1 support flag. 2024-02-05 13:27:29 -05:00
Дамян Минков
82c58178b3 feat(visitors): Adds notification for promotion requests. (#14316)
* feat(visitors): Adds notification for promotion requests.

* squash: fixes lint.
2024-02-02 14:58:24 -06:00
Jaya Allamsetty
46d15a9c5c chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1764.0.0+ba21a236...v1767.0.0+178f9bbf
2024-02-02 14:58:09 -06:00
Hristo Terezov
248908f476 fix(lobby): Hide enable option if there is a visitor 2024-02-02 14:20:16 -06:00
Hristo Terezov
4a5ba0f05d fix(visitors): Disable polls. 2024-02-02 12:50:01 -06:00
damencho
b23f3b9e17 feat(visitors): Whitelist trusted domains when promoting. 2024-02-02 12:01:59 -06:00
Дамян Минков
b6b77f55f1 feat: Drops some debug prints. (#14311)
* feat: Drops some debug prints.

* squash: Fix linter errors.
2024-02-02 11:11:50 -06:00
damencho
b106c20fa2 feat: Adds indication for visitors messages to event. 2024-02-01 21:06:25 -06:00
Hristo Terezov
fab61d8c32 fix(reactions): Show when received even if UI is disabled 2024-02-01 18:08:19 -06:00
Hristo Terezov
9ac614cc4b fix(shortcut): Unregister for altKey shortcuts. 2024-02-01 18:08:19 -06:00
Hristo Terezov
a3bb1a3459 feat(visitors): Hide reactions. 2024-02-01 18:08:19 -06:00
Avram Tudor
34abd279be feat(recording) add notification to suggest recording at meeting startup (#14296)
* feat(recording) add notification to suggest recording at meeting startup

* code review changes

* update strings

* fix mobile

* fix lint
2024-02-01 17:08:41 +02:00
Saúl Ibarra Corretgé
ad6554a789 fix(transcriptions) obey skipInterimTranscriptions for chunk events 2024-02-01 10:43:10 +01:00
Saúl Ibarra Corretgé
2be8377009 fix(transcriptions) avoid generating duplicated events 2024-02-01 10:43:10 +01:00
damencho
0a0b0a760e fix: Fixes promoting the only moderator in a non vpass meeting. 2024-01-31 17:53:16 -06:00
Дамян Минков
fec6de4536 fix(visitors): Lowers visitor's hand on leave. (#14302)
* fix(visitors): Lowers visitor's hand on leave.

* squash: Drop not needed nick.

That is used only when raising hand.
2024-01-31 11:49:16 -06:00
damencho
29b2a519b3 fix(visitors): Handles promotion rejection msg.
If not handled an error is returned that the stanza is not handled (service-unavailable error) and displayed in UI.
2024-01-31 07:37:43 -06:00
Hristo Terezov
831d39d447 fix(visitors): hide highlight button. 2024-01-30 17:57:05 -06:00
damencho
35bc430549 fix(visitors): Fixes clearing promotion requests.
We were modifying DEFAULT_STATE and later the request magically appears after the state is cleared, like joining and leaving breakout rooms.
2024-01-30 17:56:16 -06:00
damencho
77927f3f2e chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1763.0.0+8a88bba7...v1764.0.0+ba21a236
2024-01-30 16:34:08 -05:00
Jaya Allamsetty
7f6d55f5f0 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1762.0.0+841b3eb8...v1763.0.0+8a88bba7
2024-01-30 13:32:31 -05:00
Edgars Voroboks
32f4767edd fix(lang) Update Latvian translation 2024-01-30 09:26:11 +01:00
damencho
ba88fc0279 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1761.0.0+f470b5f4...v1762.0.0+841b3eb8
2024-01-29 15:34:33 -06:00
Saúl Ibarra Corretgé
ba7ed83d48 feat(recording) stop transcription when recording is stopped 2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
5a96050973 feat(recording) always show advanced options in dialog 2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
7e8c1fd99a feat(recording) show recording icon when transcribing 2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
6e6433e2d9 feat(transcribing) emit recording notifications for transcriptions
Both audible and visual.

They are only emitted if there is no video recording on.
2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
4ff2422e3c feat(subtitles) drop disableStartForAll config option
It wasn't related to transcriptions but to subtitles, really.

Since we are not going to show subtitles just because transcriptions are
enabled, this setting no longer makes sense.
2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
cfbcfdc857 feat(recording) update labels with transcription status 2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
3074b1741f feat(conference) drop transcribing label 2024-01-29 15:09:28 +01:00
Saúl Ibarra Corretgé
00503d163a feat(transcribing) drop transcribing notifications 2024-01-29 15:09:28 +01:00
Christoph Settgast
f847a3e2e0 fix(deeplinking): allow translating all strings on deeplinking pages (#14286) 2024-01-28 13:26:53 +01:00
Christoph Settgast
93d0ee710b fix(deeplinking): allow partial config of deeplinking and defaults for the remaining (#14285) 2024-01-28 13:26:16 +01:00
Christoph Settgast
0102efd2d0 fix(deeplinking): skip deeplinking within electron (#14284)
otherwise we are stuck in a loop inside the desktop app

Signed-off-by: Christoph Settgast <csett86_git@quicksands.de>
2024-01-27 16:34:33 +01:00
Christoph Settgast
8afdebca08 feat(deeplinking): offer electron app for download, configurable (#14261)
Offer the default app, but make it configurable for own branded
desktop clients

Signed-off-by: Christoph Settgast <csett86_git@quicksands.de>
2024-01-26 22:04:44 +01:00
Göktuğ Aşcı
d2bfb464ba lang: add translation tr (#14282) 2024-01-26 13:52:56 -06:00
Jaya Allamsetty
5f3233b9a6 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1760.0.0+7895cb25...v1761.0.0+f470b5f4
2024-01-26 10:31:29 -05:00
Hristo Terezov
3c32d9c552 feat(LargeVideo): Add logging. 2024-01-25 09:31:38 -06:00
Hristo Terezov
f51d8d54e4 fix(LargeVideo): Add retries for play. 2024-01-25 09:31:38 -06:00
Saúl Ibarra Corretgé
ac26dce0a5 feat(android) list HDMI connections as speakers 2024-01-25 10:59:21 +01:00
Jaya Allamsetty
7a23625752 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1759.0.0+fc2f2490...v1760.0.0+7895cb25
2024-01-24 16:47:05 -05:00
Corey Maler
4cb4a4db00 fix(participant) prevent gravatar request during loading of config (#14255) 2024-01-24 13:32:59 +01:00
Sveinn í Felli
05e484c4aa fix(lang) Update main-is.json (#14270)
Updated Icelandic translation
2024-01-22 20:39:00 +01:00
Дамян Минков
5871e50a20 * feat: Stores vpaas check in room object.
* feat: Clear queues on destroy for muc rate limit join/leave.

* feat: Stores vpaas check in room object.

* squash: Replace one regexp with starts_with.
2024-01-22 11:48:35 -06:00
Damien Fetis
95ad04b19c fix(lang) update french translation (#14269) 2024-01-22 17:59:46 +01:00
damencho
36fab239cc fix: Updates checking for granted feature. 2024-01-22 09:28:48 -06:00
Saúl Ibarra Corretgé
92492dc482 feat(ci,ios) check if Pod file is in sync when running CI 2024-01-22 13:49:42 +01:00
Saúl Ibarra Corretgé
51c3658205 fix(ios) sync Podfile 2024-01-22 12:56:17 +01:00
Saúl Ibarra Corretgé
418d333caf feat(rn,invite) add ability to disable dial-in options 2024-01-22 12:56:17 +01:00
Christoph Settgast
7f3f2f7034 lang: typo in German translation 2024-01-21 18:06:30 -06:00
Christoph Settgast
bd3012f110 feat(deeplinking): Desktop deeplinking configurable (default off) and using jitsi-meet-electron (#14259)
As we have the jitsi-meet-electron app, lets allow deployments to use it.

Allow deployments to enable desktop deeplinking without the need to re-implement _openDesktopApp()
Disable it by default to keep the current behaviour (deeplinking on mobile on, on desktop off)

This feature is meant as purely opt-in.

Signed-off-by: Christoph Settgast <csett86_git@quicksands.de>
2024-01-21 21:02:16 +01:00
Saúl Ibarra Corretgé
f8cef330f7 feat(android) set target SDK to 34 (#14129)
* feat(android) set target SDK to 34 and fixed mediaProjection service type
2024-01-19 16:52:39 +02:00
210 changed files with 4752 additions and 1898 deletions

View File

@@ -73,6 +73,17 @@ jobs:
node-version: 16
cache: 'npm'
- run: npm install
- name: setup-cocoapods
uses: maxim-lobanov/setup-cocoapods@v1
with:
podfile-path: ios/Podfile.lock
- name: Install Pods
run: |
pod --version
cd ios
pod install --repo-update
- name: Check if the git repository is clean
run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1)
- run: npx react-native bundle --entry-file react/index.native.js --platform ios --bundle-output /tmp/ios.bundle --reset-cache
debian-build:
name: Test Debian packages build

View File

@@ -15,11 +15,13 @@ def vcode = (int) (((new Date().getTime() / 1000) - 1546297200) / 10)
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
packagingOptions {
exclude 'lib/*/libhermes*.so'
jniLibs {
excludes += ['lib/*/libhermes*.so']
}
}
defaultConfig {
applicationId 'org.jitsi.meet'
versionCode vcode
@@ -72,12 +74,13 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
namespace 'org.jitsi.meet'
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.13'
if (!rootProject.ext.libreBuild) {
// Sync with react-native-google-signin

View File

@@ -1,6 +1,5 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jitsi.meet"
android:installLocation="auto">
<application
android:allowBackup="true"

View File

@@ -10,7 +10,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.google.gms:google-services:4.4.0'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
}

View File

@@ -30,6 +30,7 @@ android {
}
}
}
namespace 'org.jitsi.meet.sdk'
}
dependencies {

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.jitsi.meet.sdk">
xmlns:tools="http://schemas.android.com/tools">
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
@@ -13,6 +12,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-feature
android:glEsVersion="0x00020000"
@@ -48,6 +49,10 @@
<service
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
android:foregroundServiceType="mediaPlayback" />
<service
android:name="org.jitsi.meet.sdk.JitsiMeetMediaProjectionService"
android:foregroundServiceType="mediaProjection" />
<provider
@@ -66,4 +71,4 @@
</application>
</manifest>
</manifest>

View File

@@ -87,6 +87,7 @@ class AudioDeviceHandlerGeneric implements
devices.add(AudioModeModule.DEVICE_EARPIECE);
break;
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
case AudioDeviceInfo.TYPE_HDMI:
devices.add(AudioModeModule.DEVICE_SPEAKER);
break;
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:

View File

@@ -0,0 +1,44 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;
@ReactModule(name = JitsiMeetMediaProjectionModule.NAME)
class JitsiMeetMediaProjectionModule
extends ReactContextBaseJavaModule {
public static final String NAME = "JitsiMeetMediaProjectionModule";
public JitsiMeetMediaProjectionModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@ReactMethod
public void launch() {
Context context = getReactApplicationContext();
Activity currentActivity = getCurrentActivity();
JitsiMeetMediaProjectionService.launch(context, currentActivity);
}
@ReactMethod
public void abort() {
Context context = getReactApplicationContext();
JitsiMeetMediaProjectionService.abort(context);
}
@NonNull
@Override
public String getName() {
return NAME;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Notification;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.IBinder;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.Random;
/**
* This class implements an Android {@link Service}, a foreground one specifically, and it's
* responsible for presenting an ongoing notification when a conference is in progress.
* The service will help keep the app running while in the background.
*
* See: https://developer.android.com/guide/components/services
*/
public class JitsiMeetMediaProjectionService extends Service {
private static final String TAG = JitsiMeetMediaProjectionService.class.getSimpleName();
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
public static void launch(Context context, Activity currentActivity) {
NotificationUtils.createNotificationChannel(currentActivity);
Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);
ComponentName componentName;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
componentName = context.startForegroundService(intent);
} else {
componentName = context.startService(intent);
}
} catch (RuntimeException e) {
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
JitsiMeetLogger.w(TAG + "Media projection service not started", e);
return;
}
if (componentName == null) {
JitsiMeetLogger.w(TAG + "Media projection service not started");
}
}
public static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);
context.stopService(intent);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
return START_NOT_STICKY;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
} else {
startForeground(NOTIFICATION_ID, notification);
}
return START_NOT_STICKY;
}
}

View File

@@ -16,6 +16,7 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
@@ -23,6 +24,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -32,6 +34,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
import java.util.Random;
/**
* This class implements an Android {@link Service}, a foreground one specifically, and it's
@@ -51,8 +54,12 @@ public class JitsiMeetOngoingConferenceService extends Service
private boolean isAudioMuted;
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
public static void launch(Context context, HashMap<String, Object> extraData) {
OngoingNotification.createOngoingConferenceNotificationChannel();
NotificationUtils.createNotificationChannel((Activity) context);
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
@@ -89,13 +96,16 @@ public class JitsiMeetOngoingConferenceService extends Service
public void onCreate() {
super.onCreate();
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
JitsiMeetLogger.i(TAG + " Service started");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
} else {
startForeground(NOTIFICATION_ID, notification);
}
}
OngoingConferenceTracker.getInstance().addListener(this);
@@ -126,13 +136,13 @@ public class JitsiMeetOngoingConferenceService extends Service
if (isAudioMuted != null) {
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(OngoingNotification.NOTIFICATION_ID, notification);
notificationManager.notify(NOTIFICATION_ID, notification);
}
}
@@ -212,13 +222,13 @@ public class JitsiMeetOngoingConferenceService extends Service
@Override
public void onReceive(Context context, Intent intent) {
isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, context);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't update service, notification is null");
} else {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(OngoingNotification.NOTIFICATION_ID, notification);
notificationManager.notify(NOTIFICATION_ID, notification);
JitsiMeetLogger.i(TAG + " audio muted changed");
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import static org.jitsi.meet.sdk.NotificationUtils.ONGOING_CONFERENCE_CHANNEL_ID;
import android.app.Notification;
import android.content.Context;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
/**
* Helper class for creating the media projection notification which is used with
* {@link JitsiMeetMediaProjectionService}.
*/
class MediaProjectionNotification {
private static final String TAG = MediaProjectionNotification.class.getSimpleName();
static Notification buildMediaProjectionNotification(Context context) {
if (context == null) {
JitsiMeetLogger.d(TAG, " Cannot create notification: no current context");
return null;
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
builder
.setCategory(NotificationCompat.CATEGORY_CALL)
.setContentTitle(context.getString(R.string.media_projection_notification_title))
.setContentText(context.getString(R.string.media_projection_notification_text))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(false)
.setUsesChronometer(false)
.setAutoCancel(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
return builder.build();
}
}

View File

@@ -1,10 +0,0 @@
package org.jitsi.meet.sdk;
import java.util.ArrayList;
import java.util.List;
public class NotificationChannels {
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
}

View File

@@ -0,0 +1,50 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.ArrayList;
import java.util.List;
class NotificationUtils {
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
private static final String TAG = NotificationUtils.class.getSimpleName();
static void createNotificationChannel(Activity context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
return;
}
NotificationManager notificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
}
}

View File

@@ -16,23 +16,18 @@
package org.jitsi.meet.sdk;
import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
import static org.jitsi.meet.sdk.NotificationUtils.ONGOING_CONFERENCE_CHANNEL_ID;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.Random;
/**
* Helper class for creating the ongoing notification which is used with
* {@link JitsiMeetOngoingConferenceService}. It allows the user to easily get back to the app
@@ -41,40 +36,10 @@ import java.util.Random;
class OngoingNotification {
private static final String TAG = OngoingNotification.class.getSimpleName();
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
private static long startingTime = 0;
static void createOngoingConferenceNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context) {
Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
return;
}
NotificationManager notificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_action_unmute), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
}
static Notification buildOngoingConferenceNotification(boolean isMuted) {
Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
return null;

View File

@@ -67,6 +67,7 @@ class ReactInstanceManagerHolder {
new DropboxModule(reactContext),
new ExternalAPIModule(reactContext),
new JavaScriptSandboxModule(reactContext),
new JitsiMeetMediaProjectionModule(reactContext),
new LocaleDetector(reactContext),
new LogBridgeModule(reactContext),
new SplashScreenModule(reactContext),

View File

@@ -1,6 +1,8 @@
<resources>
<string name="app_name">Jitsi Meet SDK</string>
<string name="dropbox_app_key"></string>
<string name="media_projection_notification_title">Media projection</string>
<string name="media_projection_notification_text">You are currently sharing your screen.</string>
<string name="ongoing_notification_title">Ongoing meeting</string>
<string name="ongoing_notification_text">You are currently in a meeting. Tap to return to it.</string>
<string name="ongoing_notification_action_hang_up">Hang up</string>

View File

@@ -38,6 +38,7 @@ import {
dataChannelClosed,
dataChannelOpened,
e2eRttChanged,
endpointMessageReceived,
kickedOut,
lockStateChanged,
nonParticipantMessageReceived,
@@ -162,7 +163,6 @@ import { isScreenAudioShared } from './react/features/screen-share/functions';
import { toggleScreenshotCaptureSummary } from './react/features/screenshot-capture/actions';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { endpointMessageReceived } from './react/features/subtitles/actions.any';
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
import { muteLocal } from './react/features/video-menu/actions.any';
import { iAmVisitor } from './react/features/visitors/functions';
@@ -679,6 +679,8 @@ export default {
startWithVideoMuted: getStartWithVideoMuted(state) || isUserInteractionRequiredForUnmute(state)
};
logger.debug(`Executed conference.init with roomName: ${roomName}`);
this.roomName = roomName;
try {
@@ -1824,28 +1826,24 @@ export default {
room.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args) => {
APP.store.dispatch(endpointMessageReceived(...args));
if (args && args.length >= 2) {
const [ sender, eventData ] = args;
if (eventData.name === ENDPOINT_TEXT_MESSAGE_NAME) {
APP.API.notifyEndpointTextMessageReceived({
senderInfo: {
jid: sender._jid,
id: sender._id
},
eventData
});
}
(participant, data) => {
APP.store.dispatch(endpointMessageReceived(participant, data));
if (data?.name === ENDPOINT_TEXT_MESSAGE_NAME) {
APP.API.notifyEndpointTextMessageReceived({
senderInfo: {
jid: participant.getJid(),
id: participant.getId()
},
eventData: data
});
}
});
room.on(
JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,
(...args) => {
APP.store.dispatch(nonParticipantMessageReceived(...args));
APP.API.notifyNonParticipantMessageReceived(...args);
(id, data) => {
APP.store.dispatch(nonParticipantMessageReceived(id, data));
APP.API.notifyNonParticipantMessageReceived(id, data);
});
room.on(

View File

@@ -85,7 +85,7 @@ var config = {
// disableE2EE: false,
// Enables supports for AV1 codec.
// enableAv1: false,
// enableAv1Support: false,
// Enables XMPP WebSocket (as opposed to BOSH) for the given amount of users.
// mobileXmppWsThreshold: 10, // enable XMPP WebSockets on mobile for 10% of the users
@@ -103,6 +103,9 @@ var config = {
// Experiment: Whether to skip interim transcriptions.
// skipInterimTranscriptions: false,
// Dump transcripts to a <transcript> element for debugging.
// dumpTranscript: false,
},
// Disables moderator indicators.
@@ -324,6 +327,16 @@ var config = {
// 'https://jitsi-meet.example.com/subfolder/static/oauth.html',
// },
// configuration for all things recording related. Existing settings will be migrated here in the future.
// recordings: {
// // If true, shows a notification at the start of the meeting with a call to action button
// // to start recording (for users who can do so).
// // suggestRecording: true,
// // If true, shows a warning label in the prejoin screen to point out the possibility that
// // the call you're joining might be recorded.
// // showPrejoinWarning: true,
// },
// recordingService: {
// // When integrations like dropbox are enabled only that will be shown,
// // by enabling fileRecordingsServiceEnabled, we show both the integrations
@@ -413,9 +426,6 @@ var config = {
// // ./src/react/features/transcribing/transcriber-langs.json.
// preferredLanguage: 'en-US',
// // Disable start transcription for all participants.
// disableStartForAll: false,
// // Enables automatic turning on transcribing when recording is started
// autoTranscribeOnRecord: false,
// },
@@ -713,7 +723,7 @@ var config = {
// Configs for prejoin page.
// prejoinConfig: {
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
// // This replaces `prejoinPageEnabled`.
// // This replaces `prejoinPageEnabled`. Defaults to true.
// enabled: true,
// // Hides the participant name editing field in the prejoin screen.
// // If requireDisplayName is also set as true, a name should still be provided through
@@ -1197,9 +1207,17 @@ var config = {
// https://firebase.google.com/docs/dynamic-links/create-manually
// deeplinking: {
//
// // The desktop deeplinking config.
// // The desktop deeplinking config, disabled by default.
// desktop: {
// appName: 'Jitsi Meet'
// appName: 'Jitsi Meet',
// appScheme: 'jitsi-meet,
// download: {
// linux:
// 'https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-x86_64.AppImage',
// macos: 'https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.dmg',
// windows: 'https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.exe'
// },
// enabled: false
// },
// // If true, any checks to handoff to another application will be prevented
// // and instead the app will continue to display in the current browser.
@@ -1421,7 +1439,6 @@ var config = {
// 'conference-timer',
// 'participants-count',
// 'e2ee',
// 'transcribing',
// 'video-quality',
// 'insecure-room',
// 'highlight-moment',

View File

@@ -104,7 +104,7 @@
}
}
.reactions-animations-container {
.reactions-animations-overflow-container {
position: absolute;
width: 20%;
bottom: 0;
@@ -117,6 +117,13 @@
position: relative;
}
.reactions-animations-container {
left: 50%;
bottom: 0px;
display: inline-block;
position: absolute;
}
$reactionCount: 20;
@function random($min, $max) {

View File

@@ -41,7 +41,6 @@
position: absolute;
top: 0;
height: 48px;
max-width: calc(100% - 24px);
}
@keyframes hideSubject {

View File

@@ -36,6 +36,7 @@ $flagsImagePath: "../images/";
@import 'modals/invite/info';
@import 'modals/screen-share/share-audio';
@import 'modals/screen-share/share-screen-warning';
@import 'modals/whiteboard';
@import 'videolayout_default';
@import 'subject';
@import 'popup_menu';

View File

@@ -0,0 +1,7 @@
.whiteboard {
.excalidraw-wrapper {
height: 100vh;
width: 100vw;
}
}

View File

@@ -51,7 +51,6 @@ VirtualHost "jitmeet.example.com"
}
av_moderation_component = "avmoderation.jitmeet.example.com"
speakerstats_component = "speakerstats.jitmeet.example.com"
conference_duration_component = "conferenceduration.jitmeet.example.com"
end_conference_component = "endconference.jitmeet.example.com"
-- we need bosh
modules_enabled = {
@@ -130,9 +129,6 @@ Component "focus.jitmeet.example.com" "client_proxy"
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
muc_component = "conference.jitmeet.example.com"
Component "conferenceduration.jitmeet.example.com" "conference_duration_component"
muc_component = "conference.jitmeet.example.com"
Component "endconference.jitmeet.example.com" "end_conference"
muc_component = "conference.jitmeet.example.com"

View File

@@ -38,9 +38,9 @@ apt install token-generator
mkdir -p /etc/jitsi/meet/jaas
VPASS_COOKIE=$(echo -n ${JAAS_KEY_ID}| cut -d/ -f1)
VPAAS_COOKIE=$(echo -n ${JAAS_KEY_ID}| cut -d/ -f1)
cp /usr/share/jitsi-meet-web-config/nginx-jaas.conf /etc/jitsi/meet/jaas
sed -i "s/jaas_magic_cookie/${VPASS_COOKIE}/g" /etc/jitsi/meet/jaas/nginx-jaas.conf
sed -i "s/jaas_magic_cookie/${VPAAS_COOKIE}/g" /etc/jitsi/meet/jaas/nginx-jaas.conf
cp /usr/share/jitsi-meet-web-config/8x8.vc-config.js /etc/jitsi/meet/jaas/
echo "set \$config_js_location /etc/jitsi/meet/jaas/8x8.vc-config.js;" >> /etc/jitsi/meet/jaas/jaas-vars

1
globals.d.ts vendored
View File

@@ -21,6 +21,7 @@ declare global {
JitsiMeetElectron?: any;
PressureObserver?: any;
PressureRecord?: any;
ReactNativeWebView?: any;
// selenium tests handler
_sharedVideoPlayer: any;
alwaysOnTop: { api: any };

3
globals.native.d.ts vendored
View File

@@ -19,6 +19,9 @@ interface IWindow {
location: ILocation;
PressureObserver?: any;
PressureRecord?: any;
ReactNativeWebView?: any;
TextDecoder?: any;
TextEncoder?: any;
self: any;
top: any;

View File

@@ -447,10 +447,10 @@ PODS:
- React-Core
- react-native-splash-screen (3.3.0):
- React-Core
- react-native-video (6.0.0-alpha.7):
- react-native-video (6.0.0-alpha.11):
- React-Core
- react-native-video/Video (= 6.0.0-alpha.7)
- react-native-video/Video (6.0.0-alpha.7):
- react-native-video/Video (= 6.0.0-alpha.11)
- react-native-video/Video (6.0.0-alpha.11):
- PromisesSwift
- React-Core
- react-native-webrtc (118.0.0):
@@ -880,7 +880,7 @@ SPEC CHECKSUMS:
react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2
react-native-slider: 1cdd6ba29675df21f30544253bf7351d3c2d68c4
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
react-native-video: 967eead48aaa42c25a9e1d65c3b1ab30762a88df
react-native-video: 472b7c366eaaaa0207e546d9a50410df89790bcf
react-native-webrtc: c8d9ad3c152105b2720ca2851d04b49659551992
react-native-webview: 8baa0f5c6d336d6ba488e942bcadea5bf51f050a
React-NativeModulesApple: 4225ac31a26696c02c54b471052b3e85e74a9a0c

View File

@@ -209,7 +209,7 @@
"appNotInstalled": "Sie benötigen die „{{app}}“-App, um der Konferenz auf dem Smartphone beizutreten.",
"description": "Nichts passiert? Wir haben versucht, die Konferenz in {{app}} zu öffnen. Versuchen Sie es erneut oder treten Sie der Konferenz in {{app}} im Web bei.",
"descriptionNew": "Nichts passiert? Wir haben versucht, die Konferenz in {{app}} zu öffnen. <br /><br /> Versuchen Sie es erneut oder treten Sie der Konferenz im Web bei.",
"descriptionWithoutWeb": "Ist nichts passiert? Wir haben versucht, Ihre Besprechung in der „{{app}}“-Desktop-App zu starten.",
"descriptionWithoutWeb": "Ist nichts passiert? Wir haben versucht, Ihre Konferenz in der „{{app}}“-Desktop-App zu starten.",
"downloadApp": "App herunterladen",
"downloadMobileApp": "Aus dem App Store herunterladen",
"ifDoNotHaveApp": "Wenn Sie die App noch nicht haben:",
@@ -219,11 +219,13 @@
"joinInBrowser": "Im Browser",
"launchMeetingLabel": "Wie möchten Sie an der Konferenz teilnehmen?",
"launchWebButton": "Im Web öffnen",
"noDesktopApp": "Sie haben die App noch nicht installiert?",
"noMobileApp": "Sie haben die App noch nicht installiert?",
"termsAndConditions": "Indem Sie fortfahren, stimmen Sie underen<a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>Nutzungsbedingungen</a> zu.",
"or": "oder",
"termsAndConditions": "Indem Sie fortfahren, stimmen Sie unseren <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>Nutzungsbedingungen</a> zu.",
"title": "Die Konferenz wird in {{app}} geöffnet …",
"titleNew": "Konferenz starten ...",
"tryAgainButton": "Erneut mit der nativen Applikation versuchen",
"tryAgainButton": "Erneut versuchen",
"unsupportedBrowser": "Sie verwenden einen Browser, der noch nicht unterstützt wird."
},
"defaultLink": "Bsp.: {{url}}",
@@ -534,7 +536,7 @@
"conferenceURL": "Link:",
"copyNumber": "Nummer kopieren",
"country": "Land",
"dialANumber": "Um am Meeting teilzunehmen, müssen Sie eine dieser Nummern wählen und dann die PIN eingeben.",
"dialANumber": "Um an der Konferenz teilzunehmen, müssen Sie eine dieser Nummern wählen und dann die PIN eingeben.",
"dialInConferenceID": "PIN:",
"dialInNotSupported": "Entschuldigung, leider wird das Einwählen derzeit nicht unterstützt.",
"dialInNumber": "Einwählen:",

View File

@@ -1,5 +1,8 @@
{
"addPeople": {
"accessibilityLabel": {
"meetingLink": "Lien de la réunion: {{url}}"
},
"add": "Inviter",
"addContacts": "Inviter vos contacts",
"contacts": "contacts",
@@ -39,6 +42,18 @@
"audioOnly": {
"audioOnly": "Bande passante faible"
},
"bandwidthSettings": {
"assumedBandwidthBps": "p. ex. 10000000 pour 10 Mbps ",
"assumedBandwidthBpsWarning": "Des valeurs élevées peuvent provoquer des problèmes réseaux.",
"customValue": "valeur personnalisée",
"customValueEffect": "Positionner la valeur actuelle en bps",
"leaveEmpty": "Laisser vide",
"leaveEmptyEffect": "Autoriser l'utilisation de l'estimation",
"possibleValues": "Valeurs possibles",
"setAssumedBandwidthBps": "Bande passante estimée",
"title": "Réglage de bande passante",
"zeroEffect": "Désactiver la vidéo"
},
"breakoutRooms": {
"actions": {
"add": "Ajouter salle annexe",
@@ -48,15 +63,22 @@
"leaveBreakoutRoom": "Quitter la salle annexe",
"more": "Plus",
"remove": "Supprimer",
"rename": "Renommer",
"renameBreakoutRoom": "Renommer la salle annexe",
"sendToBreakoutRoom": "Envoyer le participant dans:"
},
"breakoutList": "Liste des salles annexes",
"buttonLabel": "Salles annexes",
"defaultName": "Salle annexe #{{index}}",
"hideParticipantList": "Masquer la liste des participants",
"mainRoom": "Salle principale",
"notifications": {
"joined": "Entrée en salle annexe \"{{name}}\"",
"joinedMainRoom": "Retour à la salle principalem",
"joinedTitle": "Salles annexes"
}
},
"showParticipantList": "Afficher la liste des participants",
"title": "Salles annexes"
},
"calendarSync": {
"addMeetingURL": "Ajouter un lien de conférence",
@@ -118,7 +140,7 @@
"buttonText": "Installer l'extension Chrome",
"buttonTextEdge": "Installer lextension Edge",
"close": "Fermer",
"dontShowAgain": "Ne plus me montrer ceci",
"dontShowAgain": "Ne plus m'afficher ceci",
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365"
},
"connectingOverlay": {
@@ -149,13 +171,14 @@
"connectedTo": "Connecté à :",
"e2eeVerified": "E2EE vérifié",
"framerate": "Images par seconde :",
"less": "Cacher les détails",
"less": "Masquer les détails",
"localaddress": "Adresse locale :",
"localaddress_plural": "Adresses locales :",
"localport": "Port local :",
"localport_plural": "Ports locaux :",
"maxEnabledResolution": "débit max",
"more": "Montrer les détails",
"more": "Afficher les détails",
"no": "non",
"packetloss": "Perte de paquets :",
"participant_id": "ID du participant:",
"quality": {
@@ -174,7 +197,8 @@
"status": "Connexion :",
"transport": "Transport :",
"transport_plural": "Transports :",
"video_ssrc": "Video SSRC :"
"video_ssrc": "Video SSRC :",
"yes": "oui"
},
"dateUtils": {
"earlier": "Plus tôt",
@@ -237,9 +261,12 @@
"Share": "Partager",
"Submit": "Soumettre",
"WaitForHostMsg": "La conférence n'a pas encore commencé. Si vous en êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre son arrivée.",
"WaitingForHostButton": "Attendre l'hôte",
"WaitingForHostTitle": "En attente de l'hôte ...",
"Yes": "Oui",
"accessibilityLabel": {
"Cancel": "Annuler (quiter la popup)",
"Ok": "Ok (Sauvegarder et quiter la popup)",
"close": "Fermer la popup",
"liveStreaming": "Diffusion en direct",
"sharingTabs": "Options de partage"
@@ -248,6 +275,8 @@
"addMeetingNote": "Ajouter une note à cette conférence",
"addOptionalNote": "Ajouter une note (optionnel):",
"allow": "Autoriser",
"allowToggleCameraDialog": "Autorisez-vous {{initiatorName}} à changer votre mode de caméra ?",
"allowToggleCameraTitle": "Autoriser-vous le changement de mode de caméra ?",
"alreadySharedVideoMsg": "Un autre participant est en train de partager sa vidéo. Cette conférence ne permet de partager qu'une seule vidéo à la fois.",
"alreadySharedVideoTitle": "Une seule vidéo partagée est autorisée à la fois",
"applicationWindow": "Fenêtre d'application",
@@ -288,8 +317,8 @@
"gracefulShutdown": "Notre service est actuellement en maintenance. Veuillez réessayer plus tard.",
"grantModeratorDialog": "Êtes-vous sûr de vouloir rendre ce participant modérateur ?",
"grantModeratorTitle": "Nommer modérateur",
"hide": "Cacher",
"hideShareAudioHelper": "Ne pas montrer ce dialogue à nouveau",
"hide": "Masquer",
"hideShareAudioHelper": "Ne pas afficher ce dialogue à nouveau",
"incorrectPassword": "Nom d'utilisateur ou mot de passe incorrect",
"incorrectRoomLockPassword": "Mot de passe incorrect",
"internalError": "Oups ! Quelque chose s'est mal passée. L'erreur suivante s'est produite : {{error}}",
@@ -308,6 +337,7 @@
"lockRoom": "Ajouter un $t(lockRoomPassword) à la réunion ",
"lockTitle": "Échec du verrouillage",
"login": "Connexion",
"loginQuestion": "Voulez-vous vraiment vous connecter et quitter la conférence ?",
"logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence ?",
"logoutTitle": "Déconnexion",
"maxUsersLimitReached": "Le nombre maximal de participants est atteint. Le conférence est complète. Merci de contacter l'organisateur de la réunion ou réessayer plus tard !",
@@ -351,8 +381,6 @@
"permissionCameraRequiredError": "L'autorisation caméra est nécessaire pour participer aux réunions avec vidéo. Merci de l'accorder dans les paramètres",
"permissionErrorTitle": "Permission nécessaire",
"permissionMicRequiredError": "L'autorisation microphone est nécessaire pour participer aux réunions avec son. Merci de l'accorder dans les paramètres",
"popupError": "Votre navigateur bloque les fenêtres pop-up. Veuillez autoriser les fenêtres pop-up dans les paramètres de votre navigateur.",
"popupErrorTitle": "Pop-up bloquée",
"readMore": "plus",
"recentlyUsedObjects": "Vos objets récemment utilisés",
"recording": "Enregistrement",
@@ -369,6 +397,8 @@
"removePassword": "Supprimer le $t(lockRoomPassword)",
"removeSharedVideoMsg": "Voulez-vous vraiment supprimer votre vidéo partagée ?",
"removeSharedVideoTitle": "Supprimer la vidéo partagée",
"renameBreakoutRoomLabel": "Nom de la salle annexe",
"renameBreakoutRoomTitle": "Renommer la salle annexe",
"reservationError": "Erreur du système de réservation",
"reservationErrorMsg": "Code d'erreur: {{code}}, message: {{msg}}",
"retry": "Réessayer",
@@ -388,8 +418,10 @@
"sendPrivateMessageTitle": "Envoyer en privé ?",
"serviceUnavailable": "Service indisponible",
"sessTerminated": "Appel terminé",
"sessTerminatedReason": "L'appel a été terminé",
"sessionRestarted": "L'appel est relancé par la passerelle",
"shareAudio": "Continuer",
"shareAudioAltText": "Pour partager le contenu voulu, naviguer vers \"Onglet du Navigateur\", sélectionner le contenu, activer le bouton \"partager laudio\" et enfin cliquer sur le bouton \"partager\"",
"shareAudioTitle": "Comment partager le son",
"shareAudioWarningD1": "vous devez cesser le partage d'écran avant de partager votre son.",
"shareAudioWarningD2": "viys devez partager votre écran à nouveau et cocher l'ootion \"Partager l'audio\".",
@@ -419,12 +451,30 @@
"thankYou": "Merci d'avoir utilisé {{appName}} !",
"token": "jeton",
"tokenAuthFailed": "Désolé, vous n'êtes pas autorisé à rejoindre cette conversation.",
"tokenAuthFailedReason": {
"audInvalid": "Valeur `aud` invalide. Cela doit être `jitsi`.",
"contextNotFound": "L'objet `context` est absent du payload.",
"expInvalid": "Valeur `exp` invalide.",
"featureInvalid": "Fonctionnalité invalide: {{feature}}, probablement pas encore implémentée.",
"featureValueInvalid": "Valeur invalide pour la fonctionnalité: {{feature}}.",
"featuresNotFound": "L'objet `feature` est absent du payload.",
"headerNotFound": "Header non trouvé.",
"issInvalid": "Valeur `iss` invalide. Cela doit être `chat`.",
"kidMismatch": "Key ID (kid) ne correspond pas au sub.",
"kidNotFound": "Key ID (kid) manquant.",
"nbfFuture": "La valeur `nbf` est dans le futur.",
"nbfInvalid": "Valeur `nbf` invalide.",
"payloadNotFound": "Payload non trouvé",
"tokenExpired": "Jeton expiré"
},
"tokenAuthFailedTitle": "Échec de l'authentification",
"tokenAuthFailedWithReasons": "Désolé, vous nêtes pas autorisé à rejoindre lappel. La raison possible : {{reason}}.",
"tokenAuthUnsupported": "Token URL n'est pas supporté.",
"transcribing": "Transcription",
"unlockRoom": "Supprimer le $t(lockRoomPassword) de la réunion",
"user": "Utilisateur",
"userIdentifier": "Identifiant utilisateur",
"userPassword": "mot de passe utilisateur",
"userPassword": "Mot de passe utilisateur",
"verifyParticipantConfirm": "Ils correspondent",
"verifyParticipantDismiss": "Ils ne correspondent pas",
"verifyParticipantQuestion": "EXPÉRIMENTAL: Demander au participant {{participantName}} s'il voit le même contenu dans le même ordre. ",
@@ -433,6 +483,10 @@
"viewUpgradeOptions": "Voir les options de mise à jour",
"viewUpgradeOptionsContent": "Pour obtenir un accès illimité à des capacités premium comme l'enregistrement, les transcriptions, diffusion RTMP et plus, vous devez mettre à jour votre plan.",
"viewUpgradeOptionsTitle": "Vous avez découvert une capacité premium !",
"whiteboardLimitContent": "Désolé, la limite dutilisateur du tableau blanc a été atteinte.",
"whiteboardLimitReference": "Pour plus dinformations merci de visiter",
"whiteboardLimitReferenceUrl": "notre site web",
"whiteboardLimitTitle": "Utilisation du tableau blanc limitée",
"yourEntireScreen": "Votre écran entier"
},
"documentSharing": {
@@ -445,6 +499,9 @@
"title": "Intégrer cette réunion"
},
"feedback": {
"accessibilityLabel": {
"yourChoice": "Votre choix: {{rating}}"
},
"average": "Moyen",
"bad": "Mauvais",
"detailsLabel": "Dites nous en plus à ce sujet.",
@@ -505,6 +562,7 @@
"password": "$t(lockRoomPasswordUppercase) :",
"reachedLimit": "Vous avez atteint la limite de votre abonnement.",
"sip": "adresse SIP",
"sipAudioOnly": "Adresse SIP en audio uniquement",
"title": "Partager",
"tooltip": "Partager le lien et les informations de connexion pour cette conférence",
"upgradeOptions": "Veuillez vérifier les options de mise à niveau"
@@ -544,7 +602,7 @@
},
"largeVideo": {
"screenIsShared": "Vous êtes en train de partager votre écran",
"showMeWhatImSharing": "Me montrer ce que je partage"
"showMeWhatImSharing": "M'afficher ce que je partage"
},
"liveStreaming": {
"busy": "Nous tentons de libérer des ressources de diffusion. Veuillez réessayez dans quelques minutes.",
@@ -609,13 +667,13 @@
"knockingParticipantList": "Liste des participants en attente",
"lobbyChatStartedNotification": "Un modérateur dialogue en salle d'attente avec {{attendee}}",
"lobbyChatStartedTitle": "Un modérateur dialogue en salle d'attente avec vous.",
"lobbyClosed": "La salle d'attente a été fermée.",
"nameField": "Saisissez votre nom",
"notificationLobbyAccessDenied": "{{targetParticipantName}} a été refusé par {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} a été accepté par {{originParticipantName}}",
"notificationLobbyDisabled": "Le mode salle d'attente a été désactivé par {{originParticipantName}}",
"notificationLobbyEnabled": "Le mode salle d'attente a été activé par {{originParticipantName}}",
"notificationTitle": "Salle d'attente",
"passwordField": "Veuillez saisir le mot de passe de la réunion",
"passwordJoinButton": "Rejoindre",
"title": "Salle d'attente",
"toggleLabel": "Activer la salle d'attente"
@@ -648,6 +706,8 @@
"sessionToken": "Token de la session",
"start": "Démarrer l'enregistrement",
"stop": "Arrêter l'enregistrement",
"stopping": "Arrêt de l'enregistrement",
"wait": "Patienter pendant la sauvegarde de votre enregistrement.",
"yes": "Oui"
},
"lockRoomPassword": "mot de passe",
@@ -669,8 +729,11 @@
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
"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.",
"disabledIframe": "L'intégration Iframe est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disabledIframeSecondary": "L'intégration Iframe de {{domaine}} est uniquement destinée à des démos, cet appel se terminera dans {{timeout}} minutes.",
"disconnected": "déconnecté",
"displayNotifications": "Afficher les notifications pour",
"dontRemindMe": "Ne pas me le rappeler",
"focus": "Focus de conférence",
"focusFail": "{{component}} n'est pas disponible - réessayez dans {{ms}} sec",
"gifsMenu": "GIPHY",
@@ -679,6 +742,7 @@
"invitedOneMember": "{{name}} a été invité(e)",
"invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités",
"invitedTwoMembers": "{{first}} et {{second}} ont été invités",
"joinMeeting": "Rejoindre",
"kickParticipant": "{{kicked}} a été expulsé par {{kicker}}",
"leftOneMember": "{{name}} a quitté la réunion",
"leftThreePlusMembers": "{{name}} et beaucoup d'autres ont quitté la réunion",
@@ -713,7 +777,6 @@
"newDeviceCameraTitle": "Nouvelle caméra détectée",
"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",
"noiseSuppressionNoTrackDescription": "Veuillez activer votre microphone d'abord.",
"noiseSuppressionStereoDescription": "La suppression de bruit dune source stéréo nest pas encore supportée.",
"oldElectronClientDescription1": "Vous semblez utiliser une ancienne version du client Jitsi Meet qui présente des failles de sécurité connues. Veuillez vous assurer de mettre à jour vers notre ",
"oldElectronClientDescription2": "dernière build",
@@ -743,7 +806,9 @@
"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",
"waitingParticipants": "{{waitingParticipants}} personnes"
"waitingParticipants": "{{waitingParticipants}} personnes",
"whiteboardLimitDescription": "Veuillez sauvegarder votre progression, car la limite dutilisation du tableau blanc sera bientôt atteinte et celui-ci sera fermé.",
"whiteboardLimitTitle": "Utiilisation du tableau blanc"
},
"participantsPane": {
"actions": {
@@ -754,6 +819,7 @@
"askUnmute": "Demander de réactiver le micro",
"audioModeration": "Rouvrir leur micro",
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
"breakoutRooms": "Salles annexes",
"invite": "Inviter quelqu'un",
"moreModerationActions": "Options de modération supplémentaires",
"moreModerationControls": "Options de modération supplémentaires",
@@ -771,6 +837,7 @@
"headings": {
"lobby": "Salle d'attente ({{count}})",
"participantsList": "Participants de la réunion ({{count}})",
"visitorRequests": "(Demande {{count}} )",
"visitors": "Visiteurs {{count}}",
"waitingLobby": "Dans la salle d'attente ({{count}})"
},
@@ -805,8 +872,8 @@
"results": {
"changeVote": "Changer le vote",
"empty": "Il n'y a pas encore de sondages dans cette réunion. Démarrez un sondage ici !",
"hideDetailedResults": "Cacher les détails",
"showDetailedResults": "Montrer les détails",
"hideDetailedResults": "Masquer les détails",
"showDetailedResults": "Afficher les détails",
"vote": "Voter"
}
},
@@ -864,9 +931,11 @@
"lookGood": "Il semble que votre microphone fonctionne correctement",
"or": "ou",
"premeeting": "Pré-séance",
"proceedAnyway": "Continuer quand même",
"screenSharingError": "Erreur de partage d'écran:",
"showScreen": "Activer l'écran de pré-séance",
"startWithPhone": "Commencez avec l'audio du téléphone",
"unsafeRoomConsent": "Je comprends les risques et je veux quand même rejoindre cette réunion",
"videoOnlyError": "Erreur vidéo:",
"videoTrackError": "Impossible de créer une piste vidéo.",
"viewAllNumbers": "voir tous les numéros"
@@ -929,7 +998,6 @@
"limitNotificationDescriptionNative": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <3> {{app}} </3>.",
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
"linkGenerated": "Nous avons généré un lien à votre enregistrement.",
"live": "DIRECT",
"localRecordingNoNotificationWarning": "Le démarrage de lenregistrement ne sera pas annoncé aux autres participants. Vous devrez les informer par vous-même que la réunion sera enregistrée.",
"localRecordingNoVideo": "La vidéo n'est pas en cours denregistrement",
"localRecordingStartWarning": "Assurez-vous darrêter lenregistrement vidéo avant de quitter la réunion afin de pouvoir le sauvegarder.",
@@ -946,13 +1014,15 @@
"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 ...",
"rec": "REC",
"recordAudioAndVideo": "Enregistrer l'audio et la vidéo",
"recordTranscription": "Enregistrer la transcription",
"saveLocalRecording": "Sauvegarder lenregistrement local (Beta)",
"serviceDescription": "Votre enregistrement sera enregistré par le service dédié",
"serviceDescriptionCloud": "Enregistrement Cloud",
"serviceDescriptionCloudInfo": "Les conférences enregistrées sont automatiquement supprimées 24h après leur heure d'enregistrement.",
"serviceName": "Service d'enregistrement",
"sessionAlreadyActive": "Cette session est déjà en cours d'enregistrement ou de diffusion.",
"showAdvancedOptions": "Afficher les options avancées",
"signIn": "Se connecter",
"signOut": "Se déconnecter",
"surfaceError": "Veuillez sélectionner longlet courant.",
@@ -968,8 +1038,14 @@
"security": {
"about": "Vous pouvez ajouter un mot de passe à votre réunion. Les participants devront fournir le mot de passe avant de pouvoir rejoindre la réunion.",
"aboutReadOnly": "Les modérateurs peuvent ajouter un mot de passe à la réunion. Les participants devront fournir le mot de passe avant de pouvoir rejoindre la réunion.",
"insecureRoomNameWarning": "Le nom de la salle est peu sûr. Des participants non désirés peuvent rejoindre votre réunion. Pensez à sécuriser votre réunion en cliquant sur le bouton de sécurité.",
"title": "Options de sécurité"
"insecureRoomNameWarningNative": "Le nom de la réunion nest pas sûr. Des participants non voulus pourraient rejoindre cette réunion. {{recommendAction}} En apprendre plus sur la sécurisation des réunions.",
"insecureRoomNameWarningWeb": "Le nom de la réunion nest pas sûr. Des participants non voulus pourraient rejoindre cette réunion. {{recommendAction}} En apprendre plus sur la sécurisation des réunions <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">here</a>.",
"title": "Options de sécurité",
"unsafeRoomActions": {
"meeting": "Envisagez de sécuriser votre réunion en utilisant le bouton options de sécurité.",
"prejoin": "Envisagez d'utiliser un nom plus unique",
"welcome": "Envisagez d'utiliser un nom plus unique ou choisissez en un parmi ceux suggérés"
}
},
"settings": {
"audio": "Audio",
@@ -1002,7 +1078,7 @@
"participantKnocking": "un participant en salle dattente",
"participantLeft": "un participant quitte",
"playSounds": "Jouer un son quand",
"reactions": "il y a une réaction à la réunion",
"reactions": "Il y a une réaction à la réunion",
"sameAsSystem": "Identique au système ({{label}})",
"selectAudioOutput": "Sortie audio",
"selectCamera": "Caméra",
@@ -1023,6 +1099,7 @@
"alertOk": "D'accord",
"alertTitle": "Avertissement",
"alertURLText": "L'URL du serveur est invalide",
"apply": "Appliquer",
"buildInfoSection": "Informations de build",
"conferenceSection": "Conférence",
"disableCallIntegration": "Désactiver l'intégration d'appels native",
@@ -1033,12 +1110,14 @@
"displayNamePlaceholderText": "Ex : Jean Dupond",
"email": "Email",
"emailPlaceholderText": "email@example..com",
"gavatarMessage": "Si votre email est associé à un compte Gravatar, nous allons lutiliser pour afficher votre image de profil.",
"goTo": "Aller à",
"header": "Paramètres",
"help": "Aide",
"links": "Liens",
"privacy": "Confidentialité",
"profileSection": "Profil",
"sdkVersion": "Version du SDK",
"serverURL": "URL du serveur",
"showAdvanced": "Afficher les paramètres avancés",
"startCarModeInLowBandwidthMode": "Commencer le mode voiture en mode bande passante réduite",
@@ -1091,17 +1170,26 @@
"audioOnly": "Activer / Désactiver le mode voix uniquement",
"audioRoute": "Sélectionner la source audio",
"boo": "Hou",
"breakoutRoom": "Rejoindre/quitter une salle annexe",
"breakoutRooms": "Rejoindre / quitter une salle annexe",
"callQuality": "Ajuster la qualité vidéo",
"carmode": "Mode voiture",
"cc": "Activer / Désactiver les sous-titres",
"chat": "Afficher / Masquer la discussion instantanée",
"clap": "Applaudir",
"closeChat": "Fermer la discussion instantanée",
"closeMoreActions": "Fermer le menu plus d'actions",
"closeParticipantsPane": "Fermer le panneau des participants",
"collapse": "Plier",
"document": "Activer / Désactiver le document partagé",
"documentClose": "Fermer le document partagé",
"documentOpen": "Ouvrir le document partagé",
"download": "Télécharger nos applications",
"embedMeeting": "Intégrer la réunion",
"endConference": "Terminer la réunion pour tout le monde",
"enterFullScreen": "Passer en mode plein écran",
"enterTileView": "Passer en vue mosaïque",
"exitFullScreen": "Quitter le mode plein écran",
"exitTileView": "Quitter la vue mosaïque",
"expand": "Développer",
"feedback": "Laisser des commentaires",
"fullScreen": "Activer / Désactiver le plein écran",
@@ -1110,6 +1198,7 @@
"hangup": "Quitter la conversation",
"heading": "Barre d'outils",
"help": "Aide",
"hideWhiteboard": "Masquer le tableau blanc",
"invite": "Inviter des participants",
"kick": "Expulser le participant",
"laugh": "Rire",
@@ -1119,6 +1208,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",
"lowerHand": "Baisser la main",
"moreActions": "Activer / Désactiver le menu d'actions supplémentaires",
"moreActionsMenu": "Menu d'actions supplémentaires",
"moreOptions": "Voir plus d'options",
@@ -1127,12 +1217,15 @@
"muteEveryoneElse": "Couper le micro de tous les autres",
"muteEveryoneElsesVideoStream": "Couper la caméra de tous les autres",
"muteEveryonesVideoStream": "Couper la caméra de tout le monde",
"muteGUMPending": "Connection de votre microphone",
"noiseSuppression": "Suppression du bruit",
"openChat": "Ouvrir le chat",
"participants": "Participants",
"pip": "Activer / Désactiver le mode Picture in Picture",
"privateMessage": "Envoyer un message privé",
"profile": "Éditer votre profil",
"raiseHand": "Lever la main",
"reactions": "Réactions",
"reactionsMenu": "Ouvrir / fermer le menu réactions",
"recording": "Activer / Désactiver l'enregistrement",
"remoteMute": "Couper le micro du participant",
@@ -1146,15 +1239,20 @@
"sharedvideo": "Démarrer / Arrêter le partage de vidéo",
"shortcuts": "Afficher / Masquer les raccourcis",
"show": "Afficher en premier plan",
"showWhiteboard": "Afficher le tableux blanc",
"silence": "Silence",
"speakerStats": "Afficher / Cacher les statistiques de parole",
"speakerStats": "Afficher / Masquer les statistiques de parole",
"stopScreenSharing": "Arrêter le partage d'écran",
"stopSharedVideo": "Arrêter la vidéo partagée",
"surprised": "Surpris",
"tileView": "Activer / Désactiver la vue mosaïque",
"toggleCamera": "Changer de caméra",
"toggleFilmstrip": "Afficher ou masquer les vignettes vidéo",
"unmute": "Rétablir le son",
"videoblur": "Activer / désactiver le floutage",
"videomute": "Activer / Couper la vidéo",
"whiteboard": "Afficher / Cacher le tableau blanc"
"videomuteGUMPending": "Connexion de votre caméra",
"videounmute": "Démarrer la vidéo"
},
"addPeople": "Ajouter des personnes à votre appel",
"audioOnlyOff": "Désactiver le mode bande passante réduite",
@@ -1167,6 +1265,7 @@
"chat": "Ouvrir / Fermer le chat",
"clap": "Applaudir",
"closeChat": "Fermer le chat",
"closeParticipantsPane": "Fermer le panneau des participants",
"closeReactionsMenu": "Fermer le menu réactions",
"disableNoiseSuppression": "Arrêter la suppression du bruit",
"disableReactionSounds": "Vous pouvez interdire les réactions sonores à cette réunion",
@@ -1185,7 +1284,7 @@
"giphy": "Activer/désactiver le menu GIPHY",
"hangup": "Quitter",
"help": "Aide",
"hideWhiteboard": "Cacher le tableau blanc",
"hideWhiteboard": "Masquer le tableau blanc",
"invite": "Inviter des participants",
"joinBreakoutRoom": "Rejoindre salle annexe",
"laugh": "Rire",
@@ -1203,6 +1302,7 @@
"mute": "Muet / Actif",
"muteEveryone": "Couper le micro à tout le monde",
"muteEveryonesVideo": "Couper la caméra de tout le monde",
"muteGUMPending": "Connection de votre microphone",
"noAudioSignalDesc": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, envisagez de changer de périphérique utilisé.",
"noAudioSignalDescSuggestion": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, pensez à utiliser le périphérique suivant :",
"noAudioSignalDialInDesc": "Vous pouvez également appeler en utilisant :",
@@ -1225,6 +1325,7 @@
"reactionLike": "Envoyer réaction approuver",
"reactionSilence": "Envoyer réaction silence",
"reactionSurprised": "Envoyer réaction surprise",
"reactions": "Reactions",
"security": "Options de sécurité",
"selectBackground": "Sélectionner un arrière-plan",
"shareRoom": "Inviter quelqu'un",
@@ -1244,8 +1345,11 @@
"talkWhileMutedPopup": "Vous voulez parler ? Votre micro est coupé.",
"tileViewToggle": "Activer / Désactiver la vue mosaïque",
"toggleCamera": "Changer de caméra",
"unmute": "Rétablir le son",
"videoSettings": "Paramètres vidéo",
"videomute": "Démarrer / Arrêter la caméra"
"videomute": "Arrêter la caméra",
"videomuteGUMPending": "Connexion de votre caméra",
"videounmute": "Démarrer la caméra"
},
"transcribing": {
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
@@ -1253,7 +1357,8 @@
"expandedLabel": "La transcription est actuellement activée",
"failedToStart": "Échec de démarrage de la transcription",
"labelToolTip": "La transcription de la réunion est en cours",
"off": "La transcription désactivée",
"off": "La transcription est désactivée",
"on": "La transcription est activée",
"pending": "Préparation de la transcription de la réunion ...",
"sourceLanguageDesc": "Actuellement, la langue de la réunion est sélectionnée à <b>{{sourceLanguage}}</b>. <br/> Vous pouvez la changer à partir de ",
"sourceLanguageHere": "ici",
@@ -1316,7 +1421,7 @@
"domuteVideoOfOthers": "Couper la caméra des autres",
"flip": "Miroir",
"grantModerator": "Donner des droits de modérateur",
"hideSelfView": "Cacher l'affichage de votre propre vidéo",
"hideSelfView": "Masquer l'affichage de votre propre vidéo",
"kick": "Exclure",
"mirrorVideo": "Inverser ma vidéo",
"moderator": "Modérateur",
@@ -1326,13 +1431,17 @@
"remoteControl": "Démarrer / Arrêter le contrôle à distance",
"screenSharing": "Cette personne partage son écran",
"show": "Afficher en premier plan",
"showSelfView": "Montrer votre propre vidéo",
"showSelfView": "Afficher votre propre vidéo",
"unpinFromStage": "Ne plus garder affiché",
"verify": "Vérifier le participant",
"videoMuted": "Caméra coupée",
"videomute": "Le participant a arrêté la caméra"
},
"virtualBackground": {
"accessibilityLabel": {
"currentBackground": "Arrière-plan actuel: {{background}}",
"selectBackground": "Sélectionner un arrière-plan"
},
"addBackground": "Ajouter un arrière-plan",
"apply": "Appliquer",
"backgroundEffectError": "Erreur dans l'application de l'effet d'arrière-plan.",
@@ -1356,7 +1465,14 @@
"webAssemblyWarning": "WebAssembly non supporté",
"webAssemblyWarningDescription": "WebAssembly invalidé ou non supporté par ce navigateur"
},
"visitorsLabel": "Nombre de visiteurs: {{count}}",
"visitors": {
"chatIndicator": "(visiteur)",
"labelTooltip": "Nombre de Visiteurs",
"notification": {
"description": "Pour participer lever la main.",
"title": "Vous êtes visiteur dans cette réunion"
}
},
"volumeSlider": "Curseur de volume",
"welcomepage": {
"accessibilityLabel": {

File diff suppressed because it is too large Load Diff

View File

@@ -219,7 +219,9 @@
"joinInBrowser": "Pievienojieties pārlūkā",
"launchMeetingLabel": "Kā vēlaties pievienoties šai sapulcei?",
"launchWebButton": "Palaist tīmekļa pārlūkā",
"noDesktopApp": "Vai jums nav lietotnes?",
"noMobileApp": "Vai jums nav lietotnes?",
"or": "vai",
"termsAndConditions": "Turpinot jūs piekrītat mūsu <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>pakalpojumu sniegšanas noteikumiem.</a>",
"title": "Notiek jūsu sapulces palaišana lietotnē {{app}}...",
"titleNew": "Notiek jūsu sapulces palaišana ...",
@@ -837,6 +839,7 @@
"headings": {
"lobby": "Vestibils ({{count}})",
"participantsList": "Sapulces dalībnieki ({{count}})",
"visitorRequests": " (pieprasījumi {{count}})",
"visitors": "Apmeklētāji ({{count}})",
"waitingLobby": "Gaida vestibilā ({{count}})"
},
@@ -997,7 +1000,6 @@
"limitNotificationDescriptionNative": "Lielā pieprasījuma dēļ jūsu ieraksts tiks ierobežots līdz {{limit}} min. Lai iegūtu neierobežotu ierakstu skaitu, izmēģiniet <3>{{app}}</3>.",
"limitNotificationDescriptionWeb": "Lielā pieprasījuma dēļ jūsu ieraksts tiks ierobežots līdz {{limit}} min. Lai iegūtu neierobežotu ierakstu skaitu, izmēģiniet <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"linkGenerated": "Mēs esam izveidojuši saiti uz jūsu ierakstu.",
"live": "ĒTERĀ",
"localRecordingNoNotificationWarning": "Ieraksts netiks izziņots citiem dalībniekiem. Jums būs jāpaziņo viņiem, ka sapulce tiek ierakstīta.",
"localRecordingNoVideo": "Video netiek ierakstīts",
"localRecordingStartWarning": "Pirms iziešanas no sapulces, lūdzu, apturiet ierakstīšanu, lai to saglabātu.",
@@ -1014,14 +1016,16 @@
"onBy": "{{name}} ieslēdza ierakstu",
"onlyRecordSelf": "Ierakstīt tikai manas audio un video straumes",
"pending": "Gatavojas ierakstīt sapulci...",
"rec": "Notiek ieraksts",
"recordAudioAndVideo": "Ierakstīt audio un video",
"recordTranscription": "Ierakstīt transkripciju",
"saveLocalRecording": "Ieraksta faila saglabāšana lokāli (beta)",
"serviceDescription": "Jūsu ierakstu saglabās attiecīgais pakalpojums",
"serviceDescriptionCloud": "Mākoņa ierakstīšana",
"serviceDescriptionCloud": "Ierakstīšana mākonī",
"serviceDescriptionCloudInfo": "Ierakstītās sapulces tiek automātiski dzēstas 24 stundas pēc to ierakstīšanas laika.",
"serviceName": "Ieraksta pakalpojums",
"sessionAlreadyActive": "Šī sesija jau tiek ierakstīta vai straumēta tiešraidē.",
"signIn": "Ierakstīties",
"showAdvancedOptions": "Papildus iespējas",
"signIn": "Pierakstīties",
"signOut": "Izrakstīties",
"surfaceError": "Lūdzu, izvēlieties pašreizējo cilni.",
"title": "Ieraksts",
@@ -1351,12 +1355,9 @@
},
"transcribing": {
"ccButtonTooltip": "Iesl./izsl. subtitrus",
"error": "Transkripcijas kļūme. Lūdzu, mēģiniet vēlāk.",
"expandedLabel": "Transkripcija ieslēgta",
"expandedLabel": "Transkripcija ir ieslēgta",
"failedToStart": "Neizdevās sākt transkripciju",
"labelToolTip": "Notiek sapulces transkripcija.",
"off": "Transkripcija izslēgta",
"pending": "Gatavojas veikt sapulces transkripciju...",
"labelToolTip": "Notiek sapulces transkripcija",
"sourceLanguageDesc": "Pašlaik sapulces valoda ir iestatīta uz <b>{{sourceLanguage}}</b>. <br/> Varat to mainīt no ",
"sourceLanguageHere": "šeit",
"start": "Iesl. subtitru rādīšanu",

View File

@@ -1,5 +1,8 @@
{
"addPeople": {
"accessibilityLabel": {
"meetingLink": "Lidhje takimi: {{url}}"
},
"add": "Ftoni",
"addContacts": "Ftoni kontaktet tuaja",
"contacts": "kontakte",
@@ -39,6 +42,18 @@
"audioOnly": {
"audioOnly": "Gjerësi e ulët bande"
},
"bandwidthSettings": {
"assumedBandwidthBps": "p.sh. 10000000 për 10 Mbps",
"assumedBandwidthBpsWarning": "Vlera më të mëdha mund të shkaktojnë probleme rrjeti.",
"customValue": "vlerë vetjake",
"customValueEffect": "për të ujdisur vlerën e tanishme bps",
"leaveEmpty": "lëreni të zbrazët",
"leaveEmptyEffect": "për të lejuar kryerje vlerësimesh",
"possibleValues": "Vlera të mundshme",
"setAssumedBandwidthBps": "Gjerësi bande e supozuar (bps)",
"title": "Rregullime gjerësi bande",
"zeroEffect": "për të çaktivizuar videon"
},
"breakoutRooms": {
"actions": {
"add": "Shtoni aneks konsultimesh",
@@ -48,15 +63,22 @@
"leaveBreakoutRoom": "Dilni nga aneks konsultimesh",
"more": "Më tepër",
"remove": "Hiqe",
"rename": "Riemërtojeni",
"renameBreakoutRoom": "Riemërtoni aneks konsultimesh",
"sendToBreakoutRoom": "Dërgoje pjesëmarrësin te:"
},
"breakoutList": "Listë aneksesh",
"buttonLabel": "Dhoma aneks konsultimesh",
"defaultName": "Dhomë aneks konsultimesh #{{index}}",
"hideParticipantList": "Fshihe listën e pjesëmarrësve",
"mainRoom": "Dhoma kryesore",
"notifications": {
"joined": "Po hyhet te dhomë aneks konsultimesh \"{{name}}\"",
"joinedMainRoom": "Po hyhet te dhoma kryesore",
"joinedTitle": "Dhoma Aneks Konsultimesh"
}
},
"showParticipantList": "Shfaq listë pjesëmarrësish",
"title": "Dhoma Aneks Konsultimesh"
},
"calendarSync": {
"addMeetingURL": "Shtoni një lidhje takimi",
@@ -68,9 +90,9 @@
},
"join": "Merrni pjesë",
"joinTooltip": "Merrni pjesë në takim",
"nextMeeting": "takimi pasues",
"nextMeeting": "Takimi pasues",
"noEvents": "Ska veprimtari të ardhshme të vëna në plan.",
"ongoingMeeting": "takim në zhvillim e sipër",
"ongoingMeeting": "Takim në zhvillim e sipër",
"permissionButton": "Hapni rregullimet",
"permissionMessage": "Që të shihni në aplikacion takimet tuaja, janë të domosdoshme lejet mbi Kalendarin.",
"refresh": "Rifresko kalendarin",
@@ -156,6 +178,7 @@
"localport_plural": "Porta vendore:",
"maxEnabledResolution": "maksimum dërgimi",
"more": "Shfaq më tepër",
"no": "jo",
"packetloss": "Humbje paketesh:",
"participant_id": "ID pjesëmarrësi:",
"quality": {
@@ -174,7 +197,8 @@
"status": "Lidhje:",
"transport": "Transport:",
"transport_plural": "Transporte:",
"video_ssrc": "SSRC Videoje:"
"video_ssrc": "Video SSRC:",
"yes": "po"
},
"dateUtils": {
"earlier": "Më herët",
@@ -195,7 +219,9 @@
"joinInBrowser": "Hyni që nga shfletues",
"launchMeetingLabel": "Si doni të hyhet në këtë takim?",
"launchWebButton": "Nise në web",
"noDesktopApp": "Se keni aplikacionin?",
"noMobileApp": "Se keni aplikacionin?",
"or": "OR",
"termsAndConditions": "Duke vazhduar, pajtoheni me <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>terms & conditions.</a> tona",
"title": "Po niset takimi juaj në {{app}}…",
"titleNew": "Po niset takimi juaj…",
@@ -220,7 +246,7 @@
"noPermission": "Su akordua leje",
"previewUnavailable": "Sbëhet dot paraparje",
"selectADevice": "Përzgjidhni një pajisje",
"testAudio": "Luaj një tingull, për provë"
"testAudio": "Testojeni"
},
"dialIn": {
"screenTitle": "Përmbledhje rënieje numrash"
@@ -231,21 +257,28 @@
"dialog": {
"Back": "Mbrapsht",
"Cancel": "Anuloje",
"IamHost": "Jam organizatori",
"IamHost": "Hyni",
"Ok": "OK",
"Remove": "Hiqe",
"Share": "Ndaje",
"Submit": "Parashtroje",
"WaitForHostMsg": "Konferenca ska nisur ende. Nëse jeni organizatori, ju lutemi, bëni mirëfilltësimin. Përndryshe, ju lutemi, pritni që të mbërrijë organizatori.",
"WaitingForHostTitle": "Po pritet për organizatorin…",
"WaitForHostMsg": "Konferenca ska nisur, ngaqë ska mbërritur ende ndonjë moderator. Nëse dëshironi të bëheni një moderator, ju lutemi, bëni hyrjen. Përndryshe, ju lutemi, pritni.",
"WaitingForHostButton": "Prit për moderator",
"WaitingForHostTitle": "Po pritet për një moderator…",
"Yes": "Po",
"accessibilityLabel": {
"liveStreaming": "Transmetim i Drejtpërdrejtë"
"Cancel": "Anuloje (dilni nga dialogu)",
"Ok": "OK (ruajeni dhe dilni nga dialogu)",
"close": "Mbylle dialogun",
"liveStreaming": "Transmetim i Drejtpërdrejtë",
"sharingTabs": "Mundësi ndarjeje me të tjerë"
},
"add": "Shtoni",
"addMeetingNote": "Shtoni një shënim rreth këtij takimi",
"addOptionalNote": "Shtoni një shënim (në daçi):",
"allow": "Lejoje",
"allowToggleCameraDialog": "A e lejoni {{initiatorName}} të ndryshojë anën nga sheh kamera juaj?",
"allowToggleCameraTitle": "Të lejohet ndryshimi i kamerës?",
"alreadySharedVideoMsg": "Një tjetër pjesëmarrës po ndan me të tjerët një video. Kjo konferencë lejon vetëm një ndarje videoje në herë.",
"alreadySharedVideoTitle": "Lejohet vetëm një ndarje videoje me të tjerët në herë",
"applicationWindow": "Dritare aplikacioni",
@@ -306,7 +339,8 @@
"lockRoom": "Shtoni takim $t(lockRoomPasswordUppercase)",
"lockTitle": "Kyçja dështoi",
"login": "Hyrje",
"logoutQuestion": "Jeni i sigurt se doni të dilet dhe të ndalet konferenca?",
"loginQuestion": "Jeni i sigurt se doni të hyhet dhe të braktiset konferenca?",
"logoutQuestion": "Jeni i sigurt se doni të dilet dhe të braktiset konferenca?",
"logoutTitle": "Dalje",
"maxUsersLimitReached": "U mbërrit në kufi numri maksimum pjesëmarrësish. Konferenca është plot. Ju lutemi, lidhuni me të zotin e takimit, ose provoni më vonë!",
"maxUsersLimitReachedTitle": "U mbërrit në kufi numri maksimum pjesëmarrësish",
@@ -334,7 +368,7 @@
"muteEveryonesVideoTitle": "Të ndalet videoja e gjithkujt?",
"muteParticipantBody": "Sdo jeni në gjendje të hiqni heshtimin për ta, por ata munden kurdo ta heqin për veten.",
"muteParticipantButton": "Heshtoje",
"muteParticipantsVideoBody": "Sdo të jeni në gjendje të riaktivizoni kamerën e tyre, por ata munden kurdo ta riaktvizojnë për veten.",
"muteParticipantsVideoBody": "Sdo të jeni në gjendje të riaktivizoni kamerën e tyre, por ata munden kurdo ta riaktivizojnë për veten.",
"muteParticipantsVideoBodyModerationOn": "Sdo të jeni në gjendje të riaktivizoni kamerën e tyre dhe as ata sdo të munden.",
"muteParticipantsVideoButton": "Ndale videon",
"muteParticipantsVideoDialog": "Jeni i sigurt se doni të çaktivizoni kamerën e këtij pjesëmarrësi? Sdo të jeni në gjendje të riaktivizoni kamerën, por ai mund ta riaktivizojë kurdo.",
@@ -349,8 +383,6 @@
"permissionCameraRequiredError": "Leja mbi kamerën është e domosdoshme për të marrë pjesë në konferenca me video. Ju lutemi, akordojeni që nga Rregullimet",
"permissionErrorTitle": "Leje e domosdoshme",
"permissionMicRequiredError": "Leja mbi mikrofonin është e domosdoshme për të marrë pjesë në konferenca me audio. Ju lutemi, akordojeni që nga Rregullimet",
"popupError": "Shfletuesi juaj i bllokon dritaret flluskë prej këtij sajti. Ju lutemi, aktivizoni flluskat te rregullimet e sigurisë të shfletuesit tuaj dhe riprovoni.",
"popupErrorTitle": "Flluska u bllokua",
"readMore": "më tepër",
"recentlyUsedObjects": "Së fundi përdorët objekte",
"recording": "Regjistrim",
@@ -367,6 +399,8 @@
"removePassword": "Hiqe $t(lockRoomPassword)",
"removeSharedVideoMsg": "Jeni i sigurt se doni të hiqni videon që keni ndarë me të tjerë?",
"removeSharedVideoTitle": "Hiqe videon e ndarë me të tjerë",
"renameBreakoutRoomLabel": "Emër dhome",
"renameBreakoutRoomTitle": "Riemërtoni aneks konsultimesh",
"reservationError": "Gabim sistemi rezervimi",
"reservationErrorMsg": "Kod gabimi: {{code}}, mesazh: {{msg}}",
"retry": "Riprovoni",
@@ -386,8 +420,10 @@
"sendPrivateMessageTitle": "Të dërgohet privatisht?",
"serviceUnavailable": "Shërbim jashtë funksionimi",
"sessTerminated": "Thirrja përfundoi",
"sessTerminatedReason": "Takimi u përfundua",
"sessionRestarted": "Thirrja rinisi për shkak të një problemi lidhjeje.",
"shareAudio": "Vazhdo",
"shareAudio": "Vazhdoni",
"shareAudioAltText": "që të ndani me të tjerë lëndën e dëshiruar, kaloni te \"Skedë Shfletuesi\", përzgjidhni lëndën, aktivizoni shenjën për \"ndani audio me të tjerë\" dhe mandej klikoni butonin \"ndaje me të tjerë\"",
"shareAudioTitle": "Si të ndahet audio me të tjerë",
"shareAudioWarningD1": "lypset të ndalni tregim ekrani, para se të ndani audion tuaj me të tjerë.",
"shareAudioWarningD2": "lypset të rinisni tregimin e ekranit tuaj dhe ti vini shenjë mundësisë “ndani audio me të tjerë”.",
@@ -417,7 +453,25 @@
"thankYou": "Faleminderit që përdorni {{appName}}!",
"token": "token",
"tokenAuthFailed": "Na ndjeni, skeni leje të merrni pjesë në këtë thirrje.",
"tokenAuthFailedReason": {
"audInvalid": "Vlerë `aud` e pavlefshme. Duhet të jetë `jitsi`.",
"contextNotFound": "Prej ngarkesës mungon objekti `context`.",
"expInvalid": "Vlerë `exp` e pavlefshme.",
"featureInvalid": "Veçori e pavlefshme: {{feature}}, gjasat janë të mos jetë sendërtuar ende.",
"featureValueInvalid": "Vlerë e pavlefshme për veçorinë: {{feature}}.",
"featuresNotFound": "Prej ngarkesës mungon objekti `features`.",
"headerNotFound": "Mungon krye.",
"issInvalid": "Vlerë `iss` e pavlefshme. Duhet të jetë `chat`.",
"kidMismatch": "ID kyçi (kid) nuk përputhet me sub.",
"kidNotFound": "Mungon ID Kyçi (kid).",
"nbfFuture": "Vlera `nbf` gjendet në të ardhmen.",
"nbfInvalid": "Vlerë `nbf` e pavlefshme.",
"payloadNotFound": "Mungon ngarkesë.",
"tokenExpired": "Token-i ka skaduar."
},
"tokenAuthFailedTitle": "Mirëfilltësimi dështoi",
"tokenAuthFailedWithReasons": "Na ndjeni, nuk keni leje të merrni pjesë në këtë thirrje. Arsye e mundshme: {{reason}}",
"tokenAuthUnsupported": "Nuk mbulohet URL token-i.",
"transcribing": "Transkriptim",
"unlockRoom": "Hiq $t(lockRoomPassword) takimi",
"user": "Përdorues",
@@ -429,8 +483,12 @@
"verifyParticipantTitle": "Verifikim përdoruesi",
"videoLink": "Lidhje videoje",
"viewUpgradeOptions": "Shihni mundësi përmirësimi",
"viewUpgradeOptionsContent": "Që të përfitoni përdorim të pakufizuar veçorish me pagesë, të tilla si regjistrimi, transcriptime, RTMP Streaming & etj, duhet të përmirësoni planin tuaj.",
"viewUpgradeOptionsContent": "Që të përfitoni përdorim të pakufizuar veçorish me pagesë, të tilla si regjistrimi, transkriptime, RTMP Streaming & etj, duhet të përmirësoni planin tuaj.",
"viewUpgradeOptionsTitle": "Zbuluat një veçori me pagesë!",
"whiteboardLimitContent": "Na ndjeni, është mbërritur te kufiri i tabelave të njëkohshme.",
"whiteboardLimitReference": "Për më tepër hollësi, ju lutemi, vizitoni",
"whiteboardLimitReferenceUrl": "sajtin tonë",
"whiteboardLimitTitle": "Kufizim përdorimi tabele",
"yourEntireScreen": "Krejt ekranin tuaj"
},
"documentSharing": {
@@ -443,6 +501,9 @@
"title": "Trupëzojeni këtë takim"
},
"feedback": {
"accessibilityLabel": {
"yourChoice": "Zgjedhja juaj: {{rating}}"
},
"average": "Çka",
"bad": "I dobët",
"detailsLabel": "Na thoni më tepër rreth tij.",
@@ -499,13 +560,16 @@
"noNumbers": "Ska numra për përdorim.",
"noPassword": "Asnjë",
"noRoom": "Su dha dhomë për të cilën të formësohet numri.",
"noWhiteboard": "Su ngarkua dot tabela.",
"numbers": "Numra Për Tu Përdorur",
"password": "$t(lockRoomPasswordUppercase): ",
"reachedLimit": "Keni mbërritur në kufijtë e planit tuaj.",
"sip": "Adresë SIP",
"sipAudioOnly": "Adresë SIP vetëm për audio",
"title": "Ndani me të tjerë",
"tooltip": "Ndani me të tjerë lidhje dhe hollësi numrash për këtë takim",
"upgradeOptions": "Ju lutemi, shihni mundësitë e përmirësimit, te"
"upgradeOptions": "Ju lutemi, shihni mundësitë e përmirësimit, te",
"whiteboardError": "Gabim në ngarkimin e tabelës. Ju lutemi, riprovoni më vonë."
},
"inlineDialogFailure": {
"msg": "Ngecëm pak.",
@@ -607,13 +671,13 @@
"knockingParticipantList": "Listë pjesëmarrësish që duan të hyjnë",
"lobbyChatStartedNotification": "{{moderator}} filloi një fjalosje në holl me {{attendee}}",
"lobbyChatStartedTitle": "{{moderator}} ka filluar një fjalosje në holl me ju.",
"lobbyClosed": "Dhoma holl u mbyll.",
"nameField": "Jepni emrin tuaj",
"notificationLobbyAccessDenied": "Hyrja e {{targetParticipantName}} është hedhur poshtë nga {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} është lejuar të hyjë nga {{originParticipantName}}",
"notificationLobbyDisabled": "Holli është çaktivizuar nga {{originParticipantName}}",
"notificationLobbyEnabled": "Holli është aktivizuar nga {{originParticipantName}}",
"notificationTitle": "Holl",
"passwordField": "Jepni fjalëkalim takimi",
"passwordJoinButton": "Hyni",
"title": "Holl",
"toggleLabel": "Aktivizoni hollin"
@@ -646,6 +710,8 @@
"sessionToken": "Token Sesioni",
"start": "Nis Regjistrim",
"stop": "Ndale Regjistrimin",
"stopping": "Po ndalet Regjistrimi",
"wait": "Ju lutemi, prisni, teksa ruajmë regjistrimin tuaj",
"yes": "Po"
},
"lockRoomPassword": "fjalëkalim",
@@ -667,8 +733,11 @@
"connectedTwoMembers": "{{first}} dhe {{second}} tjetër hynë në takim",
"dataChannelClosed": "Rënie cilësie videoje",
"dataChannelClosedDescription": "Kanali urë u shkëput, kështu që cilësia e videos është kufizuar te vlera më e ulët.",
"disabledIframe": "Trupëzimi është menduar vetëm për qëllime demonstrimi, ndaj kjo thirrje do të ndërpritet pas {{timeout}} minutash.",
"disabledIframeSecondary": "Trupëzimi i {{domain}} është menduar vetëm për qëllime demonstrimi, ndaj kjo thirrje do të ndërpritet pas {{timeout}} minutash. Ju lutemi, për trupëzime të njëmendta përdorni <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a>!",
"disconnected": "u shkëput",
"displayNotifications": "Shfaq njoftime për",
"dontRemindMe": "Mos ma kujto",
"focus": "Fokusi te konferenca",
"focusFail": "{{component}} jo i passhëm - riprovoni pas {{ms}} sekondash",
"gifsMenu": "GIPHY",
@@ -677,6 +746,7 @@
"invitedOneMember": "{{name}} u ftua",
"invitedThreePlusMembers": "{{name}} dhe {{count}} të tjerë u ftuan",
"invitedTwoMembers": "{{first}} dhe {{second}} u ftuan",
"joinMeeting": "Hyni",
"kickParticipant": "{{kicked}} u përzu nga {{kicker}}",
"leftOneMember": "{{name}} doli nga takimi",
"leftThreePlusMembers": "{{name}} dhe mjaft të tjerë dolën nga takimi",
@@ -711,7 +781,6 @@
"newDeviceCameraTitle": "U pikas kamerë e re",
"noiseSuppressionDesktopAudioDescription": "Mbytja e zhurmave smund të aktivizohet teksa ndahet me të tjerët audioja e desktopit, ju lutemi, çaktivizojeni dhe riprovoni.",
"noiseSuppressionFailedTitle": "Su arrit të nisej mbytja e zhurmave",
"noiseSuppressionNoTrackDescription": "Ju lutemi, së pari, çheshtoni mikrofonin tuaj.",
"noiseSuppressionStereoDescription": "Aktualisht nuk mbulohet mbytje zhurmash audioje stereo.",
"oldElectronClientDescription1": "Duket se përdorni një version të vjetër të klientit Jitsi Meet, i cili ka cenueshmëri të ditura sigurie. Ju lutemi, siguroni përditësimin me ",
"oldElectronClientDescription2": "montimin tonë më të ri",
@@ -735,13 +804,18 @@
"startSilentTitle": "Hytë pa zë në dalje!",
"suboptimalBrowserWarning": "Kemi frikë se funksionimi i takimit për ju sdo të jetë kushedi këtu. Po kërkojmë rrugë për ta përmirësuar këtë punë, por deri atëherë, ju lutemi, provoni të përdorni një nga <a href='{{recommendedBrowserPageLink}}' target='_blank'>shfletuesit e mbuluar plotësisht</a>.",
"suboptimalExperienceTitle": "Sinjalizim Mbi Shfletuesin",
"suggestRecordingAction": "Niseni",
"suggestRecordingDescription": "Do të donit të nisej një regjistrim?",
"suggestRecordingTitle": "Regjistroje këtë takim",
"unmute": "Çheshtoje",
"videoMutedRemotelyDescription": "Mundeni përherë ta rihapni.",
"videoMutedRemotelyTitle": "Videoja juaj u mbyll nga {{moderator}}",
"videoUnmuteBlockedDescription": "Heqja e heshtimit të kamerës dhe veprimi i tregimit të desktopit janë bllokuar përkohësisht për shkak kufizimesh të sistemit.",
"videoUnmuteBlockedTitle": "Heqja e heshtimit të kamerës dhe tregimi i desktopit janë bllokuar!",
"viewLobby": "Shihni hollin",
"waitingParticipants": "{{waitingParticipants}} vetë"
"waitingParticipants": "{{waitingParticipants}} vetë",
"whiteboardLimitDescription": "Ju lutemi, ruani çkeni bërë, ngaqë së shpejti do të mbërrihet në kufi përdoruesi dhe tabela do të mbyllet.",
"whiteboardLimitTitle": "Përdorim tabele"
},
"participantsPane": {
"actions": {
@@ -752,6 +826,7 @@
"askUnmute": "Kërkoni heqje heshtimi",
"audioModeration": "Heqin heshtim të vetes",
"blockEveryoneMicCamera": "Bllokoni mikrofonin dhe kamerën e gjithkujt",
"breakoutRooms": "Dhoma anekse konsultimesh",
"invite": "Ftoni Dikë",
"moreModerationActions": "Më tepër mundësi moderimi",
"moreModerationControls": "Më tepër kontrolle moderimi",
@@ -769,7 +844,8 @@
"headings": {
"lobby": "Holli ({{count}})",
"participantsList": "Pjesëmarrës në takim ({{count}})",
"visitors": "Vizitorë ({{count}})",
"visitorRequests": " (requests {{count}})",
"visitors": "Vizitorë {{count}}",
"waitingLobby": "Duke pritur në holl ({{count}})"
},
"search": "Kërkoni te pjesëmarrësit",
@@ -859,12 +935,15 @@
"joinWithoutAudio": "Merrni pjesë pa audio",
"keyboardShortcuts": "Aktivizo shkurtore tastiere",
"linkCopied": "Lidhja u kopjua në të papastër",
"lookGood": "Mikrofoni juaj po punon si duhet",
"lookGood": "Gjithçka po punon si duhet",
"or": "ose",
"premeeting": "Para takimit",
"proceedAnyway": "Vazhdo, sido qoftë",
"recordingWarning": "Këtë thirrje pjesëmarrës të tjerë mund ta regjistrojnë",
"screenSharingError": "Gabim ndarjeje ekrani me të tjerë:",
"showScreen": "Aktivizoni skenë para takimit",
"startWithPhone": "Nise me audio telefoni",
"unsafeRoomConsent": "I kuptoj rreziqet, dëshiroj të marr pjesë te takimi",
"videoOnlyError": "Gabim video:",
"videoTrackError": "Su krijua dot pistë video.",
"viewAllNumbers": "shihni krejt numrat"
@@ -885,9 +964,9 @@
},
"profile": {
"avatar": "avatar",
"setDisplayNameLabel": "Caktoni emrin tuaj për në ekran",
"setDisplayNameLabel": "Emër",
"setEmailInput": "Jepni email",
"setEmailLabel": "Caktoni email të gravatarit tuaj",
"setEmailLabel": "Jepni email të gravatarit tuaj",
"title": "Profil"
},
"raisedHand": "Do të donte të fliste",
@@ -919,17 +998,16 @@
"failedToStart": "Su arrit të niset regjistrimi",
"fileSharingdescription": "Ndajeni regjistrimin me pjesëmarrësit në takim",
"highlight": "Nxjerrje në pah",
"highlightMoment": "Nxirni në pah një çast",
"highlightMoment": "Nxirrni në pah një çast",
"highlightMomentDisabled": "Mund të nxirrni në pah çaste kur fillon regjistrimi",
"highlightMomentSuccess": "Çasti u nxorr në pah",
"highlightMomentSuccess": "Çasti u nxor në pah",
"highlightMomentSucessDescription": "Çasti i nxjerrë në pah nga ju do të shtohet te përmbledhja e takimit.",
"inProgress": "Regjistrim ose transmetim drejtpërsëdrejti në ecuri e sipër",
"limitNotificationDescriptionNative": "Për shkak kërkesash të shumta, regjistrimi juaj do të kufizohet në {{limit}} min. Për regjistrime të pakufizuara provoni <3>{{app}}</3>.",
"limitNotificationDescriptionWeb": "Për shkak kërkesash të shumta, regjistrimi juaj do të kufizohet në {{limit}} min. Për regjistrime të pakufizuara provoni <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"linkGenerated": "Kemi prodhuar një lidhje për te regjistrimi juaj.",
"live": "DREJTPËRSËDREJTI",
"localRecordingNoNotificationWarning": "Regjistrimi sdo tu njoftohet pjesëmarrësve të tjerë. Do tju duhet ti vini në dijeni se takimi po regjistrohet.",
"localRecordingNoVideo": "Videoja nuk po regjistrohet",
"localRecordingNoVideo": "Videoja spo regjistrohet",
"localRecordingStartWarning": "Ju lutemi, sigurohuni se e ndalni regjistrimin para se dilni nga takimi, që të mund ta ruani.",
"localRecordingStartWarningTitle": "Ndaleni regjistrimin që ta ruani",
"localRecordingVideoStop": "Ndalja e videos tuaj do të ndalë gjithashtu edhe regjistrimin vendor. Jeni i sigurt se doni të vazhdohet?",
@@ -944,13 +1022,15 @@
"onBy": "{{name}} nisi regjistrimin",
"onlyRecordSelf": "Regjistro vetëm rrjedhat e mia audio dhe video",
"pending": "Po përgatitet të regjistrohet takimi…",
"rec": "REC",
"recordAudioAndVideo": "Regjistro audio dhe video",
"recordTranscription": "Regjistro transkriptimin",
"saveLocalRecording": "Ruajeni lokalisht kartelën e regjistrimit (Beta)",
"serviceDescription": "Regjistrimi juaj do të ruhet nga shërbimi i regjistrimit",
"serviceDescriptionCloud": "Regjistrim në re",
"serviceDescriptionCloudInfo": "Takimet e regjistruara spastrohen automatikisht 24h pas kohës së regjistrimit të tyre.",
"serviceName": "Shërbim regjistrimi",
"sessionAlreadyActive": "Ky sesion po regjistrohet ose transmetohet drejtpërsëdrejti tashmë.",
"showAdvancedOptions": "Mundësi të mëtejshme",
"signIn": "Hyni",
"signOut": "Dilni",
"surfaceError": "Ju lutemi, përdorni skedën e tanishme.",
@@ -966,10 +1046,17 @@
"security": {
"about": "Takimit tuaj mund ti shtoni një $t(lockRoomPassword). Pjesëmarrësve do tu duhet të japin $t(lockRoomPassword) përpara se të lejohen të marrin pjesë në takim.",
"aboutReadOnly": "Pjesëmarrësit moderatorë mund ti shtojnë takimit një $t(lockRoomPassword). Pjesëmarrësve do tu duhet të japin $t(lockRoomPassword) përpara se të lejohen të marrin pjesë në takim.",
"insecureRoomNameWarning": "Emri i dhomës sështë pa rrezik. Pjesëmarrës të padëshiruar munden të marrin pjesë në konferencën tuaj. Shihni mundësinë e bërjes të sigurt të takimit tuaj, duke përdorur butonin e sigurisë.",
"title": "Mundësi Sigurie"
"insecureRoomNameWarningNative": "Emri i dhomës sështë pa rrezik. Pjesëmarrës të padëshiruar munden të hyjnë në takimin tuaj. {{recommendAction}} Mësoni më tepër rreth sigurimit të takimit tuaj ",
"insecureRoomNameWarningWeb": "Emri i dhomës sështë pa rrezik. Pjesëmarrës të padëshiruar munden të hyjnë në takimin tuaj. {{recommendAction}} Mësoni më tepër rreth sigurimit të takimit tuaj <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">këtu</a>.",
"title": "Mundësi Sigurie",
"unsafeRoomActions": {
"meeting": "Shihni mundësinë e sigurimit të takimit tuaj, duke përdorur butonin e sigurisë.",
"prejoin": "Shihni mundësinë e përdorimit për takimin të një emri më të veçantë.",
"welcome": "Shihni mundësinë e përdorimit për takimin të një emri më të veçantë, ose zgjidhni një nga sugjerimet."
}
},
"settings": {
"audio": "Audio",
"buttonLabel": "Rregullime",
"calendar": {
"about": "Integrimi me kalendar {{appName}} përdoret për të hyrë me sukses te kalendari juaj, që kështu të mund të lexohen veprimtari të ardhshme.",
@@ -990,9 +1077,11 @@
"maxStageParticipants": "Numër maksimum pjesëmarrësish që mund të fiksohen te skena kryesore (EKSPERIMENTALe)",
"microphones": "Mikrofona",
"moderator": "Moderator",
"more": "Më tepër",
"moderatorOptions": "Mundësi moderatori",
"more": "Të përgjitshme",
"name": "Emër",
"noDevice": "Asnjë",
"notifications": "Njoftime",
"participantJoined": "Hyri Pjesëmarrës",
"participantKnocking": "Pjesëmarrës hyri në holl",
"participantLeft": "Doli Pjesëmarrës",
@@ -1003,13 +1092,14 @@
"selectCamera": "Kamerë",
"selectMic": "Mikrofon",
"selfView": "Parje e vetes",
"sounds": "Tinguj",
"shortcuts": "Shkurtore",
"speakers": "Altoparlantë",
"startAudioMuted": "Gjithkush fillon i heshtuar",
"startReactionsMuted": "Heshto tinguj reagimesh për këdo",
"startVideoMuted": "Gjithkush fillon i fshehur",
"talkWhileMuted": "Flisni, teksa jeni i heshtuar",
"title": "Rregullime"
"title": "Rregullime",
"video": "Video"
},
"settingsView": {
"advanced": "Të mëtejshme",
@@ -1017,6 +1107,7 @@
"alertOk": "OK",
"alertTitle": "Kujdes",
"alertURLText": "URL-ja e dhënë për shërbyesin është e pavlefshme",
"apply": "Aplikoji",
"buildInfoSection": "Hollësi Montimi",
"conferenceSection": "Konferencë",
"disableCallIntegration": "Çaktivizo integrim thirrje me sistemin",
@@ -1027,19 +1118,21 @@
"displayNamePlaceholderText": "P.sh.: Zamir Gjoli",
"email": "Email",
"emailPlaceholderText": "email@example.com",
"gavatarMessage": "Nëse emal-i juaj është i përshoqëruar me një llogari Gravatar, do ta përdorim për të shfaqur foton e profilit tuaj.",
"goTo": "Kalo te",
"header": "Rregullime",
"help": "Ndihmë",
"links": "Lidhje",
"privacy": "Privatësi",
"profileSection": "Profil",
"sdkVersion": "Version SDK",
"serverURL": "URL Shërbyesi",
"showAdvanced": "Shfaq rregullime të mëtejshme",
"startCarModeInLowBandwidthMode": "Nën mënyrën “gjerësi e ulët bande” nis mënyrën automjet",
"startWithAudioMuted": "Fillo me audio të mbyllur",
"startWithVideoMuted": "Fillo me video të mbyllur",
"terms": "Terma",
"version": "Version"
"version": "Version aplikacioni"
},
"share": {
"dialInfoText": "\n\n=====\n\nThjesht doni ti bini numrit në telefonin tuaj?\n\n{{defaultDialInNumber}}Klikoni mbi këtë lidhje, që të shihni numra telefoni për këtë takim\n{{dialInfoPageUrl}}",
@@ -1050,7 +1143,7 @@
"angry": "I zemëruar",
"disgusted": "I pështirosur",
"displayEmotions": "Shfaq emocione",
"fearful": "I firkësuar",
"fearful": "I frikësuar",
"happy": "I gëzuar",
"hours": "{{count}}h",
"minutes": "{{count}}m",
@@ -1058,7 +1151,7 @@
"neutral": "Asnjanës",
"sad": "I trishtuar",
"search": "Kërko",
"searchHint": "Kërkoni pjesëmarrësit",
"searchHint": "Kërkoni për pjesëmarrës",
"seconds": "{{count}}s",
"speakerStats": "Statistika Folësi",
"speakerTime": "Kohë Folësi",
@@ -1081,7 +1174,7 @@
"toolbar": {
"Settings": "Rregullime",
"accessibilityLabel": {
"Settings": "Shfaq/Fshih rregullimet",
"Settings": "Hap rregullimet",
"audioOnly": "Hap/Mbyll vetëm audion",
"audioRoute": "Përzgjidhni pajisje zëri",
"boo": "Ya",
@@ -1091,11 +1184,20 @@
"cc": "Shfaq/Fshih titra",
"chat": "Hapni / Mbyllni fjalosje",
"clap": "Duartrokitje",
"closeChat": "Mbylle fjalosjen",
"closeMoreActions": "Mbyll menunë Më Tepër Veprime",
"closeParticipantsPane": "Mbyll kuadratin pjesëmarrës",
"collapse": "Tkurre",
"document": "Shfaq/Fshih dokument të ndarë",
"documentClose": "Mbyll dokument të ndarë",
"documentOpen": "Hap dokument të ndarë",
"download": "Shkarkoni aplikacionet tona",
"embedMeeting": "Trupëzoni takimin",
"endConference": "Përfundoje takimin për të tërë",
"enterFullScreen": "Shiheni sa krejt ekrani",
"enterTileView": "Kaloni nën mënyrën me kuadrate",
"exitFullScreen": "Dil nga mënyra “Sa krejt ekrani”",
"exitTileView": "Dil nga mënyra me kuadrate",
"expand": "Zgjeroje",
"feedback": "Lini përshtypje",
"fullScreen": "Kalo nën/Dil nga mënyra “Sa krejt ekrani”",
@@ -1104,6 +1206,7 @@
"hangup": "Braktiseni takimin",
"heading": "Panel",
"help": "Ndihmë",
"hideWhiteboard": "Fshihe tabelën",
"invite": "Ftoni njerëz",
"kick": "Përzëre pjesëmarrësin",
"laugh": "E qeshur",
@@ -1113,21 +1216,25 @@
"lobbyButton": "Aktivizo/Çaktivizoni mënyrën holl",
"localRecording": "Shfaq/Fshih kontrolle regjistrimi vendor",
"lockRoom": "Aktivizo/Çaktivizo fjalëkalim takimi",
"lowerHand": "Ulni dorën",
"moreActions": "Më tepër veprime",
"moreActionsMenu": "Menu “Më tepër veprime”",
"moreOptions": "Shfaq më tepër mundësi",
"mute": "Heshtoje / Çheshtoje",
"mute": "Heshto mikrofonin",
"muteEveryone": "Heshto gjithkënd",
"muteEveryoneElse": "Heshto gjithkënd tjetër",
"muteEveryoneElsesVideoStream": "Ndal videon e gjithkujt tjetër",
"muteEveryonesVideoStream": "Ndal videon e gjithkujt",
"muteGUMPending": "Po lidhet mikrofoni juaj",
"noiseSuppression": "Mbytje zhurmash",
"participants": "Pjesëmarrës",
"openChat": "Hapni fjalosje",
"participants": "Hapni kuadrat pjesëmarrësish",
"pip": "Aktivizo/Çaktivizo mënyrën “Picture-in-Picture”",
"privateMessage": "Dërgoni mesazh privat",
"profile": "Përpunoni profilin tuaj",
"raiseHand": "Ngrini / Ulni dorën tuaj",
"reactionsMenu": "Hap / Mbyll menu reagimesh",
"raiseHand": "Ngrini dorën",
"reactions": "Reagime",
"reactionsMenu": "Menu reagimesh",
"recording": "Nis/Ndal regjistrimin",
"remoteMute": "Heshto pjesëmarrësin",
"remoteVideoMute": "Çaktivizo kamerën e pjesëmarrësit",
@@ -1135,20 +1242,25 @@
"selectBackground": "Përzgjidhni Sfond",
"selfView": "Shfaq/Fshih pamje të vetes",
"shareRoom": "Ftoni dikë",
"shareYourScreen": "Nisni / Ndalni tregimin e ekranit tuaj",
"shareYourScreen": "Nisni tregimin e ekranit tuaj",
"shareaudio": "Ndani audio me të tjerë",
"sharedvideo": "Aktivizo/Çaktivizo ndarje videoje me të tjerë",
"sharedvideo": "Ndani video me të tjerë",
"shortcuts": "Shfaq/Fshih shkurtore",
"show": "Shfaqe në skenë",
"showWhiteboard": "Shfaq tabelë",
"silence": "Heshtje",
"speakerStats": "Shfaq/Fshih statistika folësi",
"stopScreenSharing": "Ndalni tregimin e ekranit tuaj",
"stopSharedVideo": "Ndalni videon",
"surprised": "I befasuar",
"tileView": "Kalo në/Dil nga mënyra mozaik",
"toggleCamera": "Hap/Mbyll kamerën",
"toggleFilmstrip": "Shfaq/Fshih shirit filmi",
"unmute": "Hiqni heshtim mikrofoni",
"videoblur": "Aktivizo/Çaktivizo turbullim videoje",
"videomute": "Nis / Ndal kamerën",
"whiteboard": "Shfaq / Fshih tabelën"
"videomute": "Ndal kamerën",
"videomuteGUMPending": "Po lidhet kamera juaj",
"videounmute": "Nis kamerën"
},
"addPeople": "Shtoni persona te thirrja juaj",
"audioOnlyOff": "Çaktivizo mënyrën “Sasi e ulët të dhënash trafiku”",
@@ -1161,6 +1273,7 @@
"chat": "Hap / Mbyll fjalosje",
"clap": "Duartrokitje",
"closeChat": "Mbyll fjalosjen",
"closeParticipantsPane": "Mbylle kuadratin e pjesëmarrësve",
"closeReactionsMenu": "Mbyll menu reagimesh",
"disableNoiseSuppression": "Çaktivizo mbytje zhurmash",
"disableReactionSounds": "Mund të çaktivizoni tinguj reagimesh për këtë takim",
@@ -1169,6 +1282,7 @@
"download": "Shkarkoni aplikacione tonat",
"e2ee": "Fshehtëzim Skaj-Më-Skaj",
"embedMeeting": "Trupëzoni takim",
"enableNoiseSuppression": "Aktivizoni mbytje zhurmash",
"endConference": "Përfundoje takimin për të tërë",
"enterFullScreen": "Shiheni sa krejt ekrani",
"enterTileView": "Kalo te pamja me kuadrate",
@@ -1193,9 +1307,10 @@
"lowerYourHand": "Ulni dorën",
"moreActions": "Më tepër veprime",
"moreOptions": "Më tepër veprime",
"mute": "Heshtoje / Hiqi heshtimin",
"mute": "Heshto mikrofonin",
"muteEveryone": "Heshto gjithkënd",
"muteEveryonesVideo": "Çaktivizo videon e gjithkujt",
"muteGUMPending": "Po lidhet mikrofoni juaj",
"noAudioSignalDesc": "Nëse se keni heshtuar që nga rregullimet e sistemit, ose nga hardware-i, shihni mundësinë e ndërrimit të pajisjes.",
"noAudioSignalDescSuggestion": "Nëse se keni heshtuar që nga rregullimet e sistemit, ose nga hardware-i, shihni mundësinë e kalimit te pajisja e sugjeruar.",
"noAudioSignalDialInDesc": "Mund ti bini numrit edhe duke përdorur:",
@@ -1210,7 +1325,7 @@
"pip": "Kalo nën mënyrën “Picture-in-Picture”",
"privateMessage": "Dërgo mesazh privat",
"profile": "Përpunoni profilin tuaj",
"raiseHand": "Ngrini / Ulni dorën",
"raiseHand": "Ngrini dorën",
"raiseYourHand": "Ngrini dorën",
"reactionBoo": "Dërgoni reagim me ya",
"reactionClap": "Dërgoni reagim me duartrokitje",
@@ -1218,6 +1333,7 @@
"reactionLike": "Dërgoni reagim me “thumbs up”",
"reactionSilence": "Dërgoni reagim me heshtje",
"reactionSurprised": "Dërgoni reagim të befasuari",
"reactions": "Reagime",
"security": "Mundësi sigurie",
"selectBackground": "Përzgjidhni sfond",
"shareRoom": "Ftoni dikë",
@@ -1237,17 +1353,17 @@
"talkWhileMutedPopup": "Po provoni të flisni? Jeni i heshtuar.",
"tileViewToggle": "Kalo në/Dil nga mënyra mozaik",
"toggleCamera": "Hapni/Mbyllni kamerën",
"videoSetting": "Rregullime videoje",
"videomute": "Nisni / Ndalni kamerën"
"unmute": "Hiqni heshtimin e mikrofonit",
"videoSettings": "Rregullime videoje",
"videomute": "Ndalni kamerën",
"videomuteGUMPending": "Po lidhet kamera juaj",
"videounmute": "Nisni kamerën"
},
"transcribing": {
"ccButtonTooltip": "Ndali / Nisi titrat",
"error": "Transkriptimi dështoi. Ju lutemi, riprovoni.",
"expandedLabel": "Transkriptimi aktualisht është aktiv",
"failedToStart": "Su arrit të nisej transkriptim",
"labelToolTip": "Takimit po i bëhet transkriptim",
"off": "Transkriptimi u ndal",
"pending": "Po përgatitet të transkriptohet takimi…",
"sourceLanguageDesc": "Aktualisht si gjuhë takimi është caktuar <b>{{sourceLanguage}}</b>. <br/> Mund ta ndryshoni që nga ",
"sourceLanguageHere": "këtu",
"start": "Fillo shfaqje titrash",
@@ -1284,7 +1400,7 @@
"audioOnly": "AUD",
"audioOnlyExpanded": "Gjendeni nën mënyrën “gjerësi e ulët bande”. Nën këtë mënyrë, do të merrni vetëm audio dhe tregim ekrani.",
"bestPerformance": "Punimi më i mirë",
"callQuality": "Cilësi videoje",
"callQuality": "Cilësi Videoje (0 për punimin më të mirë, 3 për cilësinë më të lartë)",
"hd": "CL",
"hdTooltip": "Po shihet video në cilësi të lartë",
"highDefinition": "Cilësi e Lartë",
@@ -1326,6 +1442,10 @@
"videomute": "Pjesëmarrësi ka ndalur kamerën"
},
"virtualBackground": {
"accessibilityLabel": {
"currentBackground": "Sfondi i tanishëm: {{background}}",
"selectBackground": "Përzgjidhni një sfond"
},
"addBackground": "Shtoni sfond",
"apply": "Zbatoje",
"backgroundEffectError": "Su arrit të zbatohej efekt sfondi.",
@@ -1343,13 +1463,20 @@
"none": "Asnjë",
"pleaseWait": "Ju lutemi, pritni…",
"removeBackground": "Hiqe sfondin",
"slightBlur": "Turbullim Paksa",
"slightBlur": "Gjysmë-Turbullim",
"title": "Sfonde virtualë",
"uploadedImage": "Ngarkoi figurën {{index}}",
"webAssemblyWarning": "Nuk mbulohet WebAssembly",
"webAssemblyWarningDescription": "WebAssembly e çaktivizuar ose e pambuluar nga ky shfletues"
},
"visitorsLabel": "Numër vizitorësh: {{count}}",
"visitors": {
"chatIndicator": "(vizitor)",
"labelTooltip": "Numër vizitorësh: {{count}}",
"notification": {
"description": "Që të merrni pjesë, ngrini dorën",
"title": "Jeni vizitor në takim"
}
},
"volumeSlider": "Rrëshqitës volumi",
"welcomepage": {
"accessibilityLabel": {
@@ -1382,6 +1509,7 @@
"microsoftLogo": "Stemë Microsoft-i",
"policyLogo": "Stemë Rregullash"
},
"meetingsAccessibilityLabel": "Takime",
"mobileDownLoadLinkAndroid": "Shkarkoni aplikacionin për celular me Android",
"mobileDownLoadLinkFDroid": "Shkarkoni aplikacionin për celular me F-Droid",
"mobileDownLoadLinkIos": "Shkarkoni aplikacionin për celular me iOS",
@@ -1405,6 +1533,7 @@
"whiteboard": {
"accessibilityLabel": {
"heading": "Tabelë shënimesh"
}
},
"screenTitle": "Tabelë shënimesh"
}
}

View File

@@ -222,6 +222,7 @@
"Share": "Paylaş",
"Submit": "Gönder",
"WaitForHostMsg": "Toplantısı henüz başlamadı. Toplantı sahibi sizseniz, lütfen kimlik doğrulaması yapın. Değilseniz lütfen toplantı sahibinin gelmesini bekleyin.",
"WaitingForHostButton": "Toplantı sahibini bekle",
"WaitingForHostTitle": "Toplantı sahibi bekleniyor ...",
"Yes": "Evet",
"accessibilityLabel": {

View File

@@ -219,7 +219,9 @@
"joinInBrowser": "Join in browser",
"launchMeetingLabel": "How do you want to join this meeting?",
"launchWebButton": "Launch in web",
"noDesktopApp": "You dont have the app?",
"noMobileApp": "You dont have the app?",
"or": "OR",
"termsAndConditions": "By continuing you agree to our <a href='{{termsAndConditionsLink}}' rel='noopener noreferrer' target='_blank'>terms & conditions.</a>",
"title": "Launching your meeting in {{app}}...",
"titleNew": "Launching your meeting ...",
@@ -558,6 +560,7 @@
"noNumbers": "No dial-in numbers.",
"noPassword": "None",
"noRoom": "No room was specified to dial-in into.",
"noWhiteboard": "Could not load the whiteboard.",
"numbers": "Dial-in Numbers",
"password": "$t(lockRoomPasswordUppercase): ",
"reachedLimit": "You have reached the limit of your plan.",
@@ -565,7 +568,8 @@
"sipAudioOnly": "SIP audio only address",
"title": "Share",
"tooltip": "Share link and dial-in info for this meeting",
"upgradeOptions": "Please check the upgrade options on"
"upgradeOptions": "Please check the upgrade options on",
"whiteboardError": "Error loading the whiteboard. Please try again later."
},
"inlineDialogFailure": {
"msg": "We stumbled a bit.",
@@ -615,7 +619,7 @@
"errorAPI": "An error occurred while accessing your YouTube broadcasts. Please try logging in again.",
"errorLiveStreamNotEnabled": "Live Streaming is not enabled on {{email}}. Please enable live streaming or log into an account with live streaming enabled.",
"expandedOff": "The live streaming has stopped",
"expandedOn": "The meeting is currently being streamed to YouTube.",
"expandedOn": "The meeting is currently being live streamed",
"expandedPending": "The live streaming is being started...",
"failedToStart": "Live Streaming failed to start",
"getStreamKeyManually": "We werent able to fetch any live streams. Try getting your live stream key from YouTube.",
@@ -800,6 +804,9 @@
"startSilentTitle": "You joined with no audio output!",
"suboptimalBrowserWarning": "We are afraid your meeting experience isn't going to be that great here. We are looking for ways to improve this, but until then please try using one of the <a href='{{recommendedBrowserPageLink}}' target='_blank'>fully supported browsers</a>.",
"suboptimalExperienceTitle": "Browser Warning",
"suggestRecordingAction": "Start",
"suggestRecordingDescription": "Would you like to start a recording?",
"suggestRecordingTitle": "Record this meeting",
"unmute": "Unmute",
"videoMutedRemotelyDescription": "You can always turn it on again.",
"videoMutedRemotelyTitle": "Your video has been turned off by {{participantDisplayName}}",
@@ -932,6 +939,7 @@
"or": "or",
"premeeting": "Pre meeting",
"proceedAnyway": "Proceed anyway",
"recordingWarning": "Other participants may be recording this call",
"screenSharingError": "Screen sharing error:",
"showScreen": "Enable pre meeting screen",
"startWithPhone": "Start with phone audio",
@@ -985,7 +993,7 @@
"error": "Recording failed. Please try again.",
"errorFetchingLink": "Error fetching recording link.",
"expandedOff": "Recording has stopped",
"expandedOn": "The meeting is currently being recorded.",
"expandedOn": "The meeting is currently being recorded",
"expandedPending": "Recording is being started...",
"failedToStart": "Recording failed to start",
"fileSharingdescription": "Share the recording link with the meeting participants",
@@ -1353,13 +1361,9 @@
},
"transcribing": {
"ccButtonTooltip": "Start / Stop subtitles",
"error": "Transcribing failed. Please try again.",
"expandedLabel": "Transcribing is currently on",
"failedToStart": "Transcribing failed to start",
"labelToolTip": "The meeting is being transcribed",
"off": "Transcribing stopped",
"on": "Transcribing started",
"pending": "Preparing to transcribe the meeting...",
"sourceLanguageDesc": "Currently the meeting language is set to <b>{{sourceLanguage}}</b>. <br/> You can change it from ",
"sourceLanguageHere": "here",
"start": "Start showing subtitles",
@@ -1529,6 +1533,7 @@
"whiteboard": {
"accessibilityLabel": {
"heading": "Whiteboard"
}
},
"screenTitle": "Whiteboard"
}
}

View File

@@ -113,7 +113,7 @@ import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/function
import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any';
import { muteAllParticipants } from '../../react/features/video-menu/actions';
import { setVideoQuality } from '../../react/features/video-quality/actions';
import { toggleWhiteboard } from '../../react/features/whiteboard/actions.any';
import { toggleWhiteboard } from '../../react/features/whiteboard/actions.web';
import { getJitsiMeetTransport } from '../transport';
import {

View File

@@ -229,6 +229,9 @@ export default class LargeVideoManager {
preUpdate.then(() => {
const { id, stream, videoType, resolve } = this.newStreamData;
this.newStreamData = null;
const state = APP.store.getState();
const shouldHideSelfView = getHideSelfView(state);
const localId = getLocalParticipant(state)?.id;
@@ -239,8 +242,6 @@ export default class LargeVideoManager {
// the video container type (Etherpad, SharedVideo, VideoContainer).
const isVideoContainer = LargeVideoManager.isVideoContainer(videoType);
this.newStreamData = null;
logger.debug(`Scheduled large video update for ${id}`);
this.state = videoType;
// eslint-disable-next-line no-shadow
@@ -287,10 +288,6 @@ export default class LargeVideoManager {
|| streamingStatusActive
);
this.videoTrack?.jitsiTrack?.getVideoType() === VIDEO_TYPE.DESKTOP
&& logger.debug(`Remote track ${videoTrack?.jitsiTrack}, isVideoMuted=${isVideoMuted},`
+ ` streamingStatusActive=${streamingStatusActive}, isVideoRenderable=${isVideoRenderable}`);
const isAudioOnly = APP.conference.isAudioOnly();
// Multi-stream is not supported on plan-b endpoints even if its is enabled via config.js. A virtual
@@ -303,6 +300,10 @@ export default class LargeVideoManager {
= isVideoContainer
&& ((isAudioOnly && videoType !== VIDEO_TYPE.DESKTOP) || !isVideoRenderable || legacyScreenshare);
logger.debug(`scheduleLargeVideoUpdate: Remote track ${videoTrack?.jitsiTrack}, isVideoMuted=${
isVideoMuted}, streamingStatusActive=${streamingStatusActive}, isVideoRenderable=${
isVideoRenderable}, showAvatar=${showAvatar}`);
let promise;
// do not show stream if video is muted

View File

@@ -26,6 +26,13 @@ const FADE_DURATION_MS = 300;
const logger = Logger.getLogger(__filename);
/**
* List of container events that we are going to process for the large video.
*
* NOTE: Currently used only for logging for debug purposes.
*/
const containerEvents = [ 'abort', 'canplaythrough', 'ended', 'error', 'stalled', 'suspend', 'waiting' ];
/**
* Returns an array of the video dimensions, so that it keeps it's aspect
* ratio and fits available area with it's larger dimension. This method
@@ -242,11 +249,18 @@ export class VideoContainer extends LargeContainer {
this.wrapperParent = document.getElementById('largeVideoElementsContainer');
this.avatarHeight = document.getElementById('dominantSpeakerAvatarContainer').getBoundingClientRect().height;
this.video.onplaying = function(event) {
logger.debug('Large video is playing!');
if (typeof resizeContainer === 'function') {
resizeContainer(event);
}
};
containerEvents.forEach(event => {
this.video.addEventListener(event, () => {
logger.debug(`${event} handler was called for the large video.`);
});
});
/**
* A Set of functions to invoke when the video element resizes.
*
@@ -255,6 +269,7 @@ export class VideoContainer extends LargeContainer {
this._resizeListeners = new Set();
this.video.onresize = this._onResize.bind(this);
this._play = this._play.bind(this);
}
/**
@@ -455,6 +470,30 @@ export class VideoContainer extends LargeContainer {
this._resizeListeners.delete(callback);
}
/**
* Plays the large video element.
*
* @param {number} retries - Number of retries to play the large video if play fails.
* @returns {void}
*/
_play(retries = 0) {
this.video.play()
.then(() => {
logger.debug(`Successfully played large video after ${retries + 1} retries!`);
})
.catch(e => {
if (retries < 3) {
logger.debug(`Error while trying to playing the large video. Will retry after 1s. Retries: ${
retries}. Error: ${e}`);
window.setTimeout(() => {
this._play(retries + 1);
}, 1000);
} else {
logger.error(`Error while trying to playing the large video after 3 retries: ${e}`);
}
});
}
/**
* Update video stream.
* @param {string} userID
@@ -464,6 +503,8 @@ export class VideoContainer extends LargeContainer {
setStream(userID, stream, videoType) {
this.userId = userID;
if (this.stream === stream && !stream?.forceStreamToReattach) {
logger.debug(`SetStream on the large video for user ${userID} ignored: the stream is not changed!`);
// Handles the use case for the remote participants when the
// videoType is received with delay after turning on/off the
// desktop sharing.
@@ -488,22 +529,28 @@ export class VideoContainer extends LargeContainer {
this.videoType = videoType;
if (!stream) {
logger.debug('SetStream on the large video is called without a stream argument!');
return;
}
if (this.video) {
logger.debug(`Attaching a remote track to the large video for user ${userID}`);
stream.attach(this.video).catch(error => {
logger.error(`Attaching the remote track ${stream} has failed with `, error);
logger.error(`Attaching the remote track ${stream} to large video has failed with `, error);
});
// Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
// case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
browser.isWebKitBased() && this.video.play();
browser.isWebKitBased() && this._play();
const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
this.video.style.transform = flipX ? 'scaleX(-1)' : 'none';
this._updateBackground();
} else {
logger.debug(`SetStream on the large video won't attach a track for ${
userID} because no large video element was found!`);
}
}

View File

@@ -154,6 +154,8 @@ const VideoLayout = {
updateLargeVideo(id, forceUpdate, forceStreamToReattach = false) {
if (!largeVideo) {
logger.debug(`Ignoring large video update with user id ${id}: large video not initialized yet!`);
return;
}
const currentContainer = largeVideo.getCurrentContainer();

296
package-lock.json generated
View File

@@ -42,7 +42,8 @@
"@vladmandic/human": "2.6.5",
"@vladmandic/human-models": "2.5.9",
"@xmldom/xmldom": "0.8.7",
"amplitude-js": "8.2.1",
"abab": "2.0.6",
"amplitude-js": "8.21.9",
"base64-js": "1.5.1",
"bc-css-flags": "3.0.0",
"clipboard-copy": "4.0.1",
@@ -60,7 +61,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/v1759.0.0+fc2f2490/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1777.0.0+3898d7aa/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -97,7 +98,7 @@
"react-native-svg-transformer": "1.1.0",
"react-native-tab-view": "3.5.2",
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.0.0-alpha.7",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "118.0.0",
"react-native-webview": "13.5.1",
@@ -110,6 +111,7 @@
"redux-thunk": "2.4.1",
"seamless-scroll-polyfill": "2.1.8",
"semver": "7.5.4",
"text-encoding": "0.7.0",
"tss-react": "4.4.4",
"util": "0.12.1",
"uuid": "8.3.2",
@@ -125,7 +127,7 @@
"@babel/preset-env": "7.21.5",
"@babel/preset-react": "7.16.0",
"@jitsi/eslint-config": "4.1.5",
"@types/amplitude-js": "8.16.2",
"@types/amplitude-js": "8.16.5",
"@types/audioworklet": "0.0.29",
"@types/dom-screen-wake-lock": "1.0.1",
"@types/js-md5": "0.4.3",
@@ -236,6 +238,11 @@
"extraneous": true,
"devDependencies": {}
},
"node_modules/@amplitude/analytics-connector": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz",
"integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g=="
},
"node_modules/@amplitude/react-native": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@amplitude/react-native/-/react-native-2.7.0.tgz",
@@ -246,38 +253,43 @@
}
},
"node_modules/@amplitude/types": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@amplitude/types/-/types-1.9.2.tgz",
"integrity": "sha512-s+Q/O8kNfocZiyGvVdtM5T4JGPwLRZ4Q26wtEF5xJhyCtJglGMe0ixe6u/6iW9s4JHIq+LlPlUu5095pVsdtNA==",
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@amplitude/types/-/types-1.10.2.tgz",
"integrity": "sha512-I8qenRI7uU6wKNb9LiZrAosSHVoNHziXouKY81CrqxH9xhVTEIJFXeuCV0hbtBr0Al/8ejnGjQRx+S2SvU/pPg==",
"engines": {
"node": ">=10"
}
},
"node_modules/@amplitude/ua-parser-js": {
"version": "0.7.24",
"resolved": "https://registry.npmjs.org/@amplitude/ua-parser-js/-/ua-parser-js-0.7.24.tgz",
"integrity": "sha512-VbQuJymJ20WEw0HtI2np7EdC3NJGUWi8+Xdbc7uk8WfMIF308T0howpzkQ3JFMN7ejnrcSM/OyNGveeE3TP3TA==",
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/@amplitude/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
}
],
"engines": {
"node": "*"
}
},
"node_modules/@amplitude/utils": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@amplitude/utils/-/utils-1.9.2.tgz",
"integrity": "sha512-hGOIoIjmZ0pq/3b2gBrr17TaEarlR+qzFGu5npm76+scd/51F0eNvjd0vgV6WbJT1cxhyH/5Z8kihGWOU3vS3Q==",
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@amplitude/utils/-/utils-1.10.2.tgz",
"integrity": "sha512-tVsHXu61jITEtRjB7NugQ5cVDd4QDzne8T3ifmZye7TiJeUfVRvqe44gDtf55A+7VqhDhyEIIXTA1iVcDGqlEw==",
"dependencies": {
"@amplitude/types": "^1.9.2",
"tslib": "^1.9.3"
"@amplitude/types": "^1.10.2",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@amplitude/utils/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@ampproject/remapping": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@@ -5574,11 +5586,6 @@
}
}
},
"node_modules/@react-native/normalize-color": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.1.0.tgz",
"integrity": "sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA=="
},
"node_modules/@react-native/normalize-colors": {
"version": "0.72.0",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.72.0.tgz",
@@ -6502,9 +6509,9 @@
}
},
"node_modules/@types/amplitude-js": {
"version": "8.16.2",
"resolved": "https://registry.npmjs.org/@types/amplitude-js/-/amplitude-js-8.16.2.tgz",
"integrity": "sha512-a+tb/CEQOlrHRvEvAuYNOcoUy1POERANnAhfKgiTmsy0eACj3eukGP0ucA9t115QOPzVUhbnUfZqtyHp99IZyA==",
"version": "8.16.5",
"resolved": "https://registry.npmjs.org/@types/amplitude-js/-/amplitude-js-8.16.5.tgz",
"integrity": "sha512-W73JfDpwDH4VijOGo+nVuQOqUCiqyEGGVdajU4ziWTLn27cn+QtFuFuBdlhCraIIrO52fDRO4NSOGkawtn77Jw==",
"dev": true
},
"node_modules/@types/audioworklet": {
@@ -7521,7 +7528,8 @@
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
"deprecated": "Use your platform's native atob() and btoa() methods instead"
},
"node_modules/abort-controller": {
"version": "3.0.0",
@@ -7660,36 +7668,62 @@
}
},
"node_modules/amplitude-js": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.2.1.tgz",
"integrity": "sha512-jp8lm/koTNRceO16RCTlQg9+gUbxip1esod+d0oApBCJYpxuABec2bLHXv/OkVYICvnUWoiz17AZLxiaX/aK4Q==",
"deprecated": "Excessive logging into console at default levels",
"version": "8.21.9",
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.21.9.tgz",
"integrity": "sha512-d0jJH00wbXu7sxKtVwkdSXtVffjqdUrxuACKlnzP7jU5qt9wriXXMgHifdH5Oq+buKmyF8wKL9S02gAykysURA==",
"dependencies": {
"@amplitude/ua-parser-js": "0.7.24",
"@amplitude/utils": "^1.0.5",
"blueimp-md5": "^2.10.0",
"query-string": "5"
"@amplitude/analytics-connector": "^1.4.6",
"@amplitude/ua-parser-js": "0.7.33",
"@amplitude/utils": "^1.10.2",
"@babel/runtime": "^7.21.0",
"blueimp-md5": "^2.19.0",
"query-string": "8.1.0"
}
},
"node_modules/amplitude-js/node_modules/decode-uri-component": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
"integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
"engines": {
"node": ">=14.16"
}
},
"node_modules/amplitude-js/node_modules/filter-obj": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
"integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/amplitude-js/node_modules/query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-8.1.0.tgz",
"integrity": "sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw==",
"dependencies": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"decode-uri-component": "^0.4.1",
"filter-obj": "^5.1.0",
"split-on-first": "^3.0.0"
},
"engines": {
"node": ">=0.10.0"
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/amplitude-js/node_modules/strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"node_modules/amplitude-js/node_modules/split-on-first": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
"integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
"engines": {
"node": ">=0.10.0"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/anser": {
@@ -9335,16 +9369,6 @@
"node": ">= 0.6"
}
},
"node_modules/deprecated-react-native-prop-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz",
"integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==",
"dependencies": {
"@react-native/normalize-color": "*",
"invariant": "*",
"prop-types": "*"
}
},
"node_modules/destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
@@ -12872,11 +12896,6 @@
"rollup": ">= 1.0.0"
}
},
"node_modules/keymirror": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
"integrity": "sha512-vIkZAFWoDijgQT/Nvl2AHCMmnegN2ehgTPYuyy2hWQkQSntI0S7ESYqdLkoSe1HyEBFHHkCgSIvVdSEiWwKvCg=="
},
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -12924,8 +12943,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1759.0.0+fc2f2490/lib-jitsi-meet.tgz",
"integrity": "sha512-YCFcx5SxyFDJXc0zeTIvF8jxlwgqgFlEq8UFCa2c68gpEwOE5iSOVn9tQT9jXXk9FAJhN9aUr5e2ar34sUYl3w==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1777.0.0+3898d7aa/lib-jitsi-meet.tgz",
"integrity": "sha512-qs18nB89iQgiNR/UWTg/GCuEqUAIBdJRyFv9r0EaDThkQS8vHLNzYO9UQId8xZxiOGOR2kunJO84SbcivOTqbw==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -16953,13 +16972,12 @@
}
},
"node_modules/react-native-video": {
"version": "6.0.0-alpha.7",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.0.0-alpha.7.tgz",
"integrity": "sha512-X/siSaJf0V//IbnozjDm1jAjNaXlFy6Hbr6X8GNFl/ztLvN+Z8R/Quq9Q8o22XVwlPacPQ9VS/G0Stdktn0FEw==",
"dependencies": {
"deprecated-react-native-prop-types": "^2.2.0",
"keymirror": "^0.1.1",
"prop-types": "^15.7.2"
"version": "6.0.0-alpha.11",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.0.0-alpha.11.tgz",
"integrity": "sha512-Z1FqIkNBqQWdBVKoh5WlmM01LIVhxlOsddKVV9IzMJ3EDl8PAU4ln7hdo85RHCHhgWSHzathPDo0UK7gPB48MA==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-watch-connectivity": {
@@ -18646,6 +18664,12 @@
"node": ">=0.10.0"
}
},
"node_modules/text-encoding": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz",
"integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==",
"deprecated": "no longer maintained"
},
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -20154,35 +20178,33 @@
}
},
"dependencies": {
"@amplitude/analytics-connector": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz",
"integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g=="
},
"@amplitude/react-native": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@amplitude/react-native/-/react-native-2.7.0.tgz",
"integrity": "sha512-2dMxCVgRPwReHRDm9JKbL+sZGyozJlcdr5Jokv8TQR7idNxxGmm4YSYkjhGjSWkoEyGEyy+lh9kRJQL/DcUWJQ=="
},
"@amplitude/types": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@amplitude/types/-/types-1.9.2.tgz",
"integrity": "sha512-s+Q/O8kNfocZiyGvVdtM5T4JGPwLRZ4Q26wtEF5xJhyCtJglGMe0ixe6u/6iW9s4JHIq+LlPlUu5095pVsdtNA=="
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@amplitude/types/-/types-1.10.2.tgz",
"integrity": "sha512-I8qenRI7uU6wKNb9LiZrAosSHVoNHziXouKY81CrqxH9xhVTEIJFXeuCV0hbtBr0Al/8ejnGjQRx+S2SvU/pPg=="
},
"@amplitude/ua-parser-js": {
"version": "0.7.24",
"resolved": "https://registry.npmjs.org/@amplitude/ua-parser-js/-/ua-parser-js-0.7.24.tgz",
"integrity": "sha512-VbQuJymJ20WEw0HtI2np7EdC3NJGUWi8+Xdbc7uk8WfMIF308T0howpzkQ3JFMN7ejnrcSM/OyNGveeE3TP3TA=="
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/@amplitude/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA=="
},
"@amplitude/utils": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@amplitude/utils/-/utils-1.9.2.tgz",
"integrity": "sha512-hGOIoIjmZ0pq/3b2gBrr17TaEarlR+qzFGu5npm76+scd/51F0eNvjd0vgV6WbJT1cxhyH/5Z8kihGWOU3vS3Q==",
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@amplitude/utils/-/utils-1.10.2.tgz",
"integrity": "sha512-tVsHXu61jITEtRjB7NugQ5cVDd4QDzne8T3ifmZye7TiJeUfVRvqe44gDtf55A+7VqhDhyEIIXTA1iVcDGqlEw==",
"requires": {
"@amplitude/types": "^1.9.2",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
"@amplitude/types": "^1.10.2",
"tslib": "^2.0.0"
}
},
"@ampproject/remapping": {
@@ -23970,11 +23992,6 @@
}
}
},
"@react-native/normalize-color": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.1.0.tgz",
"integrity": "sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA=="
},
"@react-native/normalize-colors": {
"version": "0.72.0",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.72.0.tgz",
@@ -24628,9 +24645,9 @@
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="
},
"@types/amplitude-js": {
"version": "8.16.2",
"resolved": "https://registry.npmjs.org/@types/amplitude-js/-/amplitude-js-8.16.2.tgz",
"integrity": "sha512-a+tb/CEQOlrHRvEvAuYNOcoUy1POERANnAhfKgiTmsy0eACj3eukGP0ucA9t115QOPzVUhbnUfZqtyHp99IZyA==",
"version": "8.16.5",
"resolved": "https://registry.npmjs.org/@types/amplitude-js/-/amplitude-js-8.16.5.tgz",
"integrity": "sha512-W73JfDpwDH4VijOGo+nVuQOqUCiqyEGGVdajU4ziWTLn27cn+QtFuFuBdlhCraIIrO52fDRO4NSOGkawtn77Jw==",
"dev": true
},
"@types/audioworklet": {
@@ -25577,30 +25594,42 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
},
"amplitude-js": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.2.1.tgz",
"integrity": "sha512-jp8lm/koTNRceO16RCTlQg9+gUbxip1esod+d0oApBCJYpxuABec2bLHXv/OkVYICvnUWoiz17AZLxiaX/aK4Q==",
"version": "8.21.9",
"resolved": "https://registry.npmjs.org/amplitude-js/-/amplitude-js-8.21.9.tgz",
"integrity": "sha512-d0jJH00wbXu7sxKtVwkdSXtVffjqdUrxuACKlnzP7jU5qt9wriXXMgHifdH5Oq+buKmyF8wKL9S02gAykysURA==",
"requires": {
"@amplitude/ua-parser-js": "0.7.24",
"@amplitude/utils": "^1.0.5",
"blueimp-md5": "^2.10.0",
"query-string": "5"
"@amplitude/analytics-connector": "^1.4.6",
"@amplitude/ua-parser-js": "0.7.33",
"@amplitude/utils": "^1.10.2",
"@babel/runtime": "^7.21.0",
"blueimp-md5": "^2.19.0",
"query-string": "8.1.0"
},
"dependencies": {
"decode-uri-component": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
"integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ=="
},
"filter-obj": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
"integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng=="
},
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-8.1.0.tgz",
"integrity": "sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"decode-uri-component": "^0.4.1",
"filter-obj": "^5.1.0",
"split-on-first": "^3.0.0"
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
"split-on-first": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
"integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA=="
}
}
},
@@ -26841,16 +26870,6 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"deprecated-react-native-prop-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz",
"integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==",
"requires": {
"@react-native/normalize-color": "*",
"invariant": "*",
"prop-types": "*"
}
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
@@ -29474,11 +29493,6 @@
"debounce": "^1.2.0"
}
},
"keymirror": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
"integrity": "sha512-vIkZAFWoDijgQT/Nvl2AHCMmnegN2ehgTPYuyy2hWQkQSntI0S7ESYqdLkoSe1HyEBFHHkCgSIvVdSEiWwKvCg=="
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -29513,8 +29527,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1759.0.0+fc2f2490/lib-jitsi-meet.tgz",
"integrity": "sha512-YCFcx5SxyFDJXc0zeTIvF8jxlwgqgFlEq8UFCa2c68gpEwOE5iSOVn9tQT9jXXk9FAJhN9aUr5e2ar34sUYl3w==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1777.0.0+3898d7aa/lib-jitsi-meet.tgz",
"integrity": "sha512-qs18nB89iQgiNR/UWTg/GCuEqUAIBdJRyFv9r0EaDThkQS8vHLNzYO9UQId8xZxiOGOR2kunJO84SbcivOTqbw==",
"requires": {
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
@@ -32442,14 +32456,9 @@
}
},
"react-native-video": {
"version": "6.0.0-alpha.7",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.0.0-alpha.7.tgz",
"integrity": "sha512-X/siSaJf0V//IbnozjDm1jAjNaXlFy6Hbr6X8GNFl/ztLvN+Z8R/Quq9Q8o22XVwlPacPQ9VS/G0Stdktn0FEw==",
"requires": {
"deprecated-react-native-prop-types": "^2.2.0",
"keymirror": "^0.1.1",
"prop-types": "^15.7.2"
}
"version": "6.0.0-alpha.11",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.0.0-alpha.11.tgz",
"integrity": "sha512-Z1FqIkNBqQWdBVKoh5WlmM01LIVhxlOsddKVV9IzMJ3EDl8PAU4ln7hdo85RHCHhgWSHzathPDo0UK7gPB48MA=="
},
"react-native-watch-connectivity": {
"version": "1.1.0",
@@ -33675,6 +33684,11 @@
}
}
},
"text-encoding": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz",
"integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA=="
},
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",

View File

@@ -48,7 +48,8 @@
"@vladmandic/human": "2.6.5",
"@vladmandic/human-models": "2.5.9",
"@xmldom/xmldom": "0.8.7",
"amplitude-js": "8.2.1",
"abab": "2.0.6",
"amplitude-js": "8.21.9",
"base64-js": "1.5.1",
"bc-css-flags": "3.0.0",
"clipboard-copy": "4.0.1",
@@ -66,7 +67,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/v1759.0.0+fc2f2490/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1777.0.0+3898d7aa/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -103,7 +104,7 @@
"react-native-svg-transformer": "1.1.0",
"react-native-tab-view": "3.5.2",
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.0.0-alpha.7",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "118.0.0",
"react-native-webview": "13.5.1",
@@ -116,6 +117,7 @@
"redux-thunk": "2.4.1",
"seamless-scroll-polyfill": "2.1.8",
"semver": "7.5.4",
"text-encoding": "0.7.0",
"tss-react": "4.4.4",
"util": "0.12.1",
"uuid": "8.3.2",
@@ -131,7 +133,7 @@
"@babel/preset-env": "7.21.5",
"@babel/preset-react": "7.16.0",
"@jitsi/eslint-config": "4.1.5",
"@types/amplitude-js": "8.16.2",
"@types/amplitude-js": "8.16.5",
"@types/audioworklet": "0.0.29",
"@types/dom-screen-wake-lock": "1.0.1",
"@types/js-md5": "0.4.3",

View File

@@ -328,6 +328,22 @@ export function createNetworkInfoEvent({ isOnline, networkType, details }:
};
}
/**
* Creates a "not allowed error" event.
*
* @param {string} reason - The reason for the error.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createNotAllowedErrorEvent(reason: string) {
return {
action: 'not.allowed.error',
attributes: {
reason
}
};
}
/**
* Creates an "offer/answer failure" event.
*

View File

@@ -35,6 +35,12 @@ export default class AmplitudeHandler extends AbstractHandler {
this._enabled = false;
};
// Forces sending all events on exit (flushing) via sendBeacon
const onExitPage = () => {
// @ts-ignore
amplitude.getInstance().sendEvents();
};
if (navigator.product === 'ReactNative') {
amplitude.getInstance().init(amplitudeAPPKey);
fixDeviceID(amplitude.getInstance()).then(() => {
@@ -50,7 +56,8 @@ export default class AmplitudeHandler extends AbstractHandler {
includeReferrer: true,
includeUtm,
saveParamsReferrerOncePerSession: false,
onError
onError,
onExitPage
};
// @ts-ignore

View File

@@ -51,5 +51,6 @@ import '../video-layout/middleware';
import '../video-quality/middleware';
import '../videosipgw/middleware';
import '../visitors/middleware';
import '../whiteboard/middleware.any';
import './middleware';

View File

@@ -13,5 +13,6 @@ import '../mobile/react-native-sdk/middleware';
import '../mobile/watchos/middleware';
import '../share-room/middleware';
import '../shared-video/middleware';
import '../whiteboard/middleware.native';
import './middlewares.any';

View File

@@ -22,6 +22,6 @@ import '../talk-while-muted/middleware';
import '../toolbox/middleware';
import '../face-landmarks/middleware';
import '../gifs/middleware';
import '../whiteboard/middleware';
import '../whiteboard/middleware.web';
import './middlewares.any';

View File

@@ -55,3 +55,4 @@ import '../video-layout/reducer';
import '../video-quality/reducer';
import '../videosipgw/reducer';
import '../visitors/reducer';
import '../whiteboard/reducer';

View File

@@ -16,7 +16,6 @@ import '../noise-suppression/reducer';
import '../screenshot-capture/reducer';
import '../talk-while-muted/reducer';
import '../virtual-background/reducer';
import '../whiteboard/reducer';
import '../web-hid/reducer';
import './reducers.any';

View File

@@ -178,6 +178,18 @@ export const DATA_CHANNEL_OPENED = 'DATA_CHANNEL_OPENED';
*/
export const DATA_CHANNEL_CLOSED = 'DATA_CHANNEL_CLOSED';
/**
* The type of (redux) action which indicates that an endpoint message
* sent by another participant to the data channel is received.
*
* {
* type: ENDPOINT_MESSAGE_RECEIVED,
* participant: Object,
* data: Object
* }
*/
export const ENDPOINT_MESSAGE_RECEIVED = 'ENDPOINT_MESSAGE_RECEIVED';
/**
* The type of action which signals that the user has been kicked out from
* the conference.
@@ -333,3 +345,13 @@ export const SET_START_MUTED_POLICY = 'SET_START_MUTED_POLICY';
* }
*/
export const SET_ASSUMED_BANDWIDTH_BPS = 'SET_ASSUMED_BANDWIDTH_BPS';
/**
* The type of (redux) action which updated the conference metadata.
*
* {
* type: UPDATE_CONFERENCE_METADATA,
* metadata: Object
* }
*/
export const UPDATE_CONFERENCE_METADATA = 'UPDATE_CONFERENCE_METADATA';

View File

@@ -1,7 +1,6 @@
import { createStartMutedConfigurationEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IReduxState, IStore } from '../../app/types';
import { endpointMessageReceived } from '../../subtitles/actions.any';
import { setIAmVisitor } from '../../visitors/actions';
import { iAmVisitor } from '../../visitors/functions';
import { overwriteConfig } from '../config/actions';
@@ -48,6 +47,7 @@ import {
DATA_CHANNEL_CLOSED,
DATA_CHANNEL_OPENED,
E2E_RTT_CHANGED,
ENDPOINT_MESSAGE_RECEIVED,
KICKED_OUT,
LOCK_STATE_CHANGED,
NON_PARTICIPANT_MESSAGE_RECEIVED,
@@ -61,7 +61,8 @@ import {
SET_PENDING_SUBJECT_CHANGE,
SET_ROOM,
SET_START_MUTED_POLICY,
SET_START_REACTIONS_MUTED
SET_START_REACTIONS_MUTED,
UPDATE_CONFERENCE_METADATA
} from './actionTypes';
import {
AVATAR_URL_COMMAND,
@@ -79,7 +80,7 @@ import {
sendLocalParticipant
} from './functions';
import logger from './logger';
import { IJitsiConference } from './reducer';
import { IConferenceMetadata, IJitsiConference } from './reducer';
/**
* Adds conference (event) listeners.
@@ -275,6 +276,21 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[
})));
}
/**
* Action for updating the conference metadata.
*
* @param {IConferenceMetadata} metadata - The metadata object.
* @returns {{
* type: UPDATE_CONFERENCE_METADATA,
* metadata: IConferenceMetadata
* }}
*/
export function updateConferenceMetadata(metadata: IConferenceMetadata | null) {
return {
type: UPDATE_CONFERENCE_METADATA,
metadata
};
}
/**
* Create an action for when the end-to-end RTT against a specific remote participant has changed.
@@ -630,6 +646,25 @@ export function dataChannelClosed(code: number, reason: string) {
};
}
/**
* Signals that a participant sent an endpoint message on the data channel.
*
* @param {Object} participant - The participant details sending the message.
* @param {Object} data - The data carried by the endpoint message.
* @returns {{
* type: ENDPOINT_MESSAGE_RECEIVED,
* participant: Object,
* data: Object
* }}
*/
export function endpointMessageReceived(participant: Object, data: Object) {
return {
type: ENDPOINT_MESSAGE_RECEIVED,
participant,
data
};
}
/**
* Action to end a conference for all participants.
*

View File

@@ -6,6 +6,7 @@ import { MIN_ASSUMED_BANDWIDTH_BPS } from '../../../../modules/API/constants';
import {
ACTION_PINNED,
ACTION_UNPINNED,
createNotAllowedErrorEvent,
createOfferAnswerFailedEvent,
createPinnedEvent
} from '../../analytics/AnalyticsEvents';
@@ -25,7 +26,7 @@ import { overwriteConfig } from '../config/actions';
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection/actionTypes';
import { connect, connectionDisconnected, disconnect } from '../connection/actions';
import { validateJwt } from '../jwt/functions';
import { JitsiConferenceErrors, JitsiConnectionErrors } from '../lib-jitsi-meet';
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
import { PARTICIPANT_UPDATED, PIN_PARTICIPANT } from '../participants/actionTypes';
import { PARTICIPANT_ROLE } from '../participants/constants';
import {
@@ -34,6 +35,7 @@ import {
getPinnedParticipant
} from '../participants/functions';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import StateListenerRegistry from '../redux/StateListenerRegistry';
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks/actionTypes';
import { getLocalTracks } from '../tracks/functions.any';
@@ -54,7 +56,8 @@ import {
conferenceWillLeave,
createConference,
setLocalSubject,
setSubject
setSubject,
updateConferenceMetadata
} from './actions';
import { CONFERENCE_LEAVE_REASONS } from './constants';
import {
@@ -65,6 +68,7 @@ import {
restoreConferenceOptions
} from './functions';
import logger from './logger';
import { IConferenceMetadata } from './reducer';
/**
* Handler for before unload event.
@@ -124,6 +128,24 @@ MiddlewareRegistry.register(store => next => action => {
return next(action);
});
/**
* Set up state change listener to perform maintenance tasks when the conference
* is left or failed.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, { dispatch }, previousConference): void => {
if (conference && !previousConference) {
conference.on(JitsiConferenceEvents.METADATA_UPDATED, (metadata: IConferenceMetadata) => {
dispatch(updateConferenceMetadata(metadata));
});
}
if (conference !== previousConference) {
dispatch(updateConferenceMetadata(null));
}
});
/**
* Makes sure to leave a failed conference in order to release any allocated
* resources like peer connections, emit participant left events, etc.
@@ -197,6 +219,12 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
break;
}
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
const [ msg ] = error.params;
sendAnalytics(createNotAllowedErrorEvent(msg));
break;
}
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
sendAnalytics(createOfferAnswerFailedEvent());
break;

View File

@@ -27,7 +27,8 @@ import {
SET_PENDING_SUBJECT_CHANGE,
SET_ROOM,
SET_START_MUTED_POLICY,
SET_START_REACTIONS_MUTED
SET_START_REACTIONS_MUTED,
UPDATE_CONFERENCE_METADATA
} from './actionTypes';
import { isRoomValid } from './functions';
@@ -39,10 +40,23 @@ const DEFAULT_STATE = {
leaving: undefined,
locked: undefined,
membersOnly: undefined,
metadata: undefined,
password: undefined,
passwordRequired: undefined
};
export interface IConferenceMetadata {
recording?: {
isTranscribingEnabled: boolean;
};
whiteboard?: {
collabDetails: {
roomId: string;
roomKey: string;
};
};
}
export interface IJitsiConference {
addCommandListener: Function;
addLobbyMessageListener: Function;
@@ -141,6 +155,7 @@ export interface IConferenceState {
localSubject?: string;
locked?: string;
membersOnly?: IJitsiConference;
metadata?: IConferenceMetadata;
obfuscatedRoom?: string;
obfuscatedRoomSource?: string;
p2p?: Object;
@@ -247,6 +262,12 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
startAudioMutedPolicy: action.startAudioMutedPolicy,
startVideoMutedPolicy: action.startVideoMutedPolicy
};
case UPDATE_CONFERENCE_METADATA:
return {
...state,
metadata: action.metadata
};
}
return state;

View File

@@ -131,19 +131,30 @@ export interface IMobileDynamicLink {
export interface IDeeplinkingPlatformConfig {
appName: string;
appScheme: string;
}
export interface IDeeplinkingMobileConfig extends IDeeplinkingPlatformConfig {
appPackage?: string;
appScheme: string;
downloadLink: string;
dynamicLink?: IMobileDynamicLink;
fDroidUrl?: string;
}
export interface IDesktopDownloadConfig {
linux?: string;
macos?: string;
windows?: string;
}
export interface IDeeplinkingDesktopConfig extends IDeeplinkingPlatformConfig {
download?: IDesktopDownloadConfig;
enabled: boolean;
}
export interface IDeeplinkingConfig {
android?: IDeeplinkingMobileConfig;
desktop?: IDeeplinkingPlatformConfig;
desktop?: IDeeplinkingDesktopConfig;
disabled?: boolean;
hideLogo?: boolean;
ios?: IDeeplinkingMobileConfig;
@@ -519,6 +530,10 @@ export interface IConfig {
sharingEnabled?: boolean;
};
recordingSharingUrl?: string;
recordings?: {
showPrejoinWarning?: boolean;
suggestRecording?: boolean;
};
remoteVideoMenu?: {
disableGrantModerator?: boolean;
disableKick?: boolean;
@@ -559,6 +574,7 @@ export interface IConfig {
testing?: {
assumeBandwidth?: boolean;
disableE2EE?: boolean;
dumpTranscript?: boolean;
mobileXmppWsThreshold?: number;
noAutoPlayVideo?: boolean;
p2pTestMode?: boolean;
@@ -583,7 +599,6 @@ export interface IConfig {
transcribingEnabled?: boolean;
transcription?: {
autoTranscribeOnRecord?: boolean;
disableStartForAll?: boolean;
enabled?: boolean;
preferredLanguage?: string;
translationLanguages?: Array<string>;

View File

@@ -201,6 +201,7 @@ export default [
'remoteVideoMenu',
'roomPasswordNumberOfDigits',
'readOnlyName',
'recordings',
'replaceParticipant',
'resolution',
'salesforceUrl',

View File

@@ -5,8 +5,8 @@ import { NOTIFY_CLICK_MODE } from '../../toolbox/constants';
import {
IConfig,
IDeeplinkingConfig,
IDeeplinkingDesktopConfig,
IDeeplinkingMobileConfig,
IDeeplinkingPlatformConfig,
NotifyClickButton,
ToolbarButton
} from './configType';
@@ -94,13 +94,21 @@ export function areAudioLevelsEnabled(state: IReduxState): boolean {
* @returns {void}
*/
export function _setDeeplinkingDefaults(deeplinking: IDeeplinkingConfig) {
const {
desktop = {} as IDeeplinkingPlatformConfig,
android = {} as IDeeplinkingMobileConfig,
ios = {} as IDeeplinkingMobileConfig
} = deeplinking;
deeplinking.desktop = deeplinking.desktop || {} as IDeeplinkingDesktopConfig;
deeplinking.android = deeplinking.android || {} as IDeeplinkingMobileConfig;
deeplinking.ios = deeplinking.ios || {} as IDeeplinkingMobileConfig;
const { android, desktop, ios } = deeplinking;
desktop.appName = desktop.appName || 'Jitsi Meet';
desktop.appScheme = desktop.appScheme || 'jitsi-meet';
desktop.download = desktop.download || {};
desktop.download.windows = desktop.download.windows
|| 'https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.exe';
desktop.download.macos = desktop.download.macos
|| 'https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.dmg';
desktop.download.linux = desktop.download.linux
|| 'https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-x86_64.AppImage';
ios.appName = ios.appName || 'Jitsi Meet';
ios.appScheme = ios.appScheme || 'org.jitsi.meet';

View File

@@ -14,8 +14,8 @@ import {
import {
IConfig,
IDeeplinkingConfig,
IDeeplinkingDesktopConfig,
IDeeplinkingMobileConfig,
IDeeplinkingPlatformConfig,
IMobileDynamicLink,
ToolbarButton
} from './configType';
@@ -295,7 +295,7 @@ function _translateInterfaceConfig(oldValue: IConfig) {
} else {
const disabled = Boolean(oldValue.disableDeepLinking);
const deeplinking: IDeeplinkingConfig = {
desktop: {} as IDeeplinkingPlatformConfig,
desktop: {} as IDeeplinkingDesktopConfig,
hideLogo: false,
disabled,
android: {} as IDeeplinkingMobileConfig,

View File

@@ -90,6 +90,12 @@ export const HELP_BUTTON_ENABLED = 'help.enabled';
*/
export const INVITE_ENABLED = 'invite.enabled';
/**
* Flag indicating if dial-in invite functionality should be enabled.
* Default: enabled (true).
*/
export const INVITE_DIAL_IN_ENABLED = 'invite-dial-in.enabled';
/**
* Flag indicating if recording should be enabled in iOS.
* Default: disabled (false).

View File

@@ -683,7 +683,7 @@ function _participantJoinedOrUpdated(store: IStore, next: Function, action: AnyA
// Only run this if the config is populated, otherwise we preload external resources
// even if disableThirdPartyRequests is set to true in config
if (Object.keys(getState()['features/base/config']).length) {
if (getState()['features/base/config']?.hosts) {
const { disableThirdPartyRequests } = getState()['features/base/config'];
if (!disableThirdPartyRequests && (avatarURL || email || id || name)) {

View File

@@ -5,6 +5,7 @@ import { makeStyles } from 'tss-react/mui';
import { IReduxState } from '../../../../app/types';
import DeviceStatus from '../../../../prejoin/components/web/preview/DeviceStatus';
import { isRoomNameEnabled } from '../../../../prejoin/functions';
import Toolbox from '../../../../toolbox/components/web/Toolbox';
import { getConferenceName } from '../../../conference/functions';
import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from '../../../config/constants';
@@ -13,6 +14,7 @@ import { withPixelLineHeight } from '../../../styles/functions.web';
import ConnectionStatus from './ConnectionStatus';
import Preview from './Preview';
import RecordingWarning from './RecordingWarning';
import UnsafeRoomWarning from './UnsafeRoomWarning';
interface IProps {
@@ -57,6 +59,11 @@ interface IProps {
*/
showDeviceStatus: boolean;
/**
* Indicates whether to display the recording warning.
*/
showRecordingWarning?: boolean;
/**
* If should show unsafe room warning when joining.
*/
@@ -167,6 +174,7 @@ const PreMeetingScreen = ({
children,
className,
showDeviceStatus,
showRecordingWarning,
showUnsafeRoomWarning,
skipPrejoinButton,
title,
@@ -200,6 +208,7 @@ const PreMeetingScreen = ({
{skipPrejoinButton}
{showUnsafeRoomWarning && <UnsafeRoomWarning />}
{showDeviceStatus && <DeviceStatus />}
{showRecordingWarning && <RecordingWarning />}
</div>
</div>
</div>
@@ -219,7 +228,7 @@ const PreMeetingScreen = ({
* @returns {Object}
*/
function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
const { hiddenPremeetingButtons, hideConferenceSubject } = state['features/base/config'];
const { hiddenPremeetingButtons } = state['features/base/config'];
const toolbarButtons = getToolbarButtons(state);
const premeetingButtons = (ownProps.thirdParty
? THIRD_PARTY_PREJOIN_BUTTONS
@@ -237,7 +246,7 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
? premeetingButtons
: premeetingButtons.filter(b => isToolbarButtonEnabled(b, toolbarButtons)),
_premeetingBackground: premeetingBackground,
_roomName: (hideConferenceSubject ? undefined : getConferenceName(state)) ?? ''
_roomName: isRoomNameEnabled(state) ? getConferenceName(state) : ''
};
}

View File

@@ -0,0 +1,40 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { makeStyles } from 'tss-react/mui';
import { withPixelLineHeight } from '../../../styles/functions.web';
const useStyles = makeStyles()(theme => {
return {
warning: {
bottom: 0,
color: theme.palette.text03,
display: 'flex',
justifyContent: 'center',
...withPixelLineHeight(theme.typography.bodyShortRegular),
marginBottom: theme.spacing(3),
marginTop: theme.spacing(2),
paddingLeft: theme.spacing(3),
paddingRight: theme.spacing(3),
position: 'absolute',
width: '100%',
'@media (max-width: 720px)': {
position: 'relative'
}
}
};
});
const RecordingWarning = () => {
const { t } = useTranslation();
const { classes } = useStyles();
return (
<div className = { classes.warning }>
{t('prejoin.recordingWarning')}
</div>
);
};
export default RecordingWarning;

View File

@@ -10,6 +10,8 @@ if (userAgent.match(/Android/i)) {
OS = 'macos';
} else if (userAgent.match(/Windows/i)) {
OS = 'windows';
} else if (userAgent.match(/Linux/i)) {
OS = 'linux';
}
/**

View File

@@ -1,3 +1,5 @@
import { NativeModules, Platform } from 'react-native';
import { IReduxState, IStore } from '../../app/types';
import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
import { showNotification } from '../../notifications/actions';
@@ -12,6 +14,8 @@ import { VIDEO_MUTISM_AUTHORITY } from '../media/constants';
import { addLocalTrack, replaceLocalTrack } from './actions.any';
import { getLocalDesktopTrack, getTrackState, isLocalVideoTrackDesktop } from './functions.native';
const { JitsiMeetMediaProjectionModule } = NativeModules;
export * from './actions.any';
/**
@@ -29,7 +33,10 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
if (enabled) {
const isSharing = isLocalVideoTrackDesktop(state);
if (!isSharing) {
if (isSharing) {
Platform.OS === 'android' && JitsiMeetMediaProjectionModule.abort();
} else {
Platform.OS === 'android' && JitsiMeetMediaProjectionModule.launch();
_startScreenSharing(dispatch, state);
}
} else {
@@ -77,7 +84,7 @@ async function _startScreenSharing(dispatch: IStore['dispatch'], state: IReduxSt
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
}
} catch (error: any) {
console.log('ERROR creating ScreeSharing stream ', error);
console.log('ERROR creating screen-sharing stream ', error);
setPictureInPictureEnabled(true);
}

View File

@@ -1,3 +1,5 @@
import base64js from 'base64-js';
import { timeoutPromise } from './timeoutPromise';
/**
@@ -43,3 +45,37 @@ export function doGetJSON(url: string, retry?: boolean, options?: Object) {
return fetchPromise;
}
/**
* Encodes strings to Base64URL.
*
* @param {any} data - The byte array to encode.
* @returns {string}
*/
export const encodeToBase64URL = (data: string): string => base64js
.fromByteArray(new window.TextEncoder().encode(data))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
/**
* Decodes strings from Base64URL.
*
* @param {string} data - The byte array to decode.
* @returns {string}
*/
export const decodeFromBase64URL = (data: string): string => {
let s = data;
// Convert from Base64URL to Base64.
if (s.length % 4 === 2) {
s += '==';
} else if (s.length % 4 === 3) {
s += '=';
}
s = s.replace(/-/g, '+').replace(/_/g, '/');
// Convert Base64 to a byte array.
return new window.TextDecoder().decode(base64js.toByteArray(s));
};

View File

@@ -6,6 +6,7 @@ import { getFeatureFlag } from '../../../base/flags/functions';
import { translate } from '../../../base/i18n/functions';
import { IconChatUnread, IconMessage } from '../../../base/icons/svg';
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import { arePollsDisabled } from '../../../conference/functions.any';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
import { getUnreadPollCount } from '../../../polls/functions';
@@ -65,11 +66,10 @@ class ChatButton extends AbstractButton<IProps> {
*/
function _mapStateToProps(state: IReduxState, ownProps: any) {
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
const { disablePolls } = state['features/base/config'];
const { visible = enabled } = ownProps;
return {
_isPollsDisabled: disablePolls,
_isPollsDisabled: arePollsDisabled(state),
// The toggled icon should also be available for new polls
_unreadMessageCount: getUnreadCount(state) || getUnreadPollCount(state),

View File

@@ -8,6 +8,7 @@ import { IconMessage, IconReply } from '../../../base/icons/svg';
import { getParticipantById } from '../../../base/participants/functions';
import { IParticipant } from '../../../base/participants/types';
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import { arePollsDisabled } from '../../../conference/functions.any';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
import { handleLobbyChatInitialized, openChat } from '../../actions.native';
@@ -96,11 +97,10 @@ class PrivateMessageButton extends AbstractButton<IProps, any> {
*/
export function _mapStateToProps(state: IReduxState, ownProps: any) {
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
const { disablePolls } = state['features/base/config'];
const { visible = enabled, isLobbyMessage, participantID } = ownProps;
return {
_isPollsDisabled: disablePolls,
_isPollsDisabled: arePollsDisabled(state),
_participant: getParticipantById(state, participantID),
_isLobbyMessage: isLobbyMessage,
visible

View File

@@ -7,6 +7,7 @@ import { translate } from '../../../base/i18n/functions';
import { getLocalParticipant } from '../../../base/participants/functions';
import { withPixelLineHeight } from '../../../base/styles/functions.web';
import Tabs from '../../../base/ui/components/web/Tabs';
import { arePollsDisabled } from '../../../conference/functions.any';
import PollsPane from '../../../polls/components/web/PollsPane';
import { sendMessage, setIsPollsTabFocused, toggleChat } from '../../actions.web';
import { CHAT_SIZE, CHAT_TABS, SMALL_WIDTH_THRESHOLD } from '../../constants';
@@ -316,12 +317,11 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
const { isOpen, isPollsTabFocused, messages, nbUnreadMessages } = state['features/chat'];
const { nbUnreadPolls } = state['features/polls'];
const _localParticipant = getLocalParticipant(state);
const { disablePolls } = state['features/base/config'];
return {
_isModal: window.innerWidth <= SMALL_WIDTH_THRESHOLD,
_isOpen: isOpen,
_isPollsEnabled: !disablePolls,
_isPollsEnabled: !arePollsDisabled(state),
_isPollsTabFocused: isPollsTabFocused,
_messages: messages,
_nbUnreadMessages: nbUnreadMessages,

View File

@@ -2,7 +2,7 @@ import { AnyAction } from 'redux';
import { IReduxState, IStore } from '../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
import { CONFERENCE_JOINED, ENDPOINT_MESSAGE_RECEIVED } from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import { IJitsiConference } from '../base/conference/reducer';
import { openDialog } from '../base/dialog/actions';
@@ -29,7 +29,6 @@ import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
import { pushReactions } from '../reactions/actions.any';
import { ENDPOINT_REACTION_NAME } from '../reactions/constants';
import { getReactionMessageFromBuffer, isReactionsEnabled } from '../reactions/functions.any';
import { endpointMessageReceived } from '../subtitles/actions.any';
import { showToolbox } from '../toolbox/actions';
@@ -93,14 +92,6 @@ MiddlewareRegistry.register(store => next => action => {
_addChatMsgListener(action.conference, store);
break;
case OPEN_CHAT:
unreadCount = 0;
if (typeof APP !== 'undefined') {
APP.API.notifyChatUpdated(unreadCount, true);
}
break;
case CLOSE_CHAT: {
const isPollTabOpen = getState()['features/chat'].isPollsTabFocused;
@@ -116,6 +107,38 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case ENDPOINT_MESSAGE_RECEIVED: {
const state = store.getState();
if (!isReactionsEnabled(state)) {
return;
}
const { participant, data } = action;
if (data?.name === ENDPOINT_REACTION_NAME) {
store.dispatch(pushReactions(data.reactions));
_handleReceivedMessage(store, {
id: participant.getId(),
message: getReactionMessageFromBuffer(data.reactions),
privateMessage: false,
lobbyChat: false,
timestamp: data.timestamp
}, false, true);
}
break;
}
case OPEN_CHAT:
unreadCount = 0;
if (typeof APP !== 'undefined') {
APP.API.notifyChatUpdated(unreadCount, true);
}
break;
case SET_IS_POLL_TAB_FOCUSED: {
dispatch(resetNbUnreadPollsMessages());
break;
@@ -253,35 +276,6 @@ function _addChatMsgListener(conference: IJitsiConference, store: IStore) {
}
);
conference.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args: any) => {
const state = store.getState();
if (!isReactionsEnabled(state)) {
return;
}
// @ts-ignore
store.dispatch(endpointMessageReceived(...args));
if (args && args.length >= 2) {
const [ { _id }, eventData ] = args;
if (eventData.name === ENDPOINT_REACTION_NAME) {
store.dispatch(pushReactions(eventData.reactions));
_handleReceivedMessage(store, {
id: _id,
message: getReactionMessageFromBuffer(eventData.reactions),
privateMessage: false,
lobbyChat: false,
timestamp: eventData.timestamp
}, false, true);
}
}
});
conference.on(
JitsiConferenceEvents.CONFERENCE_ERROR, (errorType: string, error: Error) => {
errorType === JitsiConferenceErrors.CHAT_ERROR && _handleChatError(store, error);

View File

@@ -1,11 +1,13 @@
import React, { useCallback } from 'react';
import { TouchableOpacity } from 'react-native';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { openHighlightDialog } from '../../../recording/actions.native';
import HighlightButton from '../../../recording/components/Recording/native/HighlightButton';
import RecordingLabel from '../../../recording/components/native/RecordingLabel';
import { getActiveSession } from '../../../recording/functions';
import VisitorsCountLabel from '../../../visitors/components/native/VisitorsCountLabel';
import RaisedHandsCountLabel from './RaisedHandsCountLabel';
@@ -28,7 +30,10 @@ interface IProps {
const AlwaysOnLabels = ({ createOnPress }: IProps) => {
const dispatch = useDispatch();
const openHighlightDialogCallback = useCallback(() => dispatch(openHighlightDialog()), [ dispatch ]);
const isStreaming = useSelector((state: IReduxState) =>
Boolean(getActiveSession(state, JitsiRecordingConstants.mode.STREAM)));
const openHighlightDialogCallback = useCallback(() =>
dispatch(openHighlightDialog()), [ dispatch ]);
return (<>
<TouchableOpacity
@@ -36,11 +41,14 @@ const AlwaysOnLabels = ({ createOnPress }: IProps) => {
onPress = { createOnPress(LABEL_ID_RECORDING) } >
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
</TouchableOpacity>
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { createOnPress(LABEL_ID_STREAMING) } >
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
</TouchableOpacity>
{
isStreaming
&& <TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { createOnPress(LABEL_ID_STREAMING) } >
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
</TouchableOpacity>
}
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { openHighlightDialogCallback }>

View File

@@ -17,7 +17,6 @@ import { IReduxState, IStore } from '../../../app/types';
import { CONFERENCE_BLURRED, CONFERENCE_FOCUSED } from '../../../base/conference/actionTypes';
import { FULLSCREEN_ENABLED, PIP_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { getParticipantCount } from '../../../base/participants/functions';
import Container from '../../../base/react/components/native/Container';
import LoadingIndicator from '../../../base/react/components/native/LoadingIndicator';
import TintedView from '../../../base/react/components/native/TintedView';
@@ -101,11 +100,6 @@ interface IProps extends AbstractProps {
*/
_fullscreenEnabled: boolean;
/**
* The indicator which determines if the conference type is one to one.
*/
_isOneToOneConference: boolean;
/**
* The indicator which determines if the participants pane is open.
*/
@@ -370,7 +364,6 @@ class Conference extends AbstractConference<IProps, State> {
_aspectRatio,
_connecting,
_filmstripVisible,
_isOneToOneConference,
_largeVideoParticipantId,
_reducedUI,
_shouldDisplayTileView,
@@ -426,13 +419,11 @@ class Conference extends AbstractConference<IProps, State> {
<Captions onPress = { this._onClick } />
{
_shouldDisplayTileView || (
!_isOneToOneConference
&& <Container style = { styles.displayNameContainer }>
<DisplayNameLabel
participantId = { _largeVideoParticipantId } />
</Container>
)
_shouldDisplayTileView
|| <Container style = { styles.displayNameContainer }>
<DisplayNameLabel
participantId = { _largeVideoParticipantId } />
</Container>
}
{ !_shouldDisplayTileView && <LonelyMeetingExperience /> }
@@ -573,7 +564,6 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
const { backgroundColor } = state['features/dynamic-branding'];
const { startCarMode } = state['features/base/settings'];
const { enabled: audioOnlyEnabled } = state['features/base/audio-only'];
const participantCount = getParticipantCount(state);
const brandingStyles = backgroundColor ? {
backgroundColor
} : undefined;
@@ -587,7 +577,6 @@ function _mapStateToProps(state: IReduxState, _ownProps: any) {
_connecting: isConnecting(state),
_filmstripVisible: isFilmstripVisible(state),
_fullscreenEnabled: getFeatureFlag(state, FULLSCREEN_ENABLED, true),
_isOneToOneConference: Boolean(participantCount === 2),
_isParticipantsPaneOpen: isOpen,
_largeVideoParticipantId: state['features/large-video'].participantId,
_pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED),

View File

@@ -1,11 +1,10 @@
import React, { Component } from 'react';
import { TouchableOpacity, View, ViewStyle } from 'react-native';
import TranscribingLabel from '../../../transcribing/components/TranscribingLabel.native';
import VideoQualityLabel from '../../../video-quality/components/VideoQualityLabel.native';
import InsecureRoomNameLabel from './InsecureRoomNameLabel';
import { LABEL_ID_INSECURE_ROOM_NAME, LABEL_ID_QUALITY, LABEL_ID_TRANSCRIBING, LabelHitSlop } from './constants';
import { LABEL_ID_INSECURE_ROOM_NAME, LABEL_ID_QUALITY, LabelHitSlop } from './constants';
import styles from './styles';
interface IProps {
@@ -33,13 +32,6 @@ class Labels extends Component<IProps> {
<View
pointerEvents = 'box-none'
style = { styles.indicatorContainer as ViewStyle }>
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = {
this.props.createOnPress(LABEL_ID_TRANSCRIBING)
} >
<TranscribingLabel />
</TouchableOpacity>
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = {

View File

@@ -4,11 +4,12 @@ import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { getConferenceName, getConferenceTimestamp } from '../../../base/conference/functions';
import { CONFERENCE_TIMER_ENABLED, MEETING_NAME_ENABLED } from '../../../base/flags/constants';
import { CONFERENCE_TIMER_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import AudioDeviceToggleButton from '../../../mobile/audio-mode/components/AudioDeviceToggleButton';
import PictureInPictureButton from '../../../mobile/picture-in-picture/components/PictureInPictureButton';
import ParticipantsPaneButton from '../../../participants-pane/components/native/ParticipantsPaneButton';
import { isRoomNameEnabled } from '../../../prejoin/functions';
import ToggleCameraButton from '../../../toolbox/components/native/ToggleCameraButton';
import { isToolboxVisible } from '../../../toolbox/functions.native';
import ConferenceTimer from '../ConferenceTimer';
@@ -16,6 +17,7 @@ import ConferenceTimer from '../ConferenceTimer';
import Labels from './Labels';
import styles from './styles';
interface IProps {
/**
@@ -35,9 +37,9 @@ interface IProps {
_meetingName: string;
/**
* Whether displaying the current meeting name is enabled or not.
* Whether displaying the current room name is enabled or not.
*/
_meetingNameEnabled: boolean;
_roomNameEnabled: boolean;
/**
* True if the navigation bar should be visible.
@@ -75,7 +77,7 @@ const TitleBar = (props: IProps) => {
</View>
}
{
props._meetingNameEnabled
props._roomNameEnabled
&& <View style = { styles.roomNameView as ViewStyle }>
<Text
numberOfLines = { 1 }
@@ -107,15 +109,14 @@ const TitleBar = (props: IProps) => {
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState) {
const { hideConferenceTimer, hideConferenceSubject } = state['features/base/config'];
const { hideConferenceTimer } = state['features/base/config'];
const startTimestamp = getConferenceTimestamp(state);
return {
_conferenceTimerEnabled:
Boolean(getFeatureFlag(state, CONFERENCE_TIMER_ENABLED, true) && !hideConferenceTimer && startTimestamp),
_meetingName: getConferenceName(state),
_meetingNameEnabled:
getFeatureFlag(state, MEETING_NAME_ENABLED, true) && !hideConferenceSubject,
_roomNameEnabled: isRoomNameEnabled(state),
_visible: isToolboxVisible(state)
};
}

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import RecordingExpandedLabel from '../../../recording/components/native/RecordingExpandedLabel';
import TranscribingExpandedLabel from '../../../transcribing/components/TranscribingExpandedLabel.native';
import VideoQualityExpandedLabel from '../../../video-quality/components/VideoQualityExpandedLabel.native';
import InsecureRoomNameExpandedLabel from './InsecureRoomNameExpandedLabel';
@@ -23,7 +22,6 @@ export const EXPANDED_LABEL_TIMEOUT = 5000;
export const LABEL_ID_QUALITY = 'quality';
export const LABEL_ID_RECORDING = 'recording';
export const LABEL_ID_STREAMING = 'streaming';
export const LABEL_ID_TRANSCRIBING = 'transcribing';
export const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
export const LABEL_ID_RAISED_HANDS_COUNT = 'raised-hands-count';
export const LABEL_ID_VISITORS_COUNT = 'visitors-count';
@@ -58,9 +56,6 @@ export const EXPANDED_LABELS: {
},
alwaysOn: true
},
[LABEL_ID_TRANSCRIBING]: {
component: TranscribingExpandedLabel
},
[LABEL_ID_INSECURE_ROOM_NAME]: {
component: InsecureRoomNameExpandedLabel
},

View File

@@ -23,6 +23,7 @@ import { getOverlayToRender } from '../../../overlay/functions.web';
import ParticipantsPane from '../../../participants-pane/components/web/ParticipantsPane';
import Prejoin from '../../../prejoin/components/web/Prejoin';
import { isPrejoinPageVisible } from '../../../prejoin/functions';
import ReactionAnimations from '../../../reactions/components/web/ReactionsAnimations';
import { toggleToolboxVisible } from '../../../toolbox/actions.any';
import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
@@ -260,6 +261,7 @@ class Conference extends AbstractConference<IProps, any> {
{ _showLobby && <LobbyScreen />}
</div>
<ParticipantsPane />
<ReactionAnimations />
</div>
);
}

View File

@@ -9,7 +9,6 @@ import HighlightButton from '../../../recording/components/Recording/web/Highlig
import RecordingLabel from '../../../recording/components/web/RecordingLabel';
import { showToolbox } from '../../../toolbox/actions.web';
import { isToolboxVisible } from '../../../toolbox/functions.web';
import TranscribingLabel from '../../../transcribing/components/TranscribingLabel.web';
import VideoQualityLabel from '../../../video-quality/components/VideoQualityLabel.web';
import VisitorsCountLabel from '../../../visitors/components/web/VisitorsCountLabel';
import ConferenceTimer from '../ConferenceTimer';
@@ -83,10 +82,6 @@ const COMPONENTS: Array<{
Component: RaisedHandsCountLabel,
id: 'raised-hands-count'
},
{
Component: TranscribingLabel,
id: 'transcribing'
},
{
Component: VideoQualityLabel,
id: 'video-quality'

View File

@@ -1,5 +1,6 @@
import { IStateful } from '../base/app/types';
import { toState } from '../base/redux/functions';
import { iAmVisitor } from '../visitors/functions';
/**
@@ -15,3 +16,19 @@ export function shouldDisplayNotifications(stateful: IStateful) {
return !calleeInfoVisible;
}
/**
*
* Returns true if polls feature is disabled.
*
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {boolean}.
*/
export function arePollsDisabled(stateful: IStateful) {
const state = toState(stateful);
return state['features/base/config']?.disablePolls || iAmVisitor(state);
}

View File

@@ -1,21 +1 @@
import { IStateful } from '../base/app/types';
import { toState } from '../base/redux/functions';
export * from './functions.any';
/**
*
* Returns true if polls feature is disabled.
*
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {boolean}.
*/
export function getDisablePolls(stateful: IStateful) {
const state = toState(stateful)['features/base/config'];
return state.disablePolls;
}

View File

@@ -35,6 +35,7 @@ import {
NOTIFICATION_ICON,
NOTIFICATION_TIMEOUT_TYPE
} from '../notifications/constants';
import { showStartRecordingNotification } from '../recording/actions';
import { showSalesforceNotification } from '../salesforce/actions';
import { setToolboxEnabled } from '../toolbox/actions.any';
@@ -149,6 +150,8 @@ function _conferenceJoined({ dispatch, getState }: IStore) {
}
dispatch(showSalesforceNotification());
dispatch(showStartRecordingNotification());
_checkIframe(getState(), dispatch);
}

View File

@@ -1,10 +1,7 @@
import i18next from 'i18next';
import { CONFERENCE_JOINED, KICKED_OUT } from '../base/conference/actionTypes';
import { IJitsiConference } from '../base/conference/reducer';
import { ENDPOINT_MESSAGE_RECEIVED, KICKED_OUT } from '../base/conference/actionTypes';
import { hangup } from '../base/connection/actions.web';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { getParticipantDisplayName } from '../base/participants/functions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { openAllowToggleCameraDialog, setCameraFacingMode } from '../base/tracks/actions.web';
@@ -14,8 +11,15 @@ import './middleware.any';
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONFERENCE_JOINED: {
_addSetCameraFacingModeListener(action.conference);
case ENDPOINT_MESSAGE_RECEIVED: {
const { participant, data } = action;
if (data?.name === CAMERA_FACING_MODE_MESSAGE) {
APP.store.dispatch(openAllowToggleCameraDialog(
/* onAllow */ () => APP.store.dispatch(setCameraFacingMode(data.facingMode)),
/* initiatorId */ participant.getId()
));
}
break;
}
@@ -34,28 +38,3 @@ MiddlewareRegistry.register(store => next => action => {
return next(action);
});
/**
* Registers listener for {@link JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED} that
* will perform various chat related activities.
*
* @param {IJitsiConference} conference - The conference.
* @returns {void}
*/
function _addSetCameraFacingModeListener(conference: IJitsiConference) {
conference.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args: any) => {
if (args && args.length >= 2) {
const [ sender, eventData ] = args;
if (eventData.name === CAMERA_FACING_MODE_MESSAGE) {
APP.store.dispatch(openAllowToggleCameraDialog(
/* onAllow */ () => APP.store.dispatch(setCameraFacingMode(eventData.facingMode)),
/* initiatorId */ sender._id
));
}
}
}
);
}

View File

@@ -11,6 +11,7 @@ import { IDeeplinkingConfig } from '../../base/config/configType';
import { getLegalUrls } from '../../base/config/functions.any';
import { isSupportedBrowser } from '../../base/environment/environment';
import { translate, translateToHTML } from '../../base/i18n/functions';
import Platform from '../../base/react/Platform.web';
import { withPixelLineHeight } from '../../base/styles/functions.web';
import Button from '../../base/ui/components/web/Button';
import { BUTTON_TYPES } from '../../base/ui/constants.any';
@@ -85,6 +86,14 @@ const DeepLinkingDesktopPage: React.FC<WithTranslation> = ({ t }) => {
const deeplinkingCfg = useSelector((state: IReduxState) =>
state['features/base/config']?.deeplinking || {} as IDeeplinkingConfig);
const generateDownloadURL = useCallback(() => {
const downloadCfg = deeplinkingCfg.desktop?.download;
if (downloadCfg) {
return downloadCfg[Platform.OS as keyof typeof downloadCfg];
}
}, [ deeplinkingCfg ]);
const legalUrls = useSelector(getLegalUrls);
const { hideLogo, desktop } = deeplinkingCfg;
@@ -134,6 +143,16 @@ const DeepLinkingDesktopPage: React.FC<WithTranslation> = ({ t }) => {
: t(`${_TNS}.descriptionWithoutWeb`, { app: desktop?.appName })
}
</div>
<div className = { styles.descriptionLabel }>
{
t(`${_TNS}.noDesktopApp`)
} &nbsp;
<a href = { generateDownloadURL() }>
{
t(`${_TNS}.downloadApp`)
}
</a>
</div>
<div className = { styles.buttonsContainer }>
<Button
label = { t(`${_TNS}.tryAgainButton`) }

View File

@@ -211,7 +211,7 @@ const DeepLinkingMobilePage: React.FC<WithTranslation> = ({ t }) => {
</a>
{isSupportedMobileBrowser() ? (
<div className = { styles.supportedBrowserContent }>
<div className = { styles.labelOr }>OR</div>
<div className = { styles.labelOr }>{ t(`${_TNS}.or`) }</div>
<a
className = { styles.linkWrapper }
onClick = { onLaunchWeb }

View File

@@ -1,5 +1,6 @@
import { IReduxState } from '../app/types';
import { isMobileBrowser } from '../base/environment/utils';
import { browser } from '../base/lib-jitsi-meet';
import Platform from '../base/react/Platform';
import { URI_PROTOCOL_PATTERN } from '../base/util/uri';
import { isVpaasMeeting } from '../jaas/functions';
@@ -63,6 +64,7 @@ export function getDeepLinkingPage(state: IReduxState) {
if (launchInWeb
|| !room
|| state['features/base/config'].deeplinking?.disabled
|| browser.isElectron()
|| (isVpaasMeeting(state) && (!appScheme || appScheme === 'com.8x8.meet'))) {
return Promise.resolve();
}

View File

@@ -1,3 +1,6 @@
import { IReduxState } from '../app/types';
import { URI_PROTOCOL_PATTERN } from '../base/util/uri';
/**
* Opens the desktop app.
*
@@ -6,5 +9,17 @@
* with false otherwise.
*/
export function _openDesktopApp(_state: Object) {
const state = _state as IReduxState;
const deeplinkingDesktop = state['features/base/config'].deeplinking?.desktop;
if (deeplinkingDesktop?.enabled) {
const { appScheme } = deeplinkingDesktop;
const regex = new RegExp(URI_PROTOCOL_PATTERN, 'gi');
window.location.href = window.location.href.replace(regex, `${appScheme}:`);
return Promise.resolve(true);
}
return Promise.resolve(false);
}

View File

@@ -56,6 +56,7 @@ class SharedDocument extends PureComponent<IProps> {
style = { styles.sharedDocContainer }>
<WebView
hideKeyboardAccessoryView = { true }
incognito = { true }
renderLoading = { this._renderLoading }
source = {{ uri: _documentUrl ?? '' }}
startInLoadingState = { true }

View File

@@ -3,12 +3,11 @@ import { AnyAction } from 'redux';
import { IStore } from '../app/types';
import {
CONFERENCE_JOINED,
CONFERENCE_WILL_LEAVE
CONFERENCE_WILL_LEAVE,
ENDPOINT_MESSAGE_RECEIVED
} from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { getLocalParticipant, getParticipantCount } from '../base/participants/functions';
import { IParticipant } from '../base/participants/types';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { TRACK_ADDED, TRACK_REMOVED, TRACK_UPDATED } from '../base/tracks/actionTypes';
@@ -28,27 +27,22 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
FaceLandmarksDetector.init(store);
}
// allow using remote face centering data when local face centering is not enabled
action.conference.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(participant: IParticipant | undefined, eventData: any) => {
if (!participant || !eventData || !participant.getId) {
return;
}
return next(action);
} else if (action.type === ENDPOINT_MESSAGE_RECEIVED) {
// Allow using remote face centering data when local face centering is not enabled.
const { participant, data } = action;
if (eventData.type === FACE_BOX_EVENT_TYPE) {
dispatch({
type: UPDATE_FACE_COORDINATES,
faceBox: eventData.faceBox,
id: participant.getId()
});
}
if (data?.type === FACE_BOX_EVENT_TYPE) {
dispatch({
type: UPDATE_FACE_COORDINATES,
faceBox: data.faceBox,
id: participant.getId()
});
}
return next(action);
}
if (!isEnabled) {
return next(action);
}
@@ -57,7 +51,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
case CONFERENCE_WILL_LEAVE : {
FaceLandmarksDetector.stopDetection(store);
return next(action);
break;
}
case TRACK_ADDED: {
const { jitsiTrack: { isLocal, videoType }, muted } = action.track;
@@ -67,18 +61,18 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
FaceLandmarksDetector.startDetection(store, action.track);
}
return next(action);
break;
}
case TRACK_UPDATED: {
const { jitsiTrack: { isLocal, videoType } } = action.track;
if (videoType !== 'camera' || !isLocal()) {
return next(action);
break;
}
const { muted } = action.track;
if (muted !== undefined) {
if (typeof muted !== 'undefined') {
// addresses video mute state changes
if (muted) {
FaceLandmarksDetector.stopDetection(store);
@@ -87,7 +81,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
}
}
return next(action);
break;
}
case TRACK_REMOVED: {
const { jitsiTrack: { isLocal, videoType } } = action.track;
@@ -96,7 +90,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
FaceLandmarksDetector.stopDetection(store);
}
return next(action);
break;
}
case ADD_FACE_LANDMARKS: {
const state = getState();
@@ -110,7 +104,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
// Disabling for now as there is no value of having the data in speakerstats at the server
// sendFaceExpressionToServer(conference, faceLandmarks);
return next(action);
break;
}
case NEW_FACE_COORDINATES: {
const state = getState();
@@ -127,6 +121,8 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
faceBox,
id: localParticipant?.id
});
break;
}
}

View File

@@ -76,6 +76,7 @@ class DialInSummary extends PureComponent<IProps> {
<JitsiScreen
style = { styles.backDrop }>
<WebView
incognito = { true }
onError = { this._onError }
onShouldStartLoadWithRequest = { this._onNavigate }
renderLoading = { this._renderLoading }

View File

@@ -66,7 +66,7 @@ const ConferenceID: React.FC<IProps> = ({ conferenceID, t }) => {
return (
<div className = { styles.container }>
<div className = { styles.descriptionLabel }>
To join the meeting via phone, dial one of these numbers and then enter the pin
{ t('info.dialANumber') }
</div>
<div className = { styles.separator } />
<div className = { styles.pinLabel }>

View File

@@ -571,12 +571,13 @@ export function searchDirectory( // eslint-disable-line max-params
* @param {IReduxState} state - The current state.
* @param {string} inviteUrl - The conference/location URL.
* @param {boolean} useHtml - Whether to return html text.
* @param {boolean} skipDialIn - Whether to skip dial-in options or not.
* @returns {Promise<string>} A {@code Promise} resolving with a
* descriptive text that can be used to invite participants to a meeting.
*/
export function getShareInfoText(state: IReduxState, inviteUrl: string, useHtml?: boolean): Promise<string> {
export function getShareInfoText(
state: IReduxState, inviteUrl: string, useHtml?: boolean, skipDialIn?: boolean): Promise<string> {
let roomUrl = _decodeRoomURI(inviteUrl);
const includeDialInfo = state['features/base/config'] !== undefined;
if (useHtml) {
roomUrl = `<a href="${roomUrl}">${roomUrl}</a>`;
@@ -584,85 +585,72 @@ export function getShareInfoText(state: IReduxState, inviteUrl: string, useHtml?
let infoText = i18next.t('share.mainText', { roomUrl });
if (includeDialInfo) {
const { room } = parseURIString(inviteUrl);
let numbersPromise;
let hasPaymentError = false;
const { room } = parseURIString(inviteUrl);
const { dialInConfCodeUrl, dialInNumbersUrl, hosts } = state['features/base/config'];
const { locationURL = {} } = state['features/base/connection'];
const mucURL = hosts?.muc;
if (state['features/invite'].numbers
&& state['features/invite'].conferenceID) {
numbersPromise = Promise.resolve(state['features/invite']);
} else {
// we are requesting numbers and conferenceId directly
// not using updateDialInNumbers, because custom room
// is specified and we do not want to store the data
// in the state
const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
= state['features/base/config'];
const { locationURL = {} } = state['features/base/connection'];
const mucURL = hosts?.muc;
if (!dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
// URLs for fetching dial in numbers not defined
return Promise.resolve(infoText);
}
numbersPromise = Promise.all([
getDialInNumbers(dialInNumbersUrl, room, mucURL), // @ts-ignore
getDialInConferenceID(dialInConfCodeUrl, room, mucURL, locationURL)
]).then(([ numbers, {
conference, id, message } ]) => {
if (!conference || !id) {
return Promise.reject(message);
}
return {
numbers,
conferenceID: id
};
});
}
return numbersPromise.then(
({ conferenceID, numbers }) => {
const phoneNumber = _getDefaultPhoneNumber(numbers) || '';
return `${
i18next.t('info.dialInNumber')} ${
phoneNumber} ${
i18next.t('info.dialInConferenceID')} ${
conferenceID}#\n\n`;
})
.catch(error => {
logger.error('Error fetching numbers or conferenceID', error);
hasPaymentError = error?.status === StatusCode.PaymentRequired;
})
.then(defaultDialInNumber => {
if (hasPaymentError) {
infoText += `${
i18next.t('info.dialInNumber')} ${i18next.t('info.reachedLimit')} ${
i18next.t('info.upgradeOptions')} ${UPGRADE_OPTIONS_TEXT}`;
return infoText;
}
let dialInfoPageUrl = getDialInfoPageURL(state, room);
if (useHtml) {
dialInfoPageUrl
= `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`;
}
infoText += i18next.t('share.dialInfoText', {
defaultDialInNumber,
dialInfoPageUrl });
return infoText;
});
if (skipDialIn || !dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
// URLs for fetching dial in numbers not defined.
return Promise.resolve(infoText);
}
return Promise.resolve(infoText);
let hasPaymentError = false;
// We are requesting numbers and conferenceId directly
// not using updateDialInNumbers, because custom room
// is specified and we do not want to store the data
// in the state.
const numbersPromise = Promise.all([
getDialInNumbers(dialInNumbersUrl, room, mucURL), // @ts-ignore
getDialInConferenceID(dialInConfCodeUrl, room, mucURL, locationURL)
]).then(([ numbers, {
conference, id, message } ]) => {
if (!conference || !id) {
return Promise.reject(message);
}
return {
numbers,
conferenceID: id
};
});
return numbersPromise.then(({ conferenceID, numbers }) => {
const phoneNumber = _getDefaultPhoneNumber(numbers) || '';
return `${
i18next.t('info.dialInNumber')} ${
phoneNumber} ${
i18next.t('info.dialInConferenceID')} ${
conferenceID}#\n\n`;
})
.catch(error => {
logger.error('Error fetching numbers or conferenceID', error);
hasPaymentError = error?.status === StatusCode.PaymentRequired;
})
.then(defaultDialInNumber => {
if (hasPaymentError) {
infoText += `${
i18next.t('info.dialInNumber')} ${i18next.t('info.reachedLimit')} ${
i18next.t('info.upgradeOptions')} ${UPGRADE_OPTIONS_TEXT}`;
return infoText;
}
let dialInfoPageUrl = getDialInfoPageURL(state, room);
if (useHtml) {
dialInfoPageUrl = `<a href="${dialInfoPageUrl}">${dialInfoPageUrl}</a>`;
}
infoText += i18next.t('share.dialInfoText', {
defaultDialInNumber,
dialInfoPageUrl });
return infoText;
});
}
/**

View File

@@ -30,7 +30,7 @@ export const registerShortcut = (shortcut: IKeyboardShortcut): AnyAction => {
*/
export const unregisterShortcut = (character: string, altKey = false): AnyAction => {
return {
altKey,
alt: altKey,
type: UNREGISTER_KEYBOARD_SHORTCUT,
character
};

View File

@@ -21,7 +21,8 @@ import {
SET_PASSWORD_JOIN_FAILED
} from './actionTypes';
import { LOBBY_CHAT_INITIALIZED, MODERATOR_IN_CHAT_WITH_LEFT } from './constants';
import { getKnockingParticipants, getLobbyConfig, getLobbyEnabled } from './functions';
import { getKnockingParticipants, getLobbyConfig, getLobbyEnabled, isEnablingLobbyAllowed } from './functions';
import logger from './logger';
import { IKnockingParticipant } from './types';
/**
@@ -243,7 +244,11 @@ export function toggleLobbyMode(enabled: boolean) {
const conference = getCurrentConference(getState);
if (enabled) {
conference?.enableLobby();
if (isEnablingLobbyAllowed(getState())) {
conference?.enableLobby();
} else {
logger.info('Ignoring enable lobby request because there are visitors in the call already.');
}
} else {
conference?.disableLobby();
}

View File

@@ -1,5 +1,6 @@
import { IReduxState } from '../app/types';
import { getCurrentConference } from '../base/conference/functions';
import { getVisitorsCount } from '../visitors/functions';
import { IKnockingParticipant } from './types';
@@ -92,3 +93,13 @@ export function showLobbyChatButton(
return false;
};
}
/**
* Returns true if enabling lobby is allowed and false otherwise.
*
* @param {IReduxState} state - State object.
* @returns {boolean}
*/
export function isEnablingLobbyAllowed(state: IReduxState) {
return getVisitorsCount(state) <= 0;
}

View File

@@ -6,7 +6,8 @@ import { IStore } from '../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
import {
CONFERENCE_FAILED,
CONFERENCE_JOINED
CONFERENCE_JOINED,
ENDPOINT_MESSAGE_RECEIVED
} from '../base/conference/actionTypes';
import { conferenceWillJoin } from '../base/conference/actions';
import {
@@ -31,6 +32,7 @@ import {
handleLobbyChatInitialized,
removeLobbyChatParticipant
} from '../chat/actions.any';
import { arePollsDisabled } from '../conference/functions.any';
import { hideNotification, showNotification } from '../notifications/actions';
import {
LOBBY_NOTIFICATION_ID,
@@ -41,7 +43,11 @@ import {
import { INotificationProps } from '../notifications/types';
import { open as openParticipantsPane } from '../participants-pane/actions';
import { getParticipantsPaneOpen } from '../participants-pane/functions';
import { isPrejoinPageVisible, shouldAutoKnock } from '../prejoin/functions';
import {
isPrejoinEnabledInConfig,
isPrejoinPageVisible,
shouldAutoKnock
} from '../prejoin/functions';
import {
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
@@ -78,6 +84,13 @@ MiddlewareRegistry.register(store => next => action => {
return _conferenceFailed(store, next, action);
case CONFERENCE_JOINED:
return _conferenceJoined(store, next, action);
case ENDPOINT_MESSAGE_RECEIVED: {
const { participant, data } = action;
_maybeSendLobbyNotification(participant, data, store);
break;
}
case KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED: {
// We need the full update result to be in the store already
const result = next(action);
@@ -165,13 +178,6 @@ StateListenerRegistry.register(
dispatch(updateLobbyParticipantOnLeave(id));
});
});
conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, (origin: any, sender: any) =>
_maybeSendLobbyNotification(origin, sender, {
dispatch,
getState
})
);
}
}
);
@@ -201,7 +207,6 @@ function _handleLobbyNotification(store: IStore) {
if (knockingParticipants.length === 1) {
const firstParticipant = knockingParticipants[0];
const { disablePolls } = getState()['features/base/config'];
const showChat = showLobbyChatButton(firstParticipant)(getState());
descriptionKey = 'notify.participantWantsToJoin';
@@ -225,7 +230,7 @@ function _handleLobbyNotification(store: IStore) {
customActionType.splice(1, 0, BUTTON_TYPES.SECONDARY);
customActionHandler.splice(1, 0, () => batch(() => {
dispatch(handleLobbyChatInitialized(firstParticipant.id));
dispatch(openChat({}, disablePolls));
dispatch(openChat({}, arePollsDisabled(getState())));
}));
}
} else {
@@ -268,7 +273,6 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
const { membersOnly } = state['features/base/conference'];
const nonFirstFailure = Boolean(membersOnly);
const { isDisplayNameRequiredError } = state['features/lobby'];
const { prejoinConfig } = state['features/base/config'];
if (error.name === JitsiConferenceErrors.MEMBERS_ONLY_ERROR) {
if (typeof error.recoverable === 'undefined') {
@@ -283,7 +287,9 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
dispatch(openLobbyScreen());
// if there was an error about display name and pre-join is not enabled
if (shouldAutoKnock(state) || (isDisplayNameRequiredError && !prejoinConfig?.enabled) || lobbyWaitingForHost) {
if (shouldAutoKnock(state)
|| (isDisplayNameRequiredError && !isPrejoinEnabledInConfig(state))
|| lobbyWaitingForHost) {
dispatch(startKnocking());
}

View File

@@ -16,6 +16,7 @@ import {
CONFERENCE_JOINED,
CONFERENCE_LEFT,
CONFERENCE_WILL_JOIN,
ENDPOINT_MESSAGE_RECEIVED,
SET_ROOM
} from '../../base/conference/actionTypes';
import { JITSI_CONFERENCE_URL_KEY } from '../../base/conference/constants';
@@ -184,6 +185,22 @@ externalAPIEnabled && MiddlewareRegistry.register(store => next => action => {
break;
}
case ENDPOINT_MESSAGE_RECEIVED: {
const { participant, data } = action;
if (data?.name === ENDPOINT_TEXT_MESSAGE_NAME) {
sendEvent(
store,
ENDPOINT_TEXT_MESSAGE_RECEIVED,
/* data */ {
message: data.text,
senderId: participant.getId()
});
}
break;
}
case ENTER_PICTURE_IN_PICTURE:
sendEvent(store, type, /* data */ {});
break;
@@ -419,24 +436,6 @@ function _unregisterForNativeEvents() {
function _registerForEndpointTextMessages(store: IStore) {
const conference = getCurrentConference(store.getState());
conference?.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args: any[]) => {
if (args && args.length >= 2) {
const [ sender, eventData ] = args;
if (eventData.name === ENDPOINT_TEXT_MESSAGE_NAME) {
sendEvent(
store,
ENDPOINT_TEXT_MESSAGE_RECEIVED,
/* data */ {
message: eventData.text,
senderId: sender._id
});
}
}
});
conference?.on(
JitsiConferenceEvents.MESSAGE_RECEIVED,
(id: string, message: string, timestamp: number) => {

View File

@@ -13,6 +13,7 @@ import { isUnsafeRoomWarningEnabled } from '../../../prejoin/functions';
// @ts-ignore
import WelcomePage from '../../../welcome/components/WelcomePage';
import { isWelcomePageEnabled } from '../../../welcome/functions';
import Whiteboard from '../../../whiteboard/components/native/Whiteboard';
import { _ROOT_NAVIGATION_READY } from '../actionTypes';
import { rootNavigationRef } from '../rootNavigationContainerRef';
import { screen } from '../routes';
@@ -23,7 +24,8 @@ import {
navigationContainerTheme,
preJoinScreenOptions,
unsafeMeetingScreenOptions,
welcomeScreenOptions
welcomeScreenOptions,
whiteboardScreenOptions
} from '../screenOptions';
import ConnectingPage from './ConnectingPage';
@@ -94,6 +96,10 @@ const RootNavigationContainer = ({ dispatch, isUnsafeRoomWarningAvailable, isWel
component = { ConnectingPage }
name = { screen.connecting }
options = { connectingScreenOptions } />
<RootStack.Screen // @ts-ignore
component = { Whiteboard }
name = { screen.conference.whiteboard }
options = { whiteboardScreenOptions } />
<RootStack.Screen
component = { Prejoin }
name = { screen.preJoin }

View File

@@ -16,7 +16,7 @@ import Conference from '../../../../../conference/components/native/Conference';
// @ts-ignore
import CarMode from '../../../../../conference/components/native/carmode/CarMode';
// @ts-ignore
import { getDisablePolls } from '../../../../../conference/functions';
import { arePollsDisabled } from '../../../../../conference/functions';
// @ts-ignore
import SharedDocument from '../../../../../etherpad/components/native/SharedDocument';
// @ts-ignore
@@ -81,7 +81,7 @@ const ConferenceStack = createStackNavigator();
const ConferenceNavigationContainer = () => {
const isPollsDisabled = useSelector(getDisablePolls);
const isPollsDisabled = useSelector(arePollsDisabled);
let ChatScreen;
let chatScreenName;
let chatTitleString;

View File

@@ -11,6 +11,7 @@ import { getFeatureFlag } from '../../base/flags/functions';
import { IconCloseLarge } from '../../base/icons/svg';
import { toState } from '../../base/redux/functions';
import { cancelKnocking } from '../../lobby/actions.native';
import { isPrejoinEnabledInConfig } from '../../prejoin/functions';
import HeaderNavigationButton from './components/HeaderNavigationButton';
@@ -50,10 +51,8 @@ export function screenHeaderCloseButton(goBack: (e?: GestureResponderEvent | Rea
*/
export function isPrejoinPageEnabled(stateful: IStateful) {
const state = toState(stateful);
const { prejoinConfig } = state['features/base/config'];
const isPrejoinEnabledInConfig = prejoinConfig?.enabled;
return getFeatureFlag(state, PREJOIN_PAGE_ENABLED, isPrejoinEnabledInConfig ?? true);
return getFeatureFlag(state, PREJOIN_PAGE_ENABLED, isPrejoinEnabledInConfig(state));
}
/**

View File

@@ -22,7 +22,8 @@ export const screen = {
security: 'Security Options',
sharedDocument: 'Shared document',
speakerStats: 'Speaker Stats',
subtitles: 'Subtitles'
subtitles: 'Subtitles',
whiteboard: 'Whiteboard'
},
connecting: 'Connecting',
dialInSummary: 'Dial-In Info',

View File

@@ -188,6 +188,19 @@ export const connectingScreenOptions = {
headerShown: false
};
/**
* Screen options for the whiteboard screen.
*/
export const whiteboardScreenOptions = {
gestureEnabled: true,
headerStyle: {
backgroundColor: BaseTheme.palette.ui01
},
headerTitleStyle: {
color: BaseTheme.palette.text01
}
};
/**
* Screen options for pre-join screen.
*/

View File

@@ -1,6 +1,8 @@
import { DOMParser } from '@xmldom/xmldom';
import { atob, btoa } from 'abab';
import { Platform } from 'react-native';
import BackgroundTimer from 'react-native-background-timer';
import { TextDecoder, TextEncoder } from 'text-encoding';
import 'promise.allsettled/auto'; // Promise.allSettled.
import 'react-native-url-polyfill/auto'; // Complete URL polyfill.
@@ -313,4 +315,23 @@ function _visitNode(node, callback) {
global.sessionStorage = new Storage();
}
global.TextDecoder = TextDecoder;
global.TextEncoder = TextEncoder;
// atob
//
// Required by:
// - Strophe
if (typeof global.atob === 'undefined') {
global.atob = atob;
}
// btoa
//
// Required by:
// - Strophe
if (typeof global.btoa === 'undefined') {
global.btoa = btoa;
}
})(global || window || this); // eslint-disable-line no-invalid-this

View File

@@ -103,6 +103,13 @@ export const RAISE_HAND_NOTIFICATION_ID = 'RAISE_HAND_NOTIFICATION';
*/
export const SALESFORCE_LINK_NOTIFICATION_ID = 'SALESFORCE_LINK_NOTIFICATION';
/**
* The identifier of the lobby notification.
*
* @type {string}
*/
export const VISITORS_PROMOTION_NOTIFICATION_ID = 'VISITORS_PROMOTION_NOTIFICATION';
/**
* Amount of participants beyond which no join notification will be emitted.
*/

View File

@@ -1,11 +1,11 @@
import { IStore } from '../app/types';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import { ENDPOINT_MESSAGE_RECEIVED, NON_PARTICIPANT_MESSAGE_RECEIVED } from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
import { playSound } from '../base/sounds/actions';
import { INCOMING_MSG_SOUND_ID } from '../chat/constants';
import { arePollsDisabled } from '../conference/functions.any';
import { showNotification } from '../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE, NOTIFICATION_TYPE } from '../notifications/constants';
@@ -58,37 +58,38 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
case ENDPOINT_MESSAGE_RECEIVED: {
const { participant, data } = action;
const isNewPoll = data.type === COMMAND_NEW_POLL;
conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(user: any, data: any) => {
const isNewPoll = data.type === COMMAND_NEW_POLL;
_handleReceivePollsMessage({
...data,
senderId: isNewPoll ? user._id : undefined,
voterId: isNewPoll ? undefined : user._id
}, dispatch);
});
conference.on(JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,
(id: any, data: any) => {
const isNewPoll = data.type === COMMAND_NEW_POLL;
_handleReceivePollsMessage({
...data,
senderId: isNewPoll ? id : undefined,
voterId: isNewPoll ? undefined : id
}, dispatch);
});
_handleReceivePollsMessage({
...data,
senderId: isNewPoll ? participant.getId() : undefined,
voterId: isNewPoll ? undefined : participant.getId()
}, dispatch, getState);
break;
}
// Middleware triggered when a poll is received
case RECEIVE_POLL: {
case NON_PARTICIPANT_MESSAGE_RECEIVED: {
const { id, json: data } = action;
const isNewPoll = data.type === COMMAND_NEW_POLL;
_handleReceivePollsMessage({
...data,
senderId: isNewPoll ? id : undefined,
voterId: isNewPoll ? undefined : id
}, dispatch, getState);
break;
}
case RECEIVE_POLL: {
const state = getState();
if (arePollsDisabled(state)) {
break;
}
const isChatOpen: boolean = state['features/chat'].isOpen;
const isPollsTabFocused: boolean = state['features/chat'].isPollsTabFocused;
@@ -109,10 +110,15 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
*
* @param {Object} data - The json data carried by the polls message.
* @param {Function} dispatch - The dispatch function.
* @param {Function} getState - The getState function.
*
* @returns {void}
*/
function _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch']) {
function _handleReceivePollsMessage(data: any, dispatch: IStore['dispatch'], getState: IStore['getState']) {
if (arePollsDisabled(getState())) {
return;
}
switch (data.type) {
case COMMAND_NEW_POLL: {
const { question, answers, pollId, senderId } = data;

View File

@@ -38,7 +38,7 @@ import { navigateRoot } from '../../../mobile/navigation/rootNavigationContainer
import { screen } from '../../../mobile/navigation/routes';
import AudioMuteButton from '../../../toolbox/components/native/AudioMuteButton';
import VideoMuteButton from '../../../toolbox/components/native/VideoMuteButton';
import { isDisplayNameRequired } from '../../functions';
import { isDisplayNameRequired, isRoomNameEnabled } from '../../functions';
import { IPrejoinProps } from '../../types';
import { hasDisplayName } from '../../utils';
@@ -58,6 +58,7 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
= useSelector((state: IReduxState) => !getFeatureFlag(state, PREJOIN_PAGE_HIDE_DISPLAY_NAME, false));
const isDisplayNameReadonly = useSelector(isNameReadOnly);
const roomName = useSelector((state: IReduxState) => getConferenceName(state));
const roomNameEnabled = useSelector((state: IReduxState) => isRoomNameEnabled(state));
const participantName = localParticipant?.name;
const [ displayName, setDisplayName ]
= useState(participantName || '');
@@ -168,12 +169,16 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
{
isFocused
&& <View style = { largeVideoContainerStyles as StyleProp<ViewStyle> }>
<View style = { styles.displayRoomNameBackdrop as StyleProp<TextStyle> }>
<Text
numberOfLines = { 1 }
style = { styles.preJoinRoomName as StyleProp<TextStyle> }>
{ roomName }
</Text>
<View style = { styles.conferenceInfo as StyleProp<ViewStyle> }>
{roomNameEnabled && (
<View style = { styles.displayRoomNameBackdrop as StyleProp<TextStyle> }>
<Text
numberOfLines = { 1 }
style = { styles.preJoinRoomName as StyleProp<TextStyle> }>
{ roomName }
</Text>
</View>
)}
</View>
<LargeVideo />
</View>
@@ -194,10 +199,15 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
placeholder = { t('dialog.enterDisplayName') }
value = { displayName } />
}
{showDisplayNameError && (
<View style = { styles.errorContainer as StyleProp<TextStyle> }>
<Text style = { styles.error as StyleProp<TextStyle> }>{t('prejoin.errorMissingName')}</Text>
</View>)}
{
showDisplayNameError && (
<View style = { styles.errorContainer as StyleProp<TextStyle> }>
<Text style = { styles.error as StyleProp<TextStyle> }>
{ t('prejoin.errorMissingName') }
</Text>
</View>
)
}
<Button
accessibilityLabel = 'prejoin.joinMeeting'
disabled = { showDisplayNameError }

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