Compare commits
1 Commits
7712
...
fix_analyt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a403a221ae |
3
.github/workflows/ci-lua.yml
vendored
3
.github/workflows/ci-lua.yml
vendored
@@ -17,8 +17,7 @@ jobs:
|
||||
|
||||
- name: Check lua codes
|
||||
run: |
|
||||
set -o pipefail && luacheck . \
|
||||
--exclude-files=resources/prosody-plugins/mod_firewall/mod_firewall.lua | awk -F: '
|
||||
set -o pipefail && luacheck . | awk -F: '
|
||||
{
|
||||
print $0
|
||||
printf "::warning file=%s,line=%s,col=%s::%s\n", $1, $2, $3, $4
|
||||
|
||||
@@ -27,7 +27,7 @@ And many more!
|
||||
|
||||
## Using Jitsi Meet
|
||||
|
||||
Using Jitsi Meet is straightforward, as it's browser based. Head over to [meet.jit.si](https://meet.jit.si) and give it a try. It's scalable and free to use. All you need is a Google, Facebook or GitHub account in order to start a meeting. All browsers are supported!
|
||||
Using Jitsi Meet is straightforward, as it's browser based. Head over to [meet.jit.si](https://meet.jit.si) and give it a try. It's anonymous, scalable and free to use. All browsers are supported!
|
||||
|
||||
Using mobile? No problem, you can either use your mobile web browser or our fully-featured
|
||||
mobile apps:
|
||||
|
||||
@@ -378,7 +378,7 @@ var config = {
|
||||
// DEPRECATED. Use transcription.preferredLanguage instead.
|
||||
// preferredTranscribeLanguage: 'en-US',
|
||||
|
||||
// DEPRECATED. Use transcription.autoTranscribeOnRecord instead.
|
||||
// DEPRECATED. Use transcription.autoCaptionOnRecord instead.
|
||||
// autoCaptionOnRecord: false,
|
||||
|
||||
// Transcription options.
|
||||
@@ -410,8 +410,8 @@ var config = {
|
||||
// // Disable start transcription for all participants.
|
||||
// disableStartForAll: false,
|
||||
|
||||
// // Enables automatic turning on transcribing when recording is started
|
||||
// autoTranscribeOnRecord: false,
|
||||
// // Enables automatic turning on captions when recording is started
|
||||
// autoCaptionOnRecord: false,
|
||||
// },
|
||||
|
||||
// Misc
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
font-size: 14px;
|
||||
margin-left: 16px;
|
||||
max-width: 70%;
|
||||
|
||||
&-no-space {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.space-top {
|
||||
|
||||
@@ -557,6 +557,8 @@
|
||||
"youtubeTerms": "شروط خدمة يوتيوب"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "سمح بالدخول",
|
||||
"admitAll": "سمح للجميع بالدخول",
|
||||
"allow": "اسمح",
|
||||
"backToKnockModeButton": "لا يوجد كلمة مرور، اطلب الإذن بالدخول بدلًا من ذلك.",
|
||||
"chat": "دردشة",
|
||||
@@ -591,6 +593,8 @@
|
||||
"notificationTitle": "غرفة الانتظار",
|
||||
"passwordField": "أدخل كلمة الدخول إلى المُلتقى",
|
||||
"passwordJoinButton": "انضم",
|
||||
"reject": "رفض",
|
||||
"rejectAll": "رفض الكل",
|
||||
"title": "غرفة الانتظار",
|
||||
"toggleLabel": "فعِّل غرفة الانتظار"
|
||||
},
|
||||
@@ -716,8 +720,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "سمح بالدخول",
|
||||
"admitAll": "سمح للجميع بالدخول",
|
||||
"allow": "السماح للحاضرين بـ:",
|
||||
"allowVideo": "السماح بالفيديو",
|
||||
"askUnmute": "اطلب إعادة الصوت",
|
||||
@@ -730,7 +732,6 @@
|
||||
"mute": "كتم الصوت",
|
||||
"muteAll": "كتم الكل",
|
||||
"muteEveryoneElse": "كتم صوت الآخرين",
|
||||
"reject": "رفض",
|
||||
"stopEveryonesVideo": "أوقف فيديو الجميع",
|
||||
"stopVideo": "أوقف الفيديو",
|
||||
"unblockEveryoneMicCamera": "قم بإلغاء حظر ميكروفون وكاميرا الجميع",
|
||||
|
||||
@@ -420,6 +420,8 @@
|
||||
"youtubeTerms": "Условия за ползване на YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Допусни",
|
||||
"allow": "Разреши",
|
||||
"backToKnockModeButton": "Заявка за включване без парола",
|
||||
"dialogTitle": "Режим лоби",
|
||||
"disableDialogContent": "Режим Лоби е включен. Този решим защитава срещите Ви от случайни посетители. Искате ли да го изключите?",
|
||||
@@ -448,6 +450,7 @@
|
||||
"notificationTitle": "Лоби",
|
||||
"passwordField": "Въведи парола за срещата",
|
||||
"passwordJoinButton": "Влез",
|
||||
"reject": "Откажи",
|
||||
"title": "Лоби",
|
||||
"toggleLabel": "Включи лоби"
|
||||
},
|
||||
@@ -518,13 +521,6 @@
|
||||
"suboptimalExperienceTitle": "Внимание",
|
||||
"unmute": "Пускане на микрофона"
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Допусни",
|
||||
"allow": "Разреши",
|
||||
"reject": "Откажи"
|
||||
}
|
||||
},
|
||||
"passwordDigitsOnly": "До {{number}} цифри",
|
||||
"passwordSetRemotely": "зададена от друг участник",
|
||||
"poweredby": "с подкрепата на",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"youtubeTerms": "Condicions de servei de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Admet",
|
||||
"admitAll": "Admet tothom",
|
||||
"allow": "Permet",
|
||||
"backToKnockModeButton": "Demaneu per a unir-vos",
|
||||
"chat": "Xat",
|
||||
@@ -595,6 +597,8 @@
|
||||
"notificationTitle": "Sala d'espera",
|
||||
"passwordField": "Introduïu la contrasenya de la reunió",
|
||||
"passwordJoinButton": "Entra",
|
||||
"reject": "Rebuja",
|
||||
"rejectAll": "Rebutja-ho tot",
|
||||
"title": "Sala d'espera",
|
||||
"toggleLabel": "Activa la sala d'espera"
|
||||
},
|
||||
@@ -723,8 +727,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Admet",
|
||||
"admitAll": "Admet tothom",
|
||||
"allow": "Permet als assistents:",
|
||||
"allowVideo": "Permet el vídeo",
|
||||
"askUnmute": "Demanar l'activació el micròfon",
|
||||
@@ -737,7 +739,6 @@
|
||||
"mute": "Silenciar",
|
||||
"muteAll": "Silencia tothom",
|
||||
"muteEveryoneElse": "Silenciar tothom",
|
||||
"reject": "Rebuja",
|
||||
"stopEveryonesVideo": "Atura el vídeo a tothom",
|
||||
"stopVideo": "Atura el vídeo",
|
||||
"unblockEveryoneMicCamera": "Desbloquejar el micròfon i la càmera de tothom",
|
||||
|
||||
@@ -557,6 +557,8 @@
|
||||
"youtubeTerms": "Podmínky používání YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "",
|
||||
"admitAll": "",
|
||||
"allow": "Povolit",
|
||||
"backToKnockModeButton": "Žádné heslo, místo toho požádat o přijetí",
|
||||
"chat": "",
|
||||
@@ -591,6 +593,7 @@
|
||||
"notificationTitle": "Předsálí",
|
||||
"passwordField": "Zadejte heslo setkání",
|
||||
"passwordJoinButton": "Vstoupit",
|
||||
"reject": "Odmítnout",
|
||||
"title": "Předsálí",
|
||||
"toggleLabel": "Zapnout předsálí"
|
||||
},
|
||||
@@ -716,7 +719,22 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"reject": "Odmítnout"
|
||||
"allow": "",
|
||||
"allowVideo": "",
|
||||
"askUnmute": "",
|
||||
"audioModeration": "",
|
||||
"blockEveryoneMicCamera": "",
|
||||
"invite": "",
|
||||
"moreModerationActions": "",
|
||||
"moreModerationControls": "",
|
||||
"moreParticipantOptions": "",
|
||||
"mute": "",
|
||||
"muteAll": "",
|
||||
"muteEveryoneElse": "",
|
||||
"stopEveryonesVideo": "",
|
||||
"stopVideo": "",
|
||||
"unblockEveryoneMicCamera": "",
|
||||
"videoModeration": ""
|
||||
},
|
||||
"close": "",
|
||||
"header": "",
|
||||
|
||||
@@ -636,6 +636,8 @@
|
||||
"youtubeTerms": "YouTube-Nutzungsbedingungen"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Zulassen",
|
||||
"admitAll": "Alle zulassen",
|
||||
"backToKnockModeButton": "Kein Passwort, stattdessen Beitritt anfragen",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Lobbymodus",
|
||||
@@ -669,6 +671,8 @@
|
||||
"notificationLobbyEnabled": "{{originParticipantName}} hat die Lobby aktiviert",
|
||||
"notificationTitle": "Lobby",
|
||||
"passwordJoinButton": "Beitreten",
|
||||
"reject": "Ablehnen",
|
||||
"rejectAll": "Alle ablehnen",
|
||||
"title": "Lobby",
|
||||
"toggleLabel": "Lobby aktivieren"
|
||||
},
|
||||
@@ -803,8 +807,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Zulassen",
|
||||
"admitAll": "Alle zulassen",
|
||||
"allow": "Anwesenden erlauben:",
|
||||
"allowVideo": "Kamera einschalten",
|
||||
"askUnmute": "Anfragen, Stummschaltung aufzuheben",
|
||||
@@ -817,7 +819,6 @@
|
||||
"mute": "Stummschalten",
|
||||
"muteAll": "Alle stummschalten",
|
||||
"muteEveryoneElse": "Alle anderen stummschalten",
|
||||
"reject": "Ablehnen",
|
||||
"stopEveryonesVideo": "Alle Kameras ausschalten",
|
||||
"stopVideo": "Kamera ausschalten",
|
||||
"unblockEveryoneMicCamera": "Kamera und Mikrofon von allen entsperren",
|
||||
|
||||
@@ -563,6 +563,8 @@
|
||||
"youtubeTerms": "wužywaŕske wustawki za youtube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "pśizwóliś",
|
||||
"admitAll": "wšyknym pśizwólenje daś",
|
||||
"backToKnockModeButton": "mimo kodowego słowa, město togo wó pśistup pšosyś",
|
||||
"chat": "chat",
|
||||
"dialogTitle": "lobbyjowy modus",
|
||||
@@ -596,6 +598,8 @@
|
||||
"notificationTitle": "lobby",
|
||||
"passwordField": "kodowe słowo za konferencu zapódaś",
|
||||
"passwordJoinButton": "pśistupiś",
|
||||
"reject": "wótpokazaś",
|
||||
"rejectAll": "wšykne wótpokazaś",
|
||||
"title": "",
|
||||
"toggleLabel": "lobby aktiwěrowaś / deaktiwěrowaś"
|
||||
},
|
||||
@@ -726,8 +730,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "pśizwóliś",
|
||||
"admitAll": "wšyknym pśizwólenje daś",
|
||||
"allow": "wobźělnikam pšawo daś:",
|
||||
"allowVideo": "kameru aktiwěrowaś",
|
||||
"askUnmute": "pšosbu wó anulěrowanje wuśišenja stajiś",
|
||||
@@ -740,7 +742,6 @@
|
||||
"mute": "wuśišyś",
|
||||
"muteAll": "wšyknych wuśišyś",
|
||||
"muteEveryoneElse": "wšykne druge wuśišyś",
|
||||
"reject": "wótpokazaś",
|
||||
"stopEveryonesVideo": "wšykne kamery wušaltowaś",
|
||||
"stopVideo": "kameru wušaltowaś",
|
||||
"unblockEveryoneMicCamera": "blokěrowane kamery a mikrofon wšyknych zasej aktiwěrowaś",
|
||||
|
||||
@@ -580,6 +580,8 @@
|
||||
"youtubeTerms": "Όροι υπηρεσιών YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Αποδοχή",
|
||||
"admitAll": "Αποδοχή όλων",
|
||||
"backToKnockModeButton": "Αίτημα εισόδου",
|
||||
"chat": "Συνομιλία",
|
||||
"dialogTitle": "Λειτουργία υποδοχής",
|
||||
@@ -613,6 +615,8 @@
|
||||
"notificationTitle": "Υποδοχή",
|
||||
"passwordField": "Εισάγετε τον κωδικό σύσκεψης",
|
||||
"passwordJoinButton": "Συμμετοχή",
|
||||
"reject": "Απόρριψη",
|
||||
"rejectAll": "Απόρριψη όλων",
|
||||
"title": "Υποδοχή",
|
||||
"toggleLabel": "Ενεργοποίηση υποδοχής"
|
||||
},
|
||||
@@ -741,8 +745,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Αποδοχή",
|
||||
"admitAll": "Αποδοχή όλων",
|
||||
"allow": "Επιτρέψτε στους συμμετέχοντες να:",
|
||||
"allowVideo": "Επιτρέψτε το βίντεο",
|
||||
"askUnmute": "Αίτηση για κατάργηση σίγησης",
|
||||
@@ -755,7 +757,6 @@
|
||||
"mute": "Σίγηση",
|
||||
"muteAll": "Σίγηση όλων",
|
||||
"muteEveryoneElse": "Σίγηση όλων των άλλων",
|
||||
"reject": "Απόρριψη",
|
||||
"stopEveryonesVideo": "Διακοπή όλων των βίντεο",
|
||||
"stopVideo": "Διακοπή του βίντεο",
|
||||
"unblockEveryoneMicCamera": "Επιτρέψτε τα μικρόφωνα και τις κάμερες όλων",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"youtubeTerms": "Uzkondiĉoj de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Akcepti",
|
||||
"admitAll": "Akcepti ĉion",
|
||||
"allow": "Permesi",
|
||||
"backToKnockModeButton": "Petu por aliĝi",
|
||||
"chat": "Babilejo",
|
||||
@@ -595,6 +597,8 @@
|
||||
"notificationTitle": "Atendejo",
|
||||
"passwordField": "Entajpu pasvorton de la renkontiĝo",
|
||||
"passwordJoinButton": "Aliĝi",
|
||||
"reject": "Malakceptu",
|
||||
"rejectAll": "Malakceptu ĉion",
|
||||
"title": "Atendejo",
|
||||
"toggleLabel": "Ŝaltu atendejon"
|
||||
},
|
||||
@@ -721,8 +725,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Akcepti",
|
||||
"admitAll": "Akcepti ĉion",
|
||||
"allow": "Al la partoprenantoj permesi:",
|
||||
"allowVideo": "Permesi kameraon",
|
||||
"askUnmute": "Peti malsilentigi",
|
||||
@@ -735,7 +737,6 @@
|
||||
"mute": "Silentigi",
|
||||
"muteAll": "Silentigi ĉiujn",
|
||||
"muteEveryoneElse": "Silentigi ĉiujn aliajn",
|
||||
"reject": "Malakceptu",
|
||||
"stopEveryonesVideo": "Ĉesigu ĉies videaĵon",
|
||||
"stopVideo": "Ĉesigu la videaĵon",
|
||||
"unblockEveryoneMicCamera": "Malbloku ĉies mikrofonon kaj kameraon",
|
||||
|
||||
@@ -598,6 +598,8 @@
|
||||
"youtubeTerms": "Términos de servicios de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Admitir",
|
||||
"admitAll": "Admitir todo",
|
||||
"backToKnockModeButton": "No hay contraseña, pide permiso para entrar.",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Sala de espera",
|
||||
@@ -631,6 +633,8 @@
|
||||
"notificationTitle": "Sala de espera",
|
||||
"passwordField": "Introduce la contraseña de la reunión",
|
||||
"passwordJoinButton": "Entrar",
|
||||
"reject": "Rechazar",
|
||||
"rejectAll": "Rechazar todo",
|
||||
"title": "Sala de espera",
|
||||
"toggleLabel": "Activar sala de espera"
|
||||
},
|
||||
@@ -764,8 +768,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Admitir",
|
||||
"admitAll": "Admitir todo",
|
||||
"allow": "Permitir a los asistentes:",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"askUnmute": "Pida que le quiten el silencio",
|
||||
@@ -778,7 +780,6 @@
|
||||
"mute": "Silenciar",
|
||||
"muteAll": "Silenciar a todos",
|
||||
"muteEveryoneElse": "Silenciar al resto",
|
||||
"reject": "Rechazar",
|
||||
"stopEveryonesVideo": "Detener el vídeo de todos",
|
||||
"stopVideo": "Detener el vídeo",
|
||||
"unblockEveryoneMicCamera": "Desbloquear el micrófono y la cámara de todos",
|
||||
|
||||
@@ -521,6 +521,8 @@
|
||||
"youtubeTerms": "Términos de servicios de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Admitir",
|
||||
"admitAll": "Admitir todo",
|
||||
"allow": "permitir",
|
||||
"backToKnockModeButton": "No hay contraseña, pide permiso para entrar.",
|
||||
"dialogTitle": "Sala de espera",
|
||||
@@ -551,6 +553,8 @@
|
||||
"notificationTitle": "Sala de espera",
|
||||
"passwordField": "Introduce la contraseña de la reunión",
|
||||
"passwordJoinButton": "Entrar",
|
||||
"reject": "Rechazar",
|
||||
"rejectAll": "Rechazar todo",
|
||||
"title": "Sala de espera",
|
||||
"toggleLabel": "Activar sala de espera"
|
||||
},
|
||||
@@ -648,8 +652,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Admitir",
|
||||
"admitAll": "Admitir todo",
|
||||
"allow": "Permitir a los asistentes:",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"askUnmute": "Pida que le quiten el silencio",
|
||||
@@ -659,7 +661,6 @@
|
||||
"mute": "Silenciar",
|
||||
"muteAll": "Silenciar a todos los demás",
|
||||
"muteEveryoneElse": "Silenciar al resto",
|
||||
"reject": "Rechazar",
|
||||
"stopEveryonesVideo": "Detener el vídeo de todos",
|
||||
"stopVideo": "Detener el vídeo",
|
||||
"unblockEveryoneMicCamera": "Desbloquear el micrófono y la cámara de todos",
|
||||
|
||||
@@ -463,6 +463,8 @@
|
||||
"youtubeTerms": "YouTuberen erabilpen baldintzak"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Onartu",
|
||||
"admitAll": "Onartu guztiak",
|
||||
"allow": "Baimendu",
|
||||
"backToKnockModeButton": "Ez du pasahitza erabili, baina sartzea eskatu du",
|
||||
"dialogTitle": "Itxaron-gela modua",
|
||||
@@ -492,6 +494,7 @@
|
||||
"notificationTitle": "Itxaron-gela",
|
||||
"passwordField": "Idatzi bileraren pasahitza",
|
||||
"passwordJoinButton": "Sartu",
|
||||
"reject": "Baztertu",
|
||||
"title": "Itxaron-gela",
|
||||
"toggleLabel": "Itxaron-gela aktibatu"
|
||||
},
|
||||
@@ -573,11 +576,8 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Onartu",
|
||||
"admitAll": "Onartu guztiak",
|
||||
"invite": "Gonbidatu norbait",
|
||||
"muteAll": "Ixilarazi guztiak",
|
||||
"reject": "Baztertu",
|
||||
"stopVideo": "Gelditu bideoa"
|
||||
},
|
||||
"close": "Itxi",
|
||||
|
||||
@@ -606,6 +606,8 @@
|
||||
"youtubeTerms": "شرایط خدمات یوتیوب"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "پذیرفتن",
|
||||
"admitAll": "پذیرفتن همه",
|
||||
"backToKnockModeButton": "درخواست برای پیوستن",
|
||||
"chat": "گپ",
|
||||
"dialogTitle": "حالت اتاق انتظار",
|
||||
@@ -639,6 +641,8 @@
|
||||
"notificationTitle": "اتاق انتظار",
|
||||
"passwordField": "گذرواژهٔ جلسه را وارد کنید",
|
||||
"passwordJoinButton": "پیوستن",
|
||||
"reject": "ردکردن",
|
||||
"rejectAll": "ردکردن همه",
|
||||
"title": "اتاق انتظار",
|
||||
"toggleLabel": "فعالکردن اتاق انتظار"
|
||||
},
|
||||
@@ -772,8 +776,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "پذیرفتن",
|
||||
"admitAll": "پذیرفتن همه",
|
||||
"allow": "به حاضران اجازه دهید:",
|
||||
"allowVideo": "اجازهٔ ویدیو",
|
||||
"askUnmute": "درخواست وصلکردن صدا",
|
||||
@@ -786,7 +788,6 @@
|
||||
"mute": "بیصداکردن",
|
||||
"muteAll": "بیصداکردن همه",
|
||||
"muteEveryoneElse": "بیصداکردن بقیه افراد",
|
||||
"reject": "ردکردن",
|
||||
"stopEveryonesVideo": "توقف ویدیوی همه",
|
||||
"stopVideo": "توقف ویدیو",
|
||||
"unblockEveryoneMicCamera": "رفع مسدودی میکروفون و دوربین همه",
|
||||
|
||||
@@ -584,6 +584,8 @@
|
||||
"youtubeTerms": "Conditions d'utilisation de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Accepter",
|
||||
"admitAll": "Tout accepter",
|
||||
"backToKnockModeButton": "Aucun mot de passe, demander à rejoindre plutôt",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Mode salle d'attente",
|
||||
@@ -617,6 +619,8 @@
|
||||
"notificationTitle": "Salle d'attente",
|
||||
"passwordField": "Veuillez saisir le mot de passe de la réunion",
|
||||
"passwordJoinButton": "Rejoindre",
|
||||
"reject": "Refuser",
|
||||
"rejectAll": "Refuser tout",
|
||||
"title": "Salle d'attente",
|
||||
"toggleLabel": "Activer la salle d'attente"
|
||||
},
|
||||
@@ -747,8 +751,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Accepter",
|
||||
"admitAll": "Tout accepter",
|
||||
"allow": "Autoriser les participants à:",
|
||||
"allowVideo": "permettre la vidéo",
|
||||
"askUnmute": "Demander de réactiver le micro",
|
||||
@@ -761,7 +763,6 @@
|
||||
"mute": "Couper le micro",
|
||||
"muteAll": "Couper le micro de tout le monde",
|
||||
"muteEveryoneElse": "Couper le micro de tous les autres",
|
||||
"reject": "Refuser",
|
||||
"stopEveryonesVideo": "Couper toutes les caméras",
|
||||
"stopVideo": "Couper la vidéo",
|
||||
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras",
|
||||
|
||||
@@ -482,6 +482,7 @@
|
||||
"notificationTitle": "लॉबी",
|
||||
"passwordField": "मीटिंग पासवर्ड दर्ज करें",
|
||||
"passwordJoinButton": "Join",
|
||||
"reject": "अस्वीकार",
|
||||
"title": "लॉबी",
|
||||
"toggleLabel": "लॉबी सक्षम करें"
|
||||
},
|
||||
@@ -558,11 +559,6 @@
|
||||
"videoMutedRemotelyDescription": "You can always turn it on again.",
|
||||
"videoMutedRemotelyTitle": "आपका कैमरा {{participantDisplayName}}द्वारा अक्षम कर दिया गया है!"
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"reject": "अस्वीकार"
|
||||
}
|
||||
},
|
||||
"passwordDigitsOnly": "Up to {{number}} digits",
|
||||
"passwordSetRemotely": "दूसरे प्रतिभागी द्वारा निर्धारित",
|
||||
"poweredby": "powered by",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"youtubeTerms": "Uvjeti YouTube usluge"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Prihvati",
|
||||
"admitAll": "Prihvati sve",
|
||||
"allow": "Dopusti",
|
||||
"backToKnockModeButton": "Zatraži pridruživanje",
|
||||
"chat": "Chat",
|
||||
@@ -595,6 +597,8 @@
|
||||
"notificationTitle": "Predvorje",
|
||||
"passwordField": "Upiši lozinku sastanka",
|
||||
"passwordJoinButton": "Pridruži se",
|
||||
"reject": "Odbij",
|
||||
"rejectAll": "Odbij sve",
|
||||
"title": "Predvorje",
|
||||
"toggleLabel": "Uključi predvorje"
|
||||
},
|
||||
@@ -721,8 +725,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Prihvati",
|
||||
"admitAll": "Prihvati sve",
|
||||
"allow": "Dozvoli sudionicima da:",
|
||||
"allowVideo": "Dozvole video",
|
||||
"askUnmute": "Zatraže isključivanje zvuka",
|
||||
@@ -735,7 +737,6 @@
|
||||
"mute": "Isključe zvuk",
|
||||
"muteAll": "Isključe zvuk svih sudionika",
|
||||
"muteEveryoneElse": "Isključe zvuk svih drugih",
|
||||
"reject": "Odbij",
|
||||
"stopEveryonesVideo": "Prekinu videa svih",
|
||||
"stopVideo": "Prekinu video",
|
||||
"unblockEveryoneMicCamera": "Deblokiraju mikrofone i kamere svih sudionika",
|
||||
|
||||
@@ -551,6 +551,8 @@
|
||||
"youtubeTerms": "wuměnjenja wužiwanja na YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "přizwolić",
|
||||
"admitAll": "wšitko přizwolić",
|
||||
"allow": "přiwzać",
|
||||
"backToKnockModeButton": "žane hesło, město toho wo přistup prosyć",
|
||||
"chat": "chat",
|
||||
@@ -585,6 +587,8 @@
|
||||
"notificationTitle": "lobby",
|
||||
"passwordField": "konferencne hesło zapodać",
|
||||
"passwordJoinButton": "přistupić",
|
||||
"reject": "wotpokazać",
|
||||
"rejectAll": "wšitko wotpokazać",
|
||||
"title": "lobby",
|
||||
"toggleLabel": "lobby aktiwěrować"
|
||||
},
|
||||
@@ -706,8 +710,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "přizwolić",
|
||||
"admitAll": "wšitko přizwolić",
|
||||
"allow": "přitomnym dowolić",
|
||||
"allowVideo": "kameru zaswěčić",
|
||||
"askUnmute": "wo wotstajenje šaltowanja na němosć prosyć",
|
||||
@@ -720,7 +722,6 @@
|
||||
"mute": "něme šaltować",
|
||||
"muteAll": "wšěch němych šaltować",
|
||||
"muteEveryoneElse": "wšěch druhich němych šaltować",
|
||||
"reject": "wotpokazać",
|
||||
"stopEveryonesVideo": "wšitke kamery hasnyć",
|
||||
"stopVideo": "kameru hasnyć",
|
||||
"unblockEveryoneMicCamera": "kameru a mikrofon wšěch wočinić",
|
||||
|
||||
@@ -462,6 +462,8 @@
|
||||
"youtubeTerms": "YouTube szolgáltatási feltételek"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Engedélyezés",
|
||||
"admitAll": "Mindet engedélyez",
|
||||
"allow": "Engedélyez",
|
||||
"backToKnockModeButton": "Csatlakozási kérelem küldése",
|
||||
"chat": "Chat",
|
||||
@@ -493,6 +495,8 @@
|
||||
"notificationTitle": "Lobby",
|
||||
"passwordField": "Adja meg az értekezlet jelszavát",
|
||||
"passwordJoinButton": "Csatlakozás",
|
||||
"reject": "Elutasít",
|
||||
"rejectAll": "Mindet elutasít",
|
||||
"toggleLabel": "Lobby engedélyezése"
|
||||
},
|
||||
"localRecording": {
|
||||
@@ -575,8 +579,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Engedélyezés",
|
||||
"admitAll": "Mindet engedélyez",
|
||||
"allow": "Engedélyezés a résztvevőknek, hogy:",
|
||||
"allowVideo": "Videó engedélyezése",
|
||||
"askUnmute": "Kérje a némítás feloldását",
|
||||
@@ -589,7 +591,6 @@
|
||||
"mute": "Némítás",
|
||||
"muteAll": "Mindenkit elnémít",
|
||||
"muteEveryoneElse": "Mute everyone else",
|
||||
"reject": "Elutasít",
|
||||
"stopEveryonesVideo": "Mindenki videójának leállítása",
|
||||
"stopVideo": "Videó leállítása",
|
||||
"unblockEveryoneMicCamera": "Unblock everyone's mic and camera",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"youtubeTerms": "Condizioni di utilizzo di YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Ammetti",
|
||||
"admitAll": "Ammetti tutti",
|
||||
"allow": "Autorizza",
|
||||
"backToKnockModeButton": "Nessuna password, richiedi l'accesso",
|
||||
"chat": "Conversazione",
|
||||
@@ -595,6 +597,8 @@
|
||||
"notificationTitle": "Sala d'attesa",
|
||||
"passwordField": "Inserisci la password della riunione",
|
||||
"passwordJoinButton": "Entra",
|
||||
"reject": "Respingi",
|
||||
"rejectAll": "Respingi tutti",
|
||||
"title": "Sala d'attesa",
|
||||
"toggleLabel": "Attiva sala d'attesa"
|
||||
},
|
||||
@@ -721,8 +725,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Ammetti",
|
||||
"admitAll": "Ammetti tutti",
|
||||
"allow": "Permetti ai partecipanti di:",
|
||||
"allowVideo": "Autorizza video",
|
||||
"askUnmute": "Chiedi di accendere microfono",
|
||||
@@ -735,7 +737,6 @@
|
||||
"mute": "Silenzia",
|
||||
"muteAll": "Silenzia tutti",
|
||||
"muteEveryoneElse": "Silenzia tutti gli altri",
|
||||
"reject": "Respingi",
|
||||
"stopEveryonesVideo": "Ferma il video di tutti",
|
||||
"stopVideo": "Ferma il video",
|
||||
"unblockEveryoneMicCamera": "Sblocca audio e video a tutti",
|
||||
|
||||
@@ -525,6 +525,8 @@
|
||||
"youtubeTerms": "YouTube サービス利用規約"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "許可",
|
||||
"admitAll": "全員許可",
|
||||
"allow": "許可",
|
||||
"backToKnockModeButton": "参加を依頼",
|
||||
"dialogTitle": "ロビーモード",
|
||||
@@ -555,6 +557,8 @@
|
||||
"notificationTitle": "ロビー",
|
||||
"passwordField": "ミーティングパスワードを入力してください",
|
||||
"passwordJoinButton": "参加",
|
||||
"reject": "却下",
|
||||
"rejectAll": "全員却下",
|
||||
"title": "ロビー",
|
||||
"toggleLabel": "ロビーを有効"
|
||||
},
|
||||
@@ -667,8 +671,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "許可",
|
||||
"admitAll": "全員許可",
|
||||
"allow": "参加者に次のことを許可:",
|
||||
"allowVideo": "ビデオを許可",
|
||||
"askUnmute": "ミュート解除を依頼",
|
||||
@@ -681,7 +683,6 @@
|
||||
"mute": "ミュート",
|
||||
"muteAll": "全員をミュート",
|
||||
"muteEveryoneElse": "他のすべての人をミュート",
|
||||
"reject": "却下",
|
||||
"stopEveryonesVideo": "全員のビデオを停止",
|
||||
"stopVideo": "ビデオを停止",
|
||||
"unblockEveryoneMicCamera": "全員のマイクとビデオのブロックを解除",
|
||||
|
||||
@@ -498,6 +498,8 @@
|
||||
"youtubeTerms": "Tiwtilin n yimeẓla n Youtube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Steεref",
|
||||
"admitAll": "Steεref s kullec",
|
||||
"allow": "Sireg",
|
||||
"backToKnockModeButton": "Ulac awal uffir, suter attekki deg ubdil-is",
|
||||
"dialogTitle": "Askar Lobby",
|
||||
@@ -528,6 +530,8 @@
|
||||
"notificationTitle": "Taxxamt n uraǧu",
|
||||
"passwordField": "Sekcem awal uffir n temlilit",
|
||||
"passwordJoinButton": "Semlil",
|
||||
"reject": "Agi",
|
||||
"rejectAll": "Agi akk",
|
||||
"title": "Taxxamt n uraǧu",
|
||||
"toggleLabel": "Rmed Lobby"
|
||||
},
|
||||
@@ -624,8 +628,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Steεref",
|
||||
"admitAll": "Steεref s kullec",
|
||||
"allow": "Sireg i yimttekkiyen ad:",
|
||||
"allowVideo": "Sireg tavidyut",
|
||||
"askUnmute": "Suter tririt n ṣṣut",
|
||||
@@ -635,7 +637,6 @@
|
||||
"mute": "Asusam",
|
||||
"muteAll": "Sgugem meṛṛa",
|
||||
"muteEveryoneElse": "Sgugem-iten i meṛṛa",
|
||||
"reject": "Agi",
|
||||
"stopEveryonesVideo": "Seḥbes tavidyut n yal yiwen",
|
||||
"stopVideo": "Seḥbes tavidyut n Youtube",
|
||||
"unblockEveryoneMicCamera": "Serreḥ i usawaḍ d tkamiṛat n yal yiwen",
|
||||
|
||||
@@ -642,6 +642,8 @@
|
||||
"youtubeTerms": "YouTube pakalpojumu sniegšanas noteikumi"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Apstiprināt",
|
||||
"admitAll": "Apstiprināt visus",
|
||||
"backToKnockModeButton": "Pajautāt pievienoties",
|
||||
"chat": "Tērzēšana",
|
||||
"dialogTitle": "Vestibila režīms",
|
||||
@@ -675,6 +677,8 @@
|
||||
"notificationLobbyEnabled": "Vestibilu iespējoja {{originParticipantName}}",
|
||||
"notificationTitle": "Vestibils",
|
||||
"passwordJoinButton": "Pievienoties",
|
||||
"reject": "Noraidīt",
|
||||
"rejectAll": "Noraidīt visus",
|
||||
"title": "Vestibils",
|
||||
"toggleLabel": "Iespējot vestibilu"
|
||||
},
|
||||
@@ -812,8 +816,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Apstiprināt",
|
||||
"admitAll": "Apstiprināt visus",
|
||||
"allow": "Atļaut dalībniekiem:",
|
||||
"allowVideo": "Atļaut video",
|
||||
"askUnmute": "Lūgt ieslēgt skaņu",
|
||||
@@ -827,7 +829,6 @@
|
||||
"mute": "Apklusināt",
|
||||
"muteAll": "Apklusināt visus",
|
||||
"muteEveryoneElse": "Apklusināt pārējos",
|
||||
"reject": "Noraidīt",
|
||||
"stopEveryonesVideo": "Izslēgt visiem video",
|
||||
"stopVideo": "Izslēgt video",
|
||||
"unblockEveryoneMicCamera": "Atbloķēt visiem mikrofonu un kameru",
|
||||
|
||||
@@ -464,6 +464,7 @@
|
||||
"notificationTitle": "ലോബി",
|
||||
"passwordField": "മീറ്റിംഗ് പാസ്വേഡ് നൽകുക",
|
||||
"passwordJoinButton": "ചേരുക",
|
||||
"reject": "നിരസിക്കുക",
|
||||
"title": "ലോബി",
|
||||
"toggleLabel": "ലോബി പ്രവർത്തനക്ഷമമാക്കുക"
|
||||
},
|
||||
@@ -538,11 +539,6 @@
|
||||
"suboptimalExperienceTitle": "ബ്രൗസർ മുന്നറിയിപ്പ്",
|
||||
"unmute": "അൺമ്യൂട്ട്"
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"reject": "നിരസിക്കുക"
|
||||
}
|
||||
},
|
||||
"passwordDigitsOnly": "{{number}} അക്കങ്ങൾ വരെ",
|
||||
"passwordSetRemotely": "മറ്റൊരു പങ്കാളി സജ്ജമാക്കിയത്",
|
||||
"poweredby": "powered by",
|
||||
|
||||
@@ -586,6 +586,8 @@
|
||||
"youtubeTerms": "YouTube үйлчилгээний нөхцөл"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Ok",
|
||||
"admitAll": "Бүгдийг зөвшөөр",
|
||||
"backToKnockModeButton": "Зөвшөөрөл хүсэх",
|
||||
"chat": "Зурвас",
|
||||
"dialogTitle": "Лобби горим",
|
||||
@@ -619,6 +621,8 @@
|
||||
"notificationTitle": "Лобби",
|
||||
"passwordField": "Нууц үгээ оруулна уу",
|
||||
"passwordJoinButton": "Оролцох",
|
||||
"reject": "Татгалзах",
|
||||
"rejectAll": "Бүгдийг татгалзуулах",
|
||||
"title": "Лобби",
|
||||
"toggleLabel": "Лобби идэвхижүүлэх"
|
||||
},
|
||||
@@ -751,8 +755,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Ok",
|
||||
"admitAll": "Бүгдийг зөвшөөр",
|
||||
"allow": "Оролцогчийг зөвшөөрөх:",
|
||||
"allowVideo": "Дүрс зөвшөөрөх",
|
||||
"askUnmute": "Дуугаа нээхийг хүсэх",
|
||||
@@ -765,7 +767,6 @@
|
||||
"mute": "Дуугүй болгох",
|
||||
"muteAll": "Бүгдийг дуугүй болгох",
|
||||
"muteEveryoneElse": "Бүгдийг дуугүй болгох",
|
||||
"reject": "Татгалзах",
|
||||
"stopEveryonesVideo": "Бүгдийн дүрсийг хаах",
|
||||
"stopVideo": "Дүрс хаах",
|
||||
"unblockEveryoneMicCamera": "Бүх хүний микрофон, камерыг нээх",
|
||||
|
||||
@@ -486,6 +486,8 @@
|
||||
"youtubeTerms": "Servicevoorwaarden YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Toelaten",
|
||||
"admitAll": "Allen toelaten",
|
||||
"allow": "Toestaan",
|
||||
"backToKnockModeButton": "Geen wachtwoord, vraag om deel te mogen nemen",
|
||||
"dialogTitle": "Lobby-modus",
|
||||
@@ -517,6 +519,8 @@
|
||||
"notificationTitle": "Lobby",
|
||||
"passwordField": "Voer wachtwoord voor vergadering in",
|
||||
"passwordJoinButton": "Deelnemen",
|
||||
"reject": "Afwijzen",
|
||||
"rejectAll": "Allen afwijzen",
|
||||
"title": "Lobby",
|
||||
"toggleLabel": "Lobby inschakelen"
|
||||
},
|
||||
@@ -620,8 +624,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Toelaten",
|
||||
"admitAll": "Allen toelaten",
|
||||
"allow": "Sta deelnemers toe:",
|
||||
"allowVideo": "Video toestaan",
|
||||
"askUnmute": "Vragen om dempen op te heffen",
|
||||
@@ -634,7 +636,6 @@
|
||||
"mute": "Dempen",
|
||||
"muteAll": "Allen dempen",
|
||||
"muteEveryoneElse": "Alle anderen dempen",
|
||||
"reject": "Afwijzen",
|
||||
"stopEveryonesVideo": "Camera's van iedereen uitzetten",
|
||||
"stopVideo": "Camera uitzetten",
|
||||
"unblockEveryoneMicCamera": "Deblokkeer microfoon en camera van allen",
|
||||
|
||||
@@ -524,6 +524,8 @@
|
||||
"youtubeTerms": "Condicions d’utilizacion de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Acceptar",
|
||||
"admitAll": "Tot acceptar",
|
||||
"allow": "Autorizar",
|
||||
"backToKnockModeButton": "Cap de senhal, demandar a participar a la plaça",
|
||||
"dialogTitle": "Mòde sala d'espèra",
|
||||
@@ -554,6 +556,8 @@
|
||||
"notificationTitle": "Sala d'espèra",
|
||||
"passwordField": "Picatz lo senhal de la conferéncia",
|
||||
"passwordJoinButton": "Rejónher",
|
||||
"reject": "Regetar",
|
||||
"rejectAll": "Tot regetar",
|
||||
"title": "Sala d'espèra",
|
||||
"toggleLabel": "Activar la sala d'espèra"
|
||||
},
|
||||
@@ -666,8 +670,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Acceptar",
|
||||
"admitAll": "Tot acceptar",
|
||||
"allow": "Permetre als convidats de :",
|
||||
"allowVideo": "Autorizar la vidèo",
|
||||
"askUnmute": "Demandar a restablir lo son",
|
||||
@@ -680,7 +682,6 @@
|
||||
"mute": "Amudir",
|
||||
"muteAll": "Amudir tot lo monde",
|
||||
"muteEveryoneElse": "Amudir tot los demai",
|
||||
"reject": "Regetar",
|
||||
"stopEveryonesVideo": "Arrestar la vidèo de tot lo monde",
|
||||
"stopVideo": "Arrestar la vidèo",
|
||||
"unblockEveryoneMicCamera": "Desblocar lo microfòn e la camèra de tot lo monde",
|
||||
|
||||
@@ -586,6 +586,8 @@
|
||||
"youtubeTerms": "Warunki użytkowania YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Pozwól",
|
||||
"admitAll": "Pozwól wszystkim",
|
||||
"backToKnockModeButton": "Brak hasła, poproś o dołączenie",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Lobby",
|
||||
@@ -619,6 +621,8 @@
|
||||
"notificationTitle": "Lobby",
|
||||
"passwordField": "Wprowadź hasło",
|
||||
"passwordJoinButton": "Dołącz",
|
||||
"reject": "Odrzuć",
|
||||
"rejectAll": "Odrzuć wszystkich",
|
||||
"title": "Lobby",
|
||||
"toggleLabel": "Włącz / Wyłącz lobby"
|
||||
},
|
||||
@@ -752,8 +756,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Pozwól",
|
||||
"admitAll": "Pozwól wszystkim",
|
||||
"allow": "Zezwól uczestnikom na:",
|
||||
"allowVideo": "Zezwól na wideo",
|
||||
"askUnmute": "Poproś o wyłączenie wyciszenia",
|
||||
@@ -766,7 +768,6 @@
|
||||
"mute": "Wycisz",
|
||||
"muteAll": "Wycisz wszystkich",
|
||||
"muteEveryoneElse": "Wycisz pozostałych",
|
||||
"reject": "Odrzuć",
|
||||
"stopEveryonesVideo": "Wyłącz wszystkie kamery",
|
||||
"stopVideo": "Wyłącz kamerę",
|
||||
"unblockEveryoneMicCamera": "Odblokuj wszystkim kamerę i mikrofon",
|
||||
|
||||
@@ -641,6 +641,8 @@
|
||||
"youtubeTerms": "Termos de serviços do YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Aceitar",
|
||||
"admitAll": "Aceitar todos",
|
||||
"backToKnockModeButton": "Peça para aderir",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Modo sala de espera",
|
||||
@@ -674,6 +676,8 @@
|
||||
"notificationLobbyEnabled": "A sala de espera foi activada por {{originParticipantName}}",
|
||||
"notificationTitle": "Sala de espera",
|
||||
"passwordJoinButton": "Solicitar",
|
||||
"reject": "Rejeitar",
|
||||
"rejectAll": "Rejeitar todos",
|
||||
"title": "Sala de espera",
|
||||
"toggleLabel": "Ativar sala de espera"
|
||||
},
|
||||
@@ -811,8 +815,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Aceitar",
|
||||
"admitAll": "Aceitar todos",
|
||||
"allow": "Permitir aos participantes:",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"askUnmute": "Pedir para ligar o som",
|
||||
@@ -826,7 +828,6 @@
|
||||
"mute": "Silenciar",
|
||||
"muteAll": "Silenciar todos",
|
||||
"muteEveryoneElse": "Silenciar todos os outros",
|
||||
"reject": "Rejeitar",
|
||||
"stopEveryonesVideo": "Desligar a câmara de todos",
|
||||
"stopVideo": "Desligar a câmara",
|
||||
"unblockEveryoneMicCamera": "Desbloquear o microfone e a câmara de todos",
|
||||
|
||||
@@ -642,6 +642,8 @@
|
||||
"youtubeTerms": "Termos de serviços do YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Aceitar",
|
||||
"admitAll": "Aceitar todos",
|
||||
"backToKnockModeButton": "Sem senha, peça para se juntar",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Modo sala de espera",
|
||||
@@ -675,6 +677,8 @@
|
||||
"notificationLobbyEnabled": "Sala de espera foi habilitada por {{originParticipantName}}",
|
||||
"notificationTitle": "Sala de espera",
|
||||
"passwordJoinButton": "Solicitar",
|
||||
"reject": "Rejeitar",
|
||||
"rejectAll": "Rejeitar todos",
|
||||
"title": "Sala de espera",
|
||||
"toggleLabel": "Habilitar sala de espera"
|
||||
},
|
||||
@@ -812,8 +816,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Aceitar",
|
||||
"admitAll": "Aceitar todos",
|
||||
"allow": "Permitir aos participantes:",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"askUnmute": "Pedir para ativar som",
|
||||
@@ -827,7 +829,6 @@
|
||||
"mute": "Silenciar",
|
||||
"muteAll": "Silenciar todos",
|
||||
"muteEveryoneElse": "Silenciar todos os demais",
|
||||
"reject": "Rejeitar",
|
||||
"stopEveryonesVideo": "Parar vídeo de todos",
|
||||
"stopVideo": "Parar vídeo",
|
||||
"unblockEveryoneMicCamera": "Desbloquear microfone e câmera de todos",
|
||||
|
||||
@@ -557,6 +557,8 @@
|
||||
"youtubeTerms": "Условия использования YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Признать",
|
||||
"admitAll": "Признать все",
|
||||
"backToKnockModeButton": "Попросить присоединиться",
|
||||
"chat": "Чат",
|
||||
"dialogTitle": "Режим лобби",
|
||||
@@ -590,6 +592,8 @@
|
||||
"notificationTitle": "Лобби",
|
||||
"passwordField": "Введите пароль встречи",
|
||||
"passwordJoinButton": "Присоединиться",
|
||||
"reject": "Отказать",
|
||||
"rejectAll": "Отказать всем",
|
||||
"title": "Лобби",
|
||||
"toggleLabel": "Включить лобби"
|
||||
},
|
||||
@@ -716,8 +720,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Признать",
|
||||
"admitAll": "Признать все",
|
||||
"allow": "Разрешить",
|
||||
"allowVideo": "Разрешить видео",
|
||||
"askUnmute": "Попросить разрешение включить микрофон",
|
||||
@@ -730,7 +732,6 @@
|
||||
"mute": "Выключить звук",
|
||||
"muteAll": "Выключить звук у всех",
|
||||
"muteEveryoneElse": "Выключить микрофон у остальных",
|
||||
"reject": "Отказать",
|
||||
"stopEveryonesVideo": "Выключить у всех камеру",
|
||||
"stopVideo": "Остановить видео",
|
||||
"unblockEveryoneMicCamera": "Разблокировать у всех микрофон и камеру",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"youtubeTerms": "Cunditziones de servìtziu de YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Ammite",
|
||||
"admitAll": "Ammite totu",
|
||||
"allow": "Permite",
|
||||
"backToKnockModeButton": "Pedi de intrare",
|
||||
"chat": "Tzarrada",
|
||||
@@ -595,6 +597,8 @@
|
||||
"notificationTitle": "Aposentu de abetu",
|
||||
"passwordField": "Inserta sa crae de sa riunione",
|
||||
"passwordJoinButton": "Aderi",
|
||||
"reject": "Refuda",
|
||||
"rejectAll": "Refuda totu",
|
||||
"title": "Aposentu de abetu",
|
||||
"toggleLabel": "Ativa s'aposentu de abetu"
|
||||
},
|
||||
@@ -723,8 +727,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Ammite",
|
||||
"admitAll": "Ammite totu",
|
||||
"allow": "Permite a is partetzipantes:",
|
||||
"allowVideo": "Permite vìdeu",
|
||||
"askUnmute": "Pedi de ativare su micròfonu",
|
||||
@@ -737,7 +739,6 @@
|
||||
"mute": "A sa muda",
|
||||
"muteAll": "Totu a sa muda",
|
||||
"muteEveryoneElse": "Pone totus a sa muda",
|
||||
"reject": "Refuda",
|
||||
"stopEveryonesVideo": "Istuda su vìdeu de totu is partetzipantes",
|
||||
"stopVideo": "Firma su vìdeu",
|
||||
"unblockEveryoneMicCamera": "Isbloca su micròfonu e sa càmera de totu is partetzipantes",
|
||||
|
||||
@@ -466,6 +466,7 @@
|
||||
"notificationTitle": "Čakáreň",
|
||||
"passwordField": "Zadajte heslo do konferencie",
|
||||
"passwordJoinButton": "Vstúpiť",
|
||||
"reject": "Odmietnuť",
|
||||
"title": "Čakáreň",
|
||||
"toggleLabel": "Zapnúť čakáreň"
|
||||
},
|
||||
@@ -540,11 +541,6 @@
|
||||
"suboptimalExperienceTitle": "Prehliadačové varovanie",
|
||||
"unmute": "Zapnúť mikrofón"
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"reject": "Odmietnuť"
|
||||
}
|
||||
},
|
||||
"passwordDigitsOnly": "až {{number}} číslic",
|
||||
"passwordSetRemotely": "nastavené iným účastníkom",
|
||||
"poweredby": "založené na",
|
||||
|
||||
@@ -499,6 +499,8 @@
|
||||
"youtubeTerms": "Pogoji uporabe YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Sprejmi",
|
||||
"admitAll": "Sprejmi vse",
|
||||
"allow": "Dovoli",
|
||||
"backToKnockModeButton": "Prosi za dostop",
|
||||
"dialogTitle": "Način predsobe",
|
||||
@@ -529,6 +531,8 @@
|
||||
"notificationTitle": "Predsoba",
|
||||
"passwordField": "Vnesite geslo sestanka",
|
||||
"passwordJoinButton": "Pridruži se",
|
||||
"reject": "Zavrni",
|
||||
"rejectAll": "Zavrni vse",
|
||||
"title": "Predsoba",
|
||||
"toggleLabel": "Omogoči predsobo"
|
||||
},
|
||||
@@ -625,8 +629,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Sprejmi",
|
||||
"admitAll": "Sprejmi vse",
|
||||
"allow": "Udeleženci si lahko:",
|
||||
"allowVideo": "Dovoli video",
|
||||
"askUnmute": "Prosi za vklop mikrofona",
|
||||
@@ -636,7 +638,6 @@
|
||||
"mute": "Izklopi zvok",
|
||||
"muteAll": "Izklopi zvok vsem",
|
||||
"muteEveryoneElse": "Izklopi zvok vsem ostalim",
|
||||
"reject": "Zavrni",
|
||||
"stopEveryonesVideo": "Izklopi video vsem ostalim",
|
||||
"stopVideo": "Izklopi video",
|
||||
"unblockEveryoneMicCamera": "Dovoli zvok in video vsem udeležencem",
|
||||
|
||||
@@ -582,6 +582,8 @@
|
||||
"youtubeTerms": "Kushte shërbimi YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Pranoje",
|
||||
"admitAll": "Pranoji të tërë",
|
||||
"backToKnockModeButton": "Kërkoji të marrë pjesë",
|
||||
"chat": "Fjalosje",
|
||||
"dialogTitle": "Mënyra holl",
|
||||
@@ -615,6 +617,8 @@
|
||||
"notificationTitle": "Holl",
|
||||
"passwordField": "Jepni fjalëkalim takimi",
|
||||
"passwordJoinButton": "Hyni",
|
||||
"reject": "Hidhe poshtë",
|
||||
"rejectAll": "Hidhi poshtë të tërë",
|
||||
"title": "Holl",
|
||||
"toggleLabel": "Aktivizoni hollin"
|
||||
},
|
||||
@@ -745,8 +749,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Pranoje",
|
||||
"admitAll": "Pranoji të tërë",
|
||||
"allow": "Lejoju pjesëmarrësve të:",
|
||||
"allowVideo": "Çaktivizoni videon",
|
||||
"askUnmute": "Kërkoni heqje heshtimi",
|
||||
@@ -759,7 +761,6 @@
|
||||
"mute": "Heshtoje",
|
||||
"muteAll": "Heshtoji të tërë",
|
||||
"muteEveryoneElse": "Heshto gjithkënd tjetër",
|
||||
"reject": "Hidhe poshtë",
|
||||
"stopEveryonesVideo": "Ndal videon e gjithkujt",
|
||||
"stopVideo": "Ndale videon",
|
||||
"unblockEveryoneMicCamera": "Zhblloko mikrofonin dhe kamerën e gjithkujt",
|
||||
|
||||
@@ -587,6 +587,8 @@
|
||||
"youtubeTerms": "Tjänstevillkor för YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Godkänn",
|
||||
"admitAll": "Godkänn alla",
|
||||
"backToKnockModeButton": "Tillbaka till väntrum",
|
||||
"chat": "Chatt",
|
||||
"dialogTitle": "Väntrum",
|
||||
@@ -620,6 +622,8 @@
|
||||
"notificationTitle": "Väntrum",
|
||||
"passwordField": "Ange möteslösenord",
|
||||
"passwordJoinButton": "Anslut",
|
||||
"reject": "Avvisa",
|
||||
"rejectAll": "Avvisa alla",
|
||||
"title": "Lobby",
|
||||
"toggleLabel": "Aktivera väntrum"
|
||||
},
|
||||
@@ -753,8 +757,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Godkänn",
|
||||
"admitAll": "Godkänn alla",
|
||||
"allow": "Låt deltagarna:",
|
||||
"allowVideo": "Tillåt kamera",
|
||||
"askUnmute": "Be om att aktivera ljud",
|
||||
@@ -767,7 +769,6 @@
|
||||
"mute": "Stäng av ljud",
|
||||
"muteAll": "Stäng av allt ljud",
|
||||
"muteEveryoneElse": "Inaktivera ljud för alla deltagare",
|
||||
"reject": "Avvisa",
|
||||
"stopEveryonesVideo": "Inaktivera allas video",
|
||||
"stopVideo": "Inaktivera video",
|
||||
"unblockEveryoneMicCamera": "Aktivera allas mikrofon och kamera",
|
||||
|
||||
@@ -455,6 +455,8 @@
|
||||
"youtubeTerms": "యూట్యూబ్ సేవా నియమాలు"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "అనుమతించు",
|
||||
"allow": "అనుమతించు",
|
||||
"backToKnockModeButton": "సంకేతపదం లేదు, చేర్చుకోమని అడుగు",
|
||||
"dialogTitle": "Lobby mode",
|
||||
"disableDialogContent": "Lobby mode is currently enabled. This feature ensures that unwanted participants can't join your meeting. Do you want to disable it?",
|
||||
@@ -483,6 +485,7 @@
|
||||
"notificationTitle": "Lobby",
|
||||
"passwordField": "సమావేశం సంకేతపదం ఇవ్వండి",
|
||||
"passwordJoinButton": "చేరు",
|
||||
"reject": "నిరాకరించు",
|
||||
"title": "Lobby",
|
||||
"toggleLabel": "Enable lobby"
|
||||
},
|
||||
@@ -563,11 +566,8 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "అనుమతించు",
|
||||
"allow": "అనుమతించు",
|
||||
"invite": "ప్రజలను ఆహ్వానించు",
|
||||
"muteAll": "అందరినీ మౌనించు",
|
||||
"reject": "నిరాకరించు",
|
||||
"stopVideo": "వీడియో ఆపివేయి"
|
||||
},
|
||||
"headings": {
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"youtubeTerms": "YouTube hizmet şartları"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Kabul et",
|
||||
"admitAll": "Hepsini kabul et",
|
||||
"allow": "İzin ver",
|
||||
"backToKnockModeButton": "Parola yok, bunun yerine katılmayı isteyin",
|
||||
"chat": "Sohbet et",
|
||||
@@ -595,6 +597,8 @@
|
||||
"notificationTitle": "Lobi",
|
||||
"passwordField": "Toplantı parolasını giriniz",
|
||||
"passwordJoinButton": "Katıl",
|
||||
"reject": "Reddet",
|
||||
"rejectAll": "Hepsini reddet",
|
||||
"title": "Lobi",
|
||||
"toggleLabel": "Lobiyi etkinleştir"
|
||||
},
|
||||
@@ -721,8 +725,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Kabul et",
|
||||
"admitAll": "Hepsini kabul et",
|
||||
"allow": "Katılımcıların şunları yapmasına izin ver:",
|
||||
"allowVideo": "Video'ya izin ver",
|
||||
"askUnmute": "Sesi açmayı iste",
|
||||
@@ -735,7 +737,6 @@
|
||||
"mute": "Sessize al",
|
||||
"muteAll": "Herkesi sessize al",
|
||||
"muteEveryoneElse": "Diğer herkesi sessize al",
|
||||
"reject": "Reddet",
|
||||
"stopEveryonesVideo": "Herkesin videosunu durdur",
|
||||
"stopVideo": "Video'yu durdur",
|
||||
"unblockEveryoneMicCamera": "Herkesin mikrofonunun ve kamerasının engellemesini kaldır",
|
||||
|
||||
@@ -584,6 +584,8 @@
|
||||
"youtubeTerms": "Умови надання послуг YouTube"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Допустити",
|
||||
"admitAll": "Допустити всіх",
|
||||
"backToKnockModeButton": "Запитати дозволу",
|
||||
"chat": "Чат",
|
||||
"dialogTitle": "Приймальна",
|
||||
@@ -617,6 +619,8 @@
|
||||
"notificationTitle": "Приймальна",
|
||||
"passwordField": "Ввести пароль зустрічі",
|
||||
"passwordJoinButton": "Приєднатися",
|
||||
"reject": "Відмовити",
|
||||
"rejectAll": "Відмовити всім",
|
||||
"title": "Приймальна",
|
||||
"toggleLabel": "Увімкнути приймальну"
|
||||
},
|
||||
@@ -749,8 +753,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Допустити",
|
||||
"admitAll": "Допустити всіх",
|
||||
"allow": "Дозволити учасникам:",
|
||||
"allowVideo": "Розблокувати камеру",
|
||||
"askUnmute": "Надати слово",
|
||||
@@ -763,7 +765,6 @@
|
||||
"mute": "Вимкнути мікрофон",
|
||||
"muteAll": "Вимкнути мікрофони всім",
|
||||
"muteEveryoneElse": "Вимкнути мікрофони всім іншим",
|
||||
"reject": "Відмовити",
|
||||
"stopEveryonesVideo": "Вимкнути камери всім",
|
||||
"stopVideo": "Вимкнути камеру",
|
||||
"unblockEveryoneMicCamera": "Розблокувати всім мікрофон і камеру",
|
||||
|
||||
@@ -627,6 +627,8 @@
|
||||
"youtubeTerms": "YouTube服务条款"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "同意",
|
||||
"admitAll": "同意全部",
|
||||
"backToKnockModeButton": "请求加入",
|
||||
"chat": "聊天",
|
||||
"dialogTitle": "大厅模式",
|
||||
@@ -660,6 +662,8 @@
|
||||
"notificationTitle": "大厅",
|
||||
"passwordField": "输入会议密码",
|
||||
"passwordJoinButton": "加入",
|
||||
"reject": "拒绝",
|
||||
"rejectAll": "拒绝全部",
|
||||
"title": "大厅",
|
||||
"toggleLabel": "开启大厅模式"
|
||||
},
|
||||
@@ -794,8 +798,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "同意",
|
||||
"admitAll": "同意全部",
|
||||
"allow": "允许参会者:",
|
||||
"allowVideo": "允许视频",
|
||||
"askUnmute": "请求解除静音",
|
||||
@@ -808,7 +810,6 @@
|
||||
"mute": "静音",
|
||||
"muteAll": "全体静音",
|
||||
"muteEveryoneElse": "全体静音",
|
||||
"reject": "拒绝",
|
||||
"stopEveryonesVideo": "禁用所有人视频",
|
||||
"stopVideo": "禁用视频",
|
||||
"unblockEveryoneMicCamera": "允许所有人的麦克风和摄像头",
|
||||
|
||||
@@ -641,6 +641,8 @@
|
||||
"youtubeTerms": "YouTube 服務條款"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "準許",
|
||||
"admitAll": "準許所有人",
|
||||
"backToKnockModeButton": "請求加入",
|
||||
"chat": "聊天",
|
||||
"dialogTitle": "大廳模式",
|
||||
@@ -674,6 +676,8 @@
|
||||
"notificationTitle": "大廳",
|
||||
"passwordField": "輸入會議密碼",
|
||||
"passwordJoinButton": "加入",
|
||||
"reject": "拒絕",
|
||||
"rejectAll": "拒絕所有人",
|
||||
"title": "大廳",
|
||||
"toggleLabel": "啟用大廳模式"
|
||||
},
|
||||
@@ -811,8 +815,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "準許",
|
||||
"admitAll": "準許所有人",
|
||||
"allow": "允許與會者能夠:",
|
||||
"allowVideo": "允許視訊",
|
||||
"askUnmute": "要求解除靜音",
|
||||
@@ -826,7 +828,6 @@
|
||||
"mute": "靜音",
|
||||
"muteAll": "靜音所有人",
|
||||
"muteEveryoneElse": "靜音其他人",
|
||||
"reject": "拒絕",
|
||||
"stopEveryonesVideo": "停用所有人的視訊",
|
||||
"stopVideo": "停用視訊",
|
||||
"unblockEveryoneMicCamera": "解除封鎖所有人的麥克風及網路攝影機",
|
||||
|
||||
@@ -642,6 +642,8 @@
|
||||
"youtubeTerms": "YouTube terms of services"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Admit",
|
||||
"admitAll": "Admit all",
|
||||
"backToKnockModeButton": "Ask to join",
|
||||
"chat": "Chat",
|
||||
"dialogTitle": "Lobby mode",
|
||||
@@ -675,6 +677,8 @@
|
||||
"notificationLobbyEnabled": "Lobby has been enabled by {{originParticipantName}}",
|
||||
"notificationTitle": "Lobby",
|
||||
"passwordJoinButton": "Join",
|
||||
"reject": "Reject",
|
||||
"rejectAll": "Reject all",
|
||||
"title": "Lobby",
|
||||
"toggleLabel": "Enable lobby"
|
||||
},
|
||||
@@ -812,8 +816,6 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Admit",
|
||||
"admitAll": "Admit all",
|
||||
"allow": "Allow attendees to:",
|
||||
"allowVideo": "Allow video",
|
||||
"askUnmute": "Ask to unmute",
|
||||
@@ -827,7 +829,6 @@
|
||||
"mute": "Mute",
|
||||
"muteAll": "Mute all",
|
||||
"muteEveryoneElse": "Mute everyone else",
|
||||
"reject": "Reject",
|
||||
"stopEveryonesVideo": "Stop everyone's video",
|
||||
"stopVideo": "Stop video",
|
||||
"unblockEveryoneMicCamera": "Unblock everyone's mic and camera",
|
||||
@@ -837,8 +838,7 @@
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "Meeting participants ({{count}})",
|
||||
"visitorRequests": " (requests {{count}})",
|
||||
"visitors": "Visitors {{count}}",
|
||||
"visitors": "Visitors ({{count}})",
|
||||
"waitingLobby": "Waiting in lobby ({{count}})"
|
||||
},
|
||||
"search": "Search participants",
|
||||
@@ -1016,15 +1016,12 @@
|
||||
"onlyRecordSelf": "Record only my audio and video streams",
|
||||
"pending": "Preparing to record the meeting...",
|
||||
"rec": "REC",
|
||||
"recordAudioAndVideo": "Record audio and video",
|
||||
"recordTranscription": "Record transcription",
|
||||
"saveLocalRecording": "Save recording file locally (Beta)",
|
||||
"serviceDescription": "Your recording will be saved by the recording service",
|
||||
"serviceDescriptionCloud": "Cloud recording",
|
||||
"serviceDescriptionCloudInfo": "Recorded meetings are automatically cleared 24h after their recording time.",
|
||||
"serviceName": "Recording service",
|
||||
"sessionAlreadyActive": "This session is already being recorded or live streamed.",
|
||||
"showAdvancedOptions": "Advanced options",
|
||||
"signIn": "Sign in",
|
||||
"signOut": "Sign out",
|
||||
"surfaceError": "Please select the current tab.",
|
||||
|
||||
@@ -466,8 +466,8 @@ function initCommands() {
|
||||
'toggle-subtitles': () => {
|
||||
APP.store.dispatch(toggleRequestingSubtitles());
|
||||
},
|
||||
'set-subtitles': (enabled, displaySubtitles, language) => {
|
||||
APP.store.dispatch(setRequestingSubtitles(enabled, displaySubtitles, language));
|
||||
'set-subtitles': enabled => {
|
||||
APP.store.dispatch(setRequestingSubtitles(enabled));
|
||||
},
|
||||
'toggle-tile-view': () => {
|
||||
sendAnalytics(createApiEvent('tile-view.toggled'));
|
||||
@@ -835,6 +835,8 @@ function initCommands() {
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.warn(`Unknown API command received: ${name}`);
|
||||
|
||||
return false;
|
||||
});
|
||||
transport.on('request', (request, callback) => {
|
||||
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -59,7 +59,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/v1750.0.0+ca40744f/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1734.0.0+34ceebd2/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -11860,8 +11860,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1750.0.0+ca40744f/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-A4NdzOU4iYjyxxMiG35NN3vPlf209p+plJpJLpZu2Q3fmjVeu4GM613vr/UzmD+4tYR9XL+sPiFH464uGDjzxA==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1734.0.0+34ceebd2/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-mHWUJ8Q4uhFsx2EZoRhgq8iGgitXig9hZ+uOuHana2YWjj1ZU0GhS5fl7VGr+LxtsonclQBBbGDt8/JkNLcfgg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -27399,8 +27399,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1750.0.0+ca40744f/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-A4NdzOU4iYjyxxMiG35NN3vPlf209p+plJpJLpZu2Q3fmjVeu4GM613vr/UzmD+4tYR9XL+sPiFH464uGDjzxA==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1734.0.0+34ceebd2/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-mHWUJ8Q4uhFsx2EZoRhgq8iGgitXig9hZ+uOuHana2YWjj1ZU0GhS5fl7VGr+LxtsonclQBBbGDt8/JkNLcfgg==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1750.0.0+ca40744f/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1734.0.0+34ceebd2/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
|
||||
@@ -13,12 +13,9 @@ import React, {
|
||||
} from 'react';
|
||||
import { View, ViewStyle } from 'react-native';
|
||||
|
||||
import type { IRoomsInfo } from '../react/features/breakout-rooms/types';
|
||||
|
||||
import { appNavigate } from './react/features/app/actions.native';
|
||||
import { App } from './react/features/app/components/App.native';
|
||||
import { setAudioMuted, setVideoMuted } from './react/features/base/media/actions';
|
||||
import { getRoomsInfo } from './react/features/breakout-rooms/functions';
|
||||
|
||||
|
||||
interface IEventListeners {
|
||||
@@ -31,7 +28,6 @@ interface IEventListeners {
|
||||
onConferenceWillJoin?: Function;
|
||||
onEnterPictureInPicture?: Function;
|
||||
onParticipantJoined?: Function;
|
||||
onParticipantLeft?: ({ id }: { id: string }) => void;
|
||||
onReadyToClose?: Function;
|
||||
}
|
||||
|
||||
@@ -52,17 +48,10 @@ interface IAppProps {
|
||||
userInfo?: IUserInfo;
|
||||
}
|
||||
|
||||
export interface JitsiRefProps {
|
||||
close: Function;
|
||||
setAudioMuted?: (muted: boolean) => void;
|
||||
setVideoMuted?: (muted: boolean) => void;
|
||||
getRoomsInfo?: () => IRoomsInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main React Native SDK component that displays a Jitsi Meet conference and gets all required params as props
|
||||
*/
|
||||
export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) => {
|
||||
export const JitsiMeeting = forwardRef((props: IAppProps, ref) => {
|
||||
const [ appProps, setAppProps ] = useState({});
|
||||
const app = useRef(null);
|
||||
const {
|
||||
@@ -92,11 +81,6 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
|
||||
const dispatch = app.current.state.store.dispatch;
|
||||
|
||||
dispatch(setVideoMuted(muted));
|
||||
},
|
||||
getRoomsInfo: () => {
|
||||
const state = app.current.state.store.getState();
|
||||
|
||||
return getRoomsInfo(state);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -134,7 +118,6 @@ export const JitsiMeeting = forwardRef<JitsiRefProps, IAppProps>((props, ref) =>
|
||||
onConferenceLeft: eventListeners?.onConferenceLeft,
|
||||
onEnterPictureInPicture: eventListeners?.onEnterPictureInPicture,
|
||||
onParticipantJoined: eventListeners?.onParticipantJoined,
|
||||
onParticipantLeft: eventListeners?.onParticipantLeft,
|
||||
onReadyToClose: eventListeners?.onReadyToClose
|
||||
},
|
||||
'url': urlProps,
|
||||
|
||||
@@ -159,9 +159,10 @@ export async function createHandlers({ getState }: IStore) {
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
|
||||
* @param {Array<Object>} handlers - The analytics handlers.
|
||||
* @param {boolean|undefined} willShowPrejoin -
|
||||
* @returns {void}
|
||||
*/
|
||||
export function initAnalytics(store: IStore, handlers: Array<Object>) {
|
||||
export function initAnalytics(store: IStore, handlers: Array<Object>, willShowPrejoin?: boolean) {
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
if (!isAnalyticsEnabled(getState) || handlers.length === 0) {
|
||||
@@ -212,7 +213,7 @@ export function initAnalytics(store: IStore, handlers: Array<Object>) {
|
||||
// Report the tenant from the URL.
|
||||
permanentProperties.tenant = tenant || '/';
|
||||
|
||||
permanentProperties.wasPrejoinDisplayed = isPrejoinPageVisible(state);
|
||||
permanentProperties.wasPrejoinDisplayed = willShowPrejoin ?? isPrejoinPageVisible(state);
|
||||
|
||||
// Currently we don't know if there will be lobby. We will update it to true if we go through lobby.
|
||||
permanentProperties.wasLobbyVisible = false;
|
||||
|
||||
@@ -114,7 +114,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
const result = next(action);
|
||||
|
||||
createHandlersPromise.then(handlers => {
|
||||
initAnalytics(store, handlers);
|
||||
initAnalytics(store, handlers, action.willShowPrejoin);
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
@@ -150,9 +150,20 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
let willShowPrejoin = false;
|
||||
let willShowUnsafeRoomWarning = false;
|
||||
|
||||
if (!options.hidePrejoin && isPrejoinPageEnabled(getState()) && room) {
|
||||
if (isUnsafeRoomWarningEnabled(getState()) && isInsecureRoomName(room)) {
|
||||
willShowUnsafeRoomWarning = true;
|
||||
} else {
|
||||
willShowPrejoin = true;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(setLocationURL(locationURL));
|
||||
dispatch(setConfig(config));
|
||||
dispatch(setRoom(room));
|
||||
dispatch(setRoom(room, willShowPrejoin));
|
||||
|
||||
if (!room) {
|
||||
goBackToRoot(getState(), dispatch);
|
||||
@@ -163,12 +174,10 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
|
||||
dispatch(createDesiredLocalTracks());
|
||||
dispatch(clearNotifications());
|
||||
|
||||
if (!options.hidePrejoin && isPrejoinPageEnabled(getState())) {
|
||||
if (isUnsafeRoomWarningEnabled(getState()) && isInsecureRoomName(room)) {
|
||||
navigateRoot(screen.unsafeRoomWarning);
|
||||
} else {
|
||||
navigateRoot(screen.preJoin);
|
||||
}
|
||||
if (willShowUnsafeRoomWarning) {
|
||||
navigateRoot(screen.unsafeRoomWarning);
|
||||
} else if (willShowPrejoin) {
|
||||
navigateRoot(screen.preJoin);
|
||||
} else {
|
||||
dispatch(connect());
|
||||
navigateRoot(screen.conference.root);
|
||||
|
||||
@@ -900,15 +900,20 @@ export function setObfuscatedRoom(obfuscatedRoom: string, obfuscatedRoomSource:
|
||||
*
|
||||
* @param {(string|undefined)} room - The name of the room of the conference to
|
||||
* be joined.
|
||||
* @param {boolean|undefined} willShowPrejoin - Whether the prejoin should be hidden or not.
|
||||
* NOTE: This argument is used only for mobile currently!
|
||||
*
|
||||
* @returns {{
|
||||
* type: SET_ROOM,
|
||||
* room: string
|
||||
* room: string,
|
||||
* willShowPrejoin: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setRoom(room?: string) {
|
||||
export function setRoom(room?: string, willShowPrejoin?: boolean) {
|
||||
return {
|
||||
type: SET_ROOM,
|
||||
room
|
||||
room,
|
||||
willShowPrejoin
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -583,7 +583,7 @@ export interface IConfig {
|
||||
transcribeWithAppLanguage?: boolean;
|
||||
transcribingEnabled?: boolean;
|
||||
transcription?: {
|
||||
autoTranscribeOnRecord?: boolean;
|
||||
autoCaptionOnRecord?: boolean;
|
||||
disableStartForAll?: boolean;
|
||||
enabled?: boolean;
|
||||
preferredLanguage?: string;
|
||||
|
||||
@@ -465,7 +465,7 @@ function _translateLegacyConfig(oldValue: IConfig) {
|
||||
if (oldValue.autoCaptionOnRecord !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
autoTranscribeOnRecord: oldValue.autoCaptionOnRecord
|
||||
autoCaptionOnRecord: oldValue.autoCaptionOnRecord
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,14 @@ export const LANGUAGES: Array<string> = Object.keys(LANGUAGES_RESOURCES);
|
||||
*/
|
||||
export const TRANSLATION_LANGUAGES: Array<string> = Object.keys(TRANSLATION_LANGUAGES_RESOURCES);
|
||||
|
||||
/**
|
||||
* The available/supported translation languages head. (Languages displayed on the top ).
|
||||
*
|
||||
* @public
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const TRANSLATION_LANGUAGES_HEAD: Array<string> = [ 'en' ];
|
||||
|
||||
/**
|
||||
* The default language.
|
||||
*
|
||||
@@ -50,14 +58,6 @@ export const TRANSLATION_LANGUAGES: Array<string> = Object.keys(TRANSLATION_LANG
|
||||
*/
|
||||
export const DEFAULT_LANGUAGE = 'en';
|
||||
|
||||
/**
|
||||
* The available/supported translation languages head. (Languages displayed on the top ).
|
||||
*
|
||||
* @public
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const TRANSLATION_LANGUAGES_HEAD: Array<string> = [ DEFAULT_LANGUAGE ];
|
||||
|
||||
/**
|
||||
* The options to initialize i18next with.
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ import { PARTICIPANT_LEFT } from '../participants/actionTypes';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
|
||||
import JitsiMeetJS from './_';
|
||||
import { LIB_WILL_INIT } from './actionTypes';
|
||||
import { disposeLib, initLib } from './actions';
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,14 @@ import { disposeLib, initLib } from './actions';
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case LIB_WILL_INIT:
|
||||
// Moved from conference.js init method. It appears the error handlers
|
||||
// are not used for mobile.
|
||||
if (typeof APP !== 'undefined') {
|
||||
_setErrorHandlers();
|
||||
}
|
||||
break;
|
||||
|
||||
case SET_NETWORK_INFO:
|
||||
JitsiMeetJS.setNetworkInfo({
|
||||
isOnline: action.isOnline
|
||||
@@ -72,3 +81,47 @@ function _setConfig({ dispatch, getState }: IStore, next: Function, action: AnyA
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches our custom error handlers to the window object.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function _setErrorHandlers() {
|
||||
// attaches global error handler, if there is already one, respect it
|
||||
if (JitsiMeetJS.getGlobalOnErrorHandler) {
|
||||
const oldOnErrorHandler = window.onerror;
|
||||
|
||||
// TODO: Don't remove this ignore. The build fails on macOS and we don't know yet why.
|
||||
|
||||
// @ts-ignore
|
||||
window.onerror = (message, source, lineno, colno, error) => { // eslint-disable-line max-params
|
||||
const errMsg = message || error?.message;
|
||||
const stack = error?.stack;
|
||||
|
||||
JitsiMeetJS.getGlobalOnErrorHandler(errMsg, source, lineno, colno, stack);
|
||||
|
||||
if (oldOnErrorHandler) {
|
||||
oldOnErrorHandler(message, source, lineno, colno, error);
|
||||
}
|
||||
};
|
||||
|
||||
const oldOnUnhandledRejection = window.onunhandledrejection;
|
||||
|
||||
window.onunhandledrejection = function(event) {
|
||||
let message = event.reason;
|
||||
let stack: string | undefined = 'n/a';
|
||||
|
||||
if (event.reason instanceof Error) {
|
||||
({ message, stack } = event.reason);
|
||||
}
|
||||
|
||||
JitsiMeetJS.getGlobalOnErrorHandler(message, null, null, null, stack);
|
||||
|
||||
if (oldOnUnhandledRejection) {
|
||||
// @ts-ignore
|
||||
oldOnUnhandledRejection(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Sound from 'react-native-sound';
|
||||
|
||||
import logger from '../../logger';
|
||||
import AbstractAudio, { IProps } from '../AbstractAudio';
|
||||
import AbstractAudio from '../AbstractAudio';
|
||||
|
||||
/**
|
||||
* The React Native/mobile {@link Component} which is similar to Web's
|
||||
@@ -31,25 +31,12 @@ export default class Audio extends AbstractAudio {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidUpdate()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
async componentDidUpdate(prevProps: IProps): Promise<void> {
|
||||
// source is different !! call didunmount and call didmount
|
||||
if (prevProps.src !== this.props.src) {
|
||||
await this.componentWillUnmount();
|
||||
await this.componentDidMount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will load the sound, after the component did mount.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
async componentDidMount() {
|
||||
componentDidMount() {
|
||||
this._sound
|
||||
= this.props.src
|
||||
? new Sound(
|
||||
@@ -63,7 +50,7 @@ export default class Audio extends AbstractAudio {
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
async componentWillUnmount() {
|
||||
componentWillUnmount() {
|
||||
if (this._sound) {
|
||||
this._sound.release();
|
||||
this._sound = null;
|
||||
|
||||
@@ -38,15 +38,6 @@ export const SCREENSHARE_MUTISM_AUTHORITY = {
|
||||
USER: 1 << 2
|
||||
};
|
||||
|
||||
/**
|
||||
* The languages supported for audio files.
|
||||
*/
|
||||
export enum AudioSupportedLanguage {
|
||||
en = 'en',
|
||||
fr = 'fr',
|
||||
frCA = 'frCA'
|
||||
}
|
||||
|
||||
/**
|
||||
* The types of authorities which may mute/unmute the local video.
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@ import { IStateful } from '../app/types';
|
||||
import { toState } from '../redux/functions';
|
||||
import { getPropertyValue } from '../settings/functions';
|
||||
|
||||
import { AudioSupportedLanguage, VIDEO_MUTISM_AUTHORITY } from './constants';
|
||||
import { VIDEO_MUTISM_AUTHORITY } from './constants';
|
||||
|
||||
|
||||
// XXX The configurations/preferences/settings startWithAudioMuted and startWithVideoMuted were introduced for
|
||||
@@ -127,20 +127,3 @@ export function shouldRenderVideoTrack(
|
||||
&& !videoTrack.muted
|
||||
&& (!waitForVideoStarted || videoTrack.videoStarted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the localized sound file source.
|
||||
*
|
||||
* @param {string} file - The default file source.
|
||||
* @param {string} language - The language to use for localization.
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getSoundFileSrc = (file: string, language: string): string => {
|
||||
if (!AudioSupportedLanguage[language as keyof typeof AudioSupportedLanguage]
|
||||
|| language === AudioSupportedLanguage.en) {
|
||||
return file;
|
||||
}
|
||||
const fileTokens = file.split('.');
|
||||
|
||||
return `${fileTokens[0]}_${language}.${fileTokens[1]}`;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import i18next from 'i18next';
|
||||
|
||||
import { registerE2eeAudioFiles } from '../../../features/e2ee/functions';
|
||||
import { registerRecordingAudioFiles } from '../../../features/recording/functions';
|
||||
import { IStore } from '../../app/types';
|
||||
import { AudioSupportedLanguage } from '../media/constants';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../redux/StateListenerRegistry';
|
||||
|
||||
import { PLAY_SOUND, STOP_SOUND } from './actionTypes';
|
||||
import logger from './logger';
|
||||
@@ -77,53 +71,3 @@ function _stopSound({ getState }: IStore, soundId: string) {
|
||||
logger.warn(`STOP_SOUND: no sound found for id: ${soundId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the language is supported for audio messages.
|
||||
*
|
||||
* @param {string} language - The requested language.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isLanguageSupported(language: string): Boolean {
|
||||
return Boolean(AudioSupportedLanguage[language as keyof typeof AudioSupportedLanguage]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking if it's necessary to reload the translated files.
|
||||
*
|
||||
* @param {string} language - The next language.
|
||||
* @param {string} prevLanguage - The previous language.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function shouldReloadAudioFiles(language: string, prevLanguage: string): Boolean {
|
||||
const isNextLanguageSupported = isLanguageSupported(language);
|
||||
const isPrevLanguageSupported = isLanguageSupported(prevLanguage);
|
||||
|
||||
return (
|
||||
|
||||
// From an unsupported language (which defaulted to English) to a supported language (that isn't English).
|
||||
isNextLanguageSupported && language !== AudioSupportedLanguage.en && !isPrevLanguageSupported
|
||||
) || (
|
||||
|
||||
// From a supported language (that wasn't English) to English.
|
||||
!isNextLanguageSupported && isPrevLanguageSupported && prevLanguage !== AudioSupportedLanguage.en
|
||||
) || (
|
||||
|
||||
// From a supported language to another.
|
||||
isNextLanguageSupported && isPrevLanguageSupported
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up state change listener for language.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
() => i18next.language,
|
||||
(language, { dispatch }, prevLanguage): void => {
|
||||
|
||||
if (language !== prevLanguage && shouldReloadAudioFiles(language, prevLanguage)) {
|
||||
registerE2eeAudioFiles(dispatch, true);
|
||||
registerRecordingAudioFiles(dispatch, true);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
import i18next from 'i18next';
|
||||
|
||||
import { IReduxState, IStore } from '../app/types';
|
||||
import { IReduxState } from '../app/types';
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { getSoundFileSrc } from '../base/media/functions';
|
||||
import { getParticipantById, getParticipantCount, getParticipantCountWithFake } from '../base/participants/functions';
|
||||
import { toState } from '../base/redux/functions';
|
||||
import { registerSound, unregisterSound } from '../base/sounds/actions';
|
||||
|
||||
import {
|
||||
E2EE_OFF_SOUND_ID,
|
||||
E2EE_ON_SOUND_ID,
|
||||
MAX_MODE_LIMIT,
|
||||
MAX_MODE_THRESHOLD
|
||||
} from './constants';
|
||||
import {
|
||||
E2EE_OFF_SOUND_FILE,
|
||||
E2EE_ON_SOUND_FILE
|
||||
} from './sounds';
|
||||
|
||||
import { MAX_MODE_LIMIT, MAX_MODE_THRESHOLD } from './constants';
|
||||
|
||||
/**
|
||||
* Gets the value of a specific React {@code Component} prop of the currently
|
||||
@@ -86,35 +73,3 @@ export function displayVerification(state: IReduxState, pId: string) {
|
||||
&& participant?.e2eeVerificationAvailable
|
||||
&& participant?.e2eeVerified === undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the audio files based on locale.
|
||||
*
|
||||
* @param {Dispatch<any>} dispatch - The redux dispatch function.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function unregisterE2eeAudioFiles(dispatch: IStore['dispatch']) {
|
||||
dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
|
||||
dispatch(unregisterSound(E2EE_ON_SOUND_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the audio files based on locale.
|
||||
*
|
||||
* @param {Dispatch<any>} dispatch - The redux dispatch function.
|
||||
* @param {boolean|undefined} shouldUnregister - Whether the sounds should be unregistered.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function registerE2eeAudioFiles(dispatch: IStore['dispatch'], shouldUnregister?: boolean) {
|
||||
const language = i18next.language;
|
||||
|
||||
shouldUnregister && unregisterE2eeAudioFiles(dispatch);
|
||||
|
||||
dispatch(registerSound(
|
||||
E2EE_OFF_SOUND_ID,
|
||||
getSoundFileSrc(E2EE_OFF_SOUND_FILE, language)));
|
||||
|
||||
dispatch(registerSound(
|
||||
E2EE_ON_SOUND_ID,
|
||||
getSoundFileSrc(E2EE_ON_SOUND_FILE, language)));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import { IStore } from '../app/types';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
|
||||
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
|
||||
@@ -13,19 +14,16 @@ import {
|
||||
} from '../base/participants/functions';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import { playSound } from '../base/sounds/actions';
|
||||
import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
|
||||
|
||||
import { PARTICIPANT_VERIFIED, SET_MEDIA_ENCRYPTION_KEY, START_VERIFICATION, TOGGLE_E2EE } from './actionTypes';
|
||||
import { setE2EEMaxMode, toggleE2EE } from './actions';
|
||||
import ParticipantVerificationDialog from './components/ParticipantVerificationDialog';
|
||||
import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID, MAX_MODE } from './constants';
|
||||
import {
|
||||
isMaxModeReached,
|
||||
isMaxModeThresholdReached,
|
||||
registerE2eeAudioFiles,
|
||||
unregisterE2eeAudioFiles
|
||||
} from './functions';
|
||||
import { isMaxModeReached, isMaxModeThresholdReached } from './functions';
|
||||
import logger from './logger';
|
||||
import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
|
||||
|
||||
|
||||
/**
|
||||
* Middleware that captures actions related to E2EE.
|
||||
@@ -38,11 +36,18 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT:
|
||||
registerE2eeAudioFiles(dispatch);
|
||||
dispatch(registerSound(
|
||||
E2EE_OFF_SOUND_ID,
|
||||
E2EE_OFF_SOUND_FILE));
|
||||
|
||||
dispatch(registerSound(
|
||||
E2EE_ON_SOUND_ID,
|
||||
E2EE_ON_SOUND_FILE));
|
||||
break;
|
||||
|
||||
case APP_WILL_UNMOUNT:
|
||||
unregisterE2eeAudioFiles(dispatch);
|
||||
dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
|
||||
dispatch(unregisterSound(E2EE_ON_SOUND_ID));
|
||||
break;
|
||||
|
||||
case CONFERENCE_JOINED:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// @ts-expect-error
|
||||
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
|
||||
import { IStore } from '../app/types';
|
||||
import { getParticipantById } from '../base/participants/functions';
|
||||
import { getVideoTrackByParticipant } from '../base/tracks/functions.web';
|
||||
import { MEDIA_TYPE } from '../base/media/constants';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../base/tracks/functions.web';
|
||||
|
||||
import { SET_SEE_WHAT_IS_BEING_SHARED } from './actionTypes';
|
||||
|
||||
@@ -19,12 +19,11 @@ export function captureLargeVideoScreenshot() {
|
||||
const largeVideo = state['features/large-video'];
|
||||
const promise = Promise.resolve();
|
||||
|
||||
if (!largeVideo?.participantId) {
|
||||
if (!largeVideo) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
const participant = getParticipantById(state, largeVideo.participantId);
|
||||
const participantTrack = getVideoTrackByParticipant(state, participant);
|
||||
const tracks = state['features/base/tracks'];
|
||||
const participantTrack = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, largeVideo.participantId);
|
||||
|
||||
// Participants that join the call video muted do not have a jitsiTrack attached.
|
||||
if (!participantTrack?.jitsiTrack) {
|
||||
|
||||
@@ -207,7 +207,7 @@ function _handleLobbyNotification(store: IStore) {
|
||||
descriptionKey = 'notify.participantWantsToJoin';
|
||||
notificationTitle = firstParticipant.name;
|
||||
icon = NOTIFICATION_ICON.PARTICIPANT;
|
||||
customActionNameKey = [ 'participantsPane.actions.admit', 'participantsPane.actions.reject' ];
|
||||
customActionNameKey = [ 'lobby.admit', 'lobby.reject' ];
|
||||
customActionType = [ BUTTON_TYPES.PRIMARY, BUTTON_TYPES.DESTRUCTIVE ];
|
||||
customActionHandler = [ () => batch(() => {
|
||||
dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
|
||||
|
||||
@@ -154,11 +154,10 @@ function _conferenceFailed({ getState }: IStore, next: Function, action: AnyActi
|
||||
// prevented the user from joining a specific conference but the app may be
|
||||
// able to eventually join the conference.
|
||||
if (!action.error.recoverable) {
|
||||
const { callUUID } = action.conference;
|
||||
|
||||
if (callUUID) {
|
||||
if (action?.conference?.callUUID) {
|
||||
delete action.conference.callUUID;
|
||||
CallIntegration.reportCallFailed(callUUID);
|
||||
CallIntegration.reportCallFailed(action.conference.callUUIDID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +184,9 @@ function _conferenceJoined({ getState }: IStore, next: Function, action: AnyActi
|
||||
return result;
|
||||
}
|
||||
|
||||
const { callUUID } = action.conference;
|
||||
if (action?.conference?.callUUID) {
|
||||
const { callUUID } = action.conference;
|
||||
|
||||
if (callUUID) {
|
||||
CallIntegration.reportConnectedOutgoingCall(callUUID)
|
||||
.then(() => {
|
||||
// iOS 13 doesn't like the mute state to be false before the call is started
|
||||
@@ -230,11 +229,9 @@ function _conferenceLeft({ getState }: IStore, next: Function, action: AnyAction
|
||||
return result;
|
||||
}
|
||||
|
||||
const { callUUID } = action.conference;
|
||||
|
||||
if (callUUID) {
|
||||
if (action?.conference?.callUUID) {
|
||||
delete action.conference.callUUID;
|
||||
CallIntegration.endCall(callUUID);
|
||||
CallIntegration.endCall(action.conference.callUUID);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -273,7 +270,7 @@ function _conferenceWillJoin({ dispatch, getState }: IStore, next: Function, act
|
||||
}
|
||||
|
||||
// When assigning the call UUID, do so in upper case, since iOS will return
|
||||
// it upper-cased.
|
||||
// it upper cased.
|
||||
conference.callUUID = (callUUID || uuidv4()).toUpperCase();
|
||||
|
||||
CallIntegration.startCall(conference.callUUID, handle, hasVideo)
|
||||
|
||||
@@ -379,10 +379,9 @@ function _registerForNativeEvents(store: IStore) {
|
||||
dispatch(sendMessage(message));
|
||||
});
|
||||
|
||||
eventEmitter.addListener(ExternalAPI.SET_CLOSED_CAPTIONS_ENABLED,
|
||||
({ enabled, displaySubtitles, language }: any) => {
|
||||
dispatch(setRequestingSubtitles(enabled, displaySubtitles, language));
|
||||
});
|
||||
eventEmitter.addListener(ExternalAPI.SET_CLOSED_CAPTIONS_ENABLED, ({ enabled }: any) => {
|
||||
dispatch(setRequestingSubtitles(enabled));
|
||||
});
|
||||
|
||||
eventEmitter.addListener(ExternalAPI.TOGGLE_CAMERA, () => {
|
||||
dispatch(toggleCameraFacingMode());
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
CONFERENCE_WILL_JOIN
|
||||
} from '../../base/conference/actionTypes';
|
||||
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../../base/media/actionTypes';
|
||||
import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../../base/participants/actionTypes';
|
||||
import { PARTICIPANT_JOINED } from '../../base/participants/actionTypes';
|
||||
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../../base/redux/StateListenerRegistry';
|
||||
import { READY_TO_CLOSE } from '../external-api/actionTypes';
|
||||
@@ -63,14 +63,6 @@ const { JMOngoingConference } = NativeModules;
|
||||
rnSdkHandlers?.onParticipantJoined && rnSdkHandlers?.onParticipantJoined(participantInfo);
|
||||
break;
|
||||
}
|
||||
case PARTICIPANT_LEFT: {
|
||||
const { participant } = action;
|
||||
|
||||
const { id } = participant ?? {};
|
||||
|
||||
rnSdkHandlers?.onParticipantLeft && rnSdkHandlers?.onParticipantLeft({ id });
|
||||
break;
|
||||
}
|
||||
case READY_TO_CLOSE:
|
||||
rnSdkHandlers?.onReadyToClose && rnSdkHandlers?.onReadyToClose();
|
||||
break;
|
||||
|
||||
@@ -58,7 +58,7 @@ const ContextMenuLobbyParticipantReject = ({ participant: p }: IProps) => {
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconCloseLarge } />
|
||||
<Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.reject') }</Text>
|
||||
<Text style = { styles.contextMenuItemText }>{ t('lobby.reject') }</Text>
|
||||
</TouchableOpacity>
|
||||
</BottomSheet>
|
||||
);
|
||||
|
||||
@@ -19,8 +19,8 @@ interface IProps {
|
||||
|
||||
export const LobbyParticipantItem = ({ participant: p }: IProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const admit = useCallback(() => dispatch(setKnockingParticipantApproval(p.id, true)), [ dispatch, p.id ]);
|
||||
const reject = useCallback(() => dispatch(setKnockingParticipantApproval(p.id, false)), [ dispatch, p.id ]);
|
||||
const admit = useCallback(() => dispatch(setKnockingParticipantApproval(p.id, true)), [ dispatch ]);
|
||||
const reject = useCallback(() => dispatch(setKnockingParticipantApproval(p.id, false)), [ dispatch ]);
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
@@ -29,16 +29,16 @@ export const LobbyParticipantItem = ({ participant: p }: IProps) => {
|
||||
key = { p.id }
|
||||
participantID = { p.id } >
|
||||
<Button
|
||||
accessibilityLabel = 'participantsPane.actions.reject'
|
||||
labelKey = 'participantsPane.actions.reject'
|
||||
accessibilityLabel = 'lobby.reject'
|
||||
labelKey = 'lobby.reject'
|
||||
onClick = { reject }
|
||||
style = { styles.buttonReject }
|
||||
style = { styles.lobbyButtonReject }
|
||||
type = { BUTTON_TYPES.DESTRUCTIVE } />
|
||||
<Button
|
||||
accessibilityLabel = 'participantsPane.actions.admit'
|
||||
labelKey = 'participantsPane.actions.admit'
|
||||
accessibilityLabel = 'lobby.admit'
|
||||
labelKey = 'lobby.admit'
|
||||
onClick = { admit }
|
||||
style = { styles.buttonAdmit }
|
||||
style = { styles.lobbyButtonAdmit }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</ParticipantItem>
|
||||
);
|
||||
|
||||
@@ -28,15 +28,15 @@ const LobbyParticipantList = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style = { styles.listDetails as ViewStyle } >
|
||||
<View style = { styles.lobbyListDetails as ViewStyle } >
|
||||
<Text style = { styles.lobbyListDescription as TextStyle }>
|
||||
{ title }
|
||||
</Text>
|
||||
{
|
||||
participants.length > 1 && (
|
||||
<Button
|
||||
accessibilityLabel = 'participantsPane.actions.admitAll'
|
||||
labelKey = 'participantsPane.actions.admitAll'
|
||||
accessibilityLabel = 'lobby.admitAll'
|
||||
labelKey = 'lobby.admitAll'
|
||||
mode = { BUTTON_MODES.TEXT }
|
||||
onClick = { admitAll }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
|
||||
@@ -69,9 +69,19 @@ const MeetingParticipantList = () => {
|
||||
: t('participantsPane.headings.participantsList',
|
||||
{ count: participantsCount });
|
||||
const { color, shareDialogVisible } = inviteOthersControl;
|
||||
const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0);
|
||||
const visitorsLabelText = visitorsCount > 0
|
||||
? t('participantsPane.headings.visitors', { count: visitorsCount })
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<View style = { styles.meetingListContainer }>
|
||||
{
|
||||
visitorsCount > 0
|
||||
&& <Text style = { styles.visitorsLabel }>
|
||||
{ visitorsLabelText }
|
||||
</Text>
|
||||
}
|
||||
<Text
|
||||
style = { styles.meetingListDescription as TextStyle }>
|
||||
{ title }
|
||||
|
||||
@@ -8,7 +8,6 @@ import { isLocalParticipantModerator } from '../../../base/participants/function
|
||||
import LobbyParticipantList from './LobbyParticipantList';
|
||||
import MeetingParticipantList from './MeetingParticipantList';
|
||||
import ParticipantsPaneFooter from './ParticipantsPaneFooter';
|
||||
import VisitorsList from './VisitorsList';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
@@ -33,7 +32,6 @@ const ParticipantsPane = () => {
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
ListHeaderComponent = { () => (
|
||||
<>
|
||||
<VisitorsList />
|
||||
<LobbyParticipantList />
|
||||
<MeetingParticipantList />
|
||||
</>
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { approveRequest, denyRequest } from '../../../visitors/actions';
|
||||
import { IPromotionRequest } from '../../../visitors/types';
|
||||
|
||||
import ParticipantItem from './ParticipantItem';
|
||||
import styles from './styles';
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Promotion request reference.
|
||||
*/
|
||||
request: IPromotionRequest;
|
||||
}
|
||||
|
||||
export const VisitorsItem = ({ request: r }: IProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const admit = useCallback(() => dispatch(approveRequest(r)), [ dispatch, r ]);
|
||||
const reject = useCallback(() => dispatch(denyRequest(r)), [ dispatch, r ]);
|
||||
const { from, nick } = r;
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
displayName = { nick ?? '' }
|
||||
isKnockingParticipant = { true }
|
||||
key = { from }
|
||||
participantID = { from } >
|
||||
<Button
|
||||
accessibilityLabel = 'participantsPane.actions.reject'
|
||||
labelKey = 'participantsPane.actions.reject'
|
||||
onClick = { reject }
|
||||
style = { styles.buttonReject }
|
||||
type = { BUTTON_TYPES.DESTRUCTIVE } />
|
||||
<Button
|
||||
accessibilityLabel = 'participantsPane.actions.admit'
|
||||
labelKey = 'participantsPane.actions.admit'
|
||||
onClick = { admit }
|
||||
style = { styles.buttonAdmit }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</ParticipantItem>
|
||||
);
|
||||
};
|
||||
@@ -1,65 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_MODES, BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { admitMultiple } from '../../../visitors/actions';
|
||||
import { getPromotionRequests } from '../../../visitors/functions';
|
||||
|
||||
import { VisitorsItem } from './VisitorsItem';
|
||||
import styles from './styles';
|
||||
|
||||
const VisitorsList = () => {
|
||||
const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const requests = useSelector(getPromotionRequests);
|
||||
|
||||
const admitAll = useCallback(() => {
|
||||
dispatch(admitMultiple(requests));
|
||||
}, [ dispatch, requests ]);
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (visitorsCount <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let title = t('participantsPane.headings.visitors', { count: visitorsCount });
|
||||
|
||||
if (requests.length > 0) {
|
||||
title += t('participantsPane.headings.visitorRequests', { count: requests.length });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style = { styles.listDetails as ViewStyle } >
|
||||
<Text style = { styles.visitorsLabel }>
|
||||
{ title }
|
||||
</Text>
|
||||
{
|
||||
requests.length > 1 && (
|
||||
<Button
|
||||
accessibilityLabel = 'participantsPane.actions.admitAll'
|
||||
labelKey = 'participantsPane.actions.admitAll'
|
||||
mode = { BUTTON_MODES.TEXT }
|
||||
onClick = { admitAll }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
)
|
||||
}
|
||||
</View>
|
||||
{
|
||||
requests.map(r => (
|
||||
<VisitorsItem
|
||||
key = { r.from }
|
||||
request = { r } />)
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VisitorsList;
|
||||
@@ -164,12 +164,12 @@ export default {
|
||||
color: BaseTheme.palette.uiBackground
|
||||
},
|
||||
|
||||
buttonAdmit: {
|
||||
lobbyButtonAdmit: {
|
||||
position: 'absolute',
|
||||
right: 16
|
||||
},
|
||||
|
||||
buttonReject: {
|
||||
lobbyButtonReject: {
|
||||
position: 'absolute',
|
||||
right: 112
|
||||
},
|
||||
@@ -178,7 +178,7 @@ export default {
|
||||
...participantListDescription
|
||||
},
|
||||
|
||||
listDetails: {
|
||||
lobbyListDetails: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -72,9 +72,9 @@ export const LobbyParticipantItem = ({
|
||||
|
||||
const renderAdmitButton = () => (
|
||||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.admit')} ${p.name}` }
|
||||
accessibilityLabel = { `${t('lobby.admit')} ${p.name}` }
|
||||
className = { styles.button }
|
||||
labelKey = { 'participantsPane.actions.admit' }
|
||||
labelKey = { 'lobby.admit' }
|
||||
onClick = { admit }
|
||||
size = 'small'
|
||||
testId = { `admit-${id}` } />);
|
||||
@@ -116,18 +116,18 @@ export const LobbyParticipantItem = ({
|
||||
} ] } />
|
||||
<ContextMenuItemGroup
|
||||
actions = { [ {
|
||||
accessibilityLabel: `${t('participantsPane.actions.reject')} ${p.name}`,
|
||||
accessibilityLabel: `${t('lobby.reject')} ${p.name}`,
|
||||
onClick: reject,
|
||||
testId: `reject-${id}`,
|
||||
icon: IconUserDeleted,
|
||||
text: t('participantsPane.actions.reject')
|
||||
text: t('lobby.reject')
|
||||
} ] } />
|
||||
</ContextMenu>
|
||||
</> : <>
|
||||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.reject')} ${p.name}` }
|
||||
accessibilityLabel = { `${t('lobby.reject')} ${p.name}` }
|
||||
className = { styles.button }
|
||||
labelKey = { 'participantsPane.actions.reject' }
|
||||
labelKey = { 'lobby.reject' }
|
||||
onClick = { reject }
|
||||
size = 'small'
|
||||
testId = { `reject-${id}` }
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function LobbyParticipants() {
|
||||
participants.length > 1
|
||||
&& <div
|
||||
className = { classes.link }
|
||||
onClick = { admitAll }>{t('participantsPane.actions.admitAll')}</div>
|
||||
onClick = { admitAll }>{t('lobby.admitAll')}</div>
|
||||
}
|
||||
</div>
|
||||
<LobbyParticipantItems
|
||||
@@ -117,7 +117,7 @@ export default function LobbyParticipants() {
|
||||
className = { classes.icon }
|
||||
size = { 20 }
|
||||
src = { IconCheck } />
|
||||
<span>{ t('participantsPane.actions.admit') }</span>
|
||||
<span>{ t('lobby.admit') }</span>
|
||||
</li>
|
||||
<li
|
||||
className = { classes.drawerItem }
|
||||
@@ -126,7 +126,7 @@ export default function LobbyParticipants() {
|
||||
className = { classes.icon }
|
||||
size = { 20 }
|
||||
src = { IconCloseLarge } />
|
||||
<span>{ t('participantsPane.actions.reject')}</span>
|
||||
<span>{ t('lobby.reject')}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</Drawer>
|
||||
|
||||
@@ -105,9 +105,10 @@ function MeetingParticipants({
|
||||
const participantActionEllipsisLabel = t('participantsPane.actions.moreParticipantOptions');
|
||||
const youText = t('chat.you');
|
||||
const isBreakoutRoom = useSelector(isInBreakoutRoom);
|
||||
const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0);
|
||||
const _isCurrentRoomRenamable = useSelector(isCurrentRoomRenamable);
|
||||
|
||||
const { classes: styles } = useStyles();
|
||||
const { classes: styles, cx } = useStyles();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -117,6 +118,11 @@ function MeetingParticipants({
|
||||
role = 'heading'>
|
||||
{ t('participantsPane.title') }
|
||||
</span>
|
||||
{visitorsCount > 0 && (
|
||||
<div className = { cx(styles.heading, styles.headingW) }>
|
||||
{t('participantsPane.headings.visitors', { count: visitorsCount })}
|
||||
</div>
|
||||
)}
|
||||
<div className = { styles.heading }>
|
||||
{currentRoom?.name
|
||||
? `${currentRoom.name} (${participantsCount})`
|
||||
|
||||
@@ -26,7 +26,7 @@ import { RoomList } from '../breakout-rooms/components/web/RoomList';
|
||||
import { FooterContextMenu } from './FooterContextMenu';
|
||||
import LobbyParticipants from './LobbyParticipants';
|
||||
import MeetingParticipants from './MeetingParticipants';
|
||||
import VisitorsList from './VisitorsList';
|
||||
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
@@ -171,8 +171,6 @@ const ParticipantsPane = () => {
|
||||
onClick = { onClosePane } />
|
||||
</div>
|
||||
<div className = { classes.container }>
|
||||
<VisitorsList />
|
||||
<br className = { classes.antiCollapse } />
|
||||
<LobbyParticipants />
|
||||
<br className = { classes.antiCollapse } />
|
||||
<MeetingParticipants
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.web';
|
||||
import { approveRequest, denyRequest } from '../../../visitors/actions';
|
||||
import { IPromotionRequest } from '../../../visitors/types';
|
||||
import { ACTION_TRIGGER, MEDIA_STATE } from '../../constants';
|
||||
|
||||
import ParticipantItem from './ParticipantItem';
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Promotion request reference.
|
||||
*/
|
||||
request: IPromotionRequest;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
button: {
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
moreButton: {
|
||||
paddingRight: '6px',
|
||||
paddingLeft: '6px',
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
contextMenu: {
|
||||
position: 'fixed',
|
||||
top: 'auto',
|
||||
marginRight: '8px'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export const VisitorsItem = ({
|
||||
request: r
|
||||
}: IProps) => {
|
||||
const { from, nick } = r;
|
||||
const { t } = useTranslation();
|
||||
const { classes: styles } = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const admit = useCallback(() => dispatch(approveRequest(r)), [ dispatch, r ]);
|
||||
const reject = useCallback(() => dispatch(denyRequest(r)), [ dispatch, r ]);
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
actionsTrigger = { ACTION_TRIGGER.PERMANENT }
|
||||
audioMediaState = { MEDIA_STATE.NONE }
|
||||
displayName = { nick }
|
||||
participantID = { from }
|
||||
raisedHand = { true }
|
||||
videoMediaState = { MEDIA_STATE.NONE }
|
||||
youText = { t('chat.you') }>
|
||||
|
||||
{<>
|
||||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.reject')} ${r.nick}` }
|
||||
className = { styles.button }
|
||||
labelKey = 'participantsPane.actions.reject'
|
||||
onClick = { reject }
|
||||
size = 'small'
|
||||
testId = { `reject-${from}` }
|
||||
type = { BUTTON_TYPES.DESTRUCTIVE } />
|
||||
<Button
|
||||
accessibilityLabel = { `${t('participantsPane.actions.admit')} ${r.nick}` }
|
||||
className = { styles.button }
|
||||
labelKey = 'participantsPane.actions.admit'
|
||||
onClick = { admit }
|
||||
size = 'small'
|
||||
testId = { `admit-${from}` } />
|
||||
</>
|
||||
}
|
||||
</ParticipantItem>
|
||||
);
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||
import { admitMultiple } from '../../../visitors/actions';
|
||||
import { getPromotionRequests } from '../../../visitors/functions';
|
||||
|
||||
import { VisitorsItem } from './VisitorsItem';
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
container: {
|
||||
margin: `${theme.spacing(3)} 0`
|
||||
},
|
||||
headingW: {
|
||||
color: theme.palette.warning02
|
||||
},
|
||||
drawerActions: {
|
||||
listStyleType: 'none',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
},
|
||||
drawerItem: {
|
||||
alignItems: 'center',
|
||||
color: theme.palette.text01,
|
||||
display: 'flex',
|
||||
padding: '12px 16px',
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
|
||||
|
||||
'&:first-child': {
|
||||
marginTop: '15px'
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
background: theme.palette.action02
|
||||
}
|
||||
},
|
||||
icon: {
|
||||
marginRight: 16
|
||||
},
|
||||
headingContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
heading: {
|
||||
...withPixelLineHeight(theme.typography.bodyShortBold),
|
||||
color: theme.palette.text02
|
||||
},
|
||||
link: {
|
||||
...withPixelLineHeight(theme.typography.labelBold),
|
||||
color: theme.palette.link01,
|
||||
cursor: 'pointer'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Component used to display a list of visitors waiting for approval to join the main meeting.
|
||||
*
|
||||
* @returns {ReactNode}
|
||||
*/
|
||||
export default function VisitorsList() {
|
||||
const requests = useSelector(getPromotionRequests);
|
||||
const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { classes, cx } = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const admitAll = useCallback(() => {
|
||||
dispatch(admitMultiple(requests));
|
||||
}, [ dispatch, requests ]);
|
||||
|
||||
if (visitorsCount <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className = { classes.headingContainer }>
|
||||
<div className = { cx(classes.heading, classes.headingW) }>
|
||||
{ t('participantsPane.headings.visitors', { count: visitorsCount })}
|
||||
{ requests.length > 0
|
||||
&& t('participantsPane.headings.visitorRequests', { count: requests.length }) }
|
||||
</div>
|
||||
{
|
||||
requests.length > 1
|
||||
&& <div
|
||||
className = { classes.link }
|
||||
onClick = { admitAll }>{t('participantsPane.actions.admitAll')}</div>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
className = { classes.container }
|
||||
id = 'visitor-list'>
|
||||
{
|
||||
requests.map(r => (
|
||||
<VisitorsItem
|
||||
key = { r.from }
|
||||
request = { r } />)
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -24,16 +24,16 @@ export function useLobbyActions(participant?: IDrawerParticipant | null, closeDr
|
||||
e.stopPropagation();
|
||||
dispatch(approveKnockingParticipant(participant?.participantID ?? ''));
|
||||
closeDrawer?.();
|
||||
}, [ dispatch, closeDrawer, participant?.participantID ]),
|
||||
}, [ dispatch, closeDrawer ]),
|
||||
|
||||
useCallback(() => {
|
||||
dispatch(rejectKnockingParticipant(participant?.participantID ?? ''));
|
||||
closeDrawer?.();
|
||||
}, [ dispatch, closeDrawer, participant?.participantID ]),
|
||||
}, [ dispatch, closeDrawer ]),
|
||||
|
||||
useCallback(() => {
|
||||
dispatch(handleLobbyChatInitialized(participant?.participantID ?? ''));
|
||||
}, [ dispatch, participant?.participantID ])
|
||||
}, [ dispatch ])
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -63,23 +63,13 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
|
||||
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);
|
||||
data.type === COMMAND_NEW_POLL ? data.senderId = user._id : data.voterId = user._id;
|
||||
_handleReceivePollsMessage(data, 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);
|
||||
data.type === COMMAND_NEW_POLL ? data.senderId = id : data.voterId = id;
|
||||
_handleReceivePollsMessage(data, dispatch);
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
@@ -189,17 +189,7 @@ const useStyles = makeStyles()(theme => {
|
||||
color: theme.palette.text04,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
position: 'relative',
|
||||
top: `-${theme.spacing(3)}`,
|
||||
|
||||
'@media (max-width: 511px)': {
|
||||
margin: '0 auto',
|
||||
top: 0
|
||||
},
|
||||
|
||||
'@media (max-width: 420px)': {
|
||||
top: 0,
|
||||
width: 'calc(100% - 32px)'
|
||||
}
|
||||
top: `-${theme.spacing(3)}`
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import { updateDropboxToken } from '../../../dropbox/actions';
|
||||
import { getDropboxData, getNewAccessToken, isEnabled as isDropboxEnabled } from '../../../dropbox/functions.any';
|
||||
import { showErrorNotification } from '../../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../../notifications/constants';
|
||||
import { setRequestingSubtitles } from '../../../subtitles/actions.any';
|
||||
import { toggleRequestingSubtitles } from '../../../subtitles/actions';
|
||||
import { setSelectedRecordingService, startLocalVideoRecording } from '../../actions';
|
||||
import { RECORDING_TYPES } from '../../constants';
|
||||
import { supportsLocalRecording } from '../../functions';
|
||||
@@ -23,9 +23,9 @@ export interface IProps extends WithTranslation {
|
||||
_appKey: string;
|
||||
|
||||
/**
|
||||
* Requests transcribing when recording is turned on.
|
||||
* Requests subtitles when recording is turned on.
|
||||
*/
|
||||
_autoTranscribeOnRecord: boolean;
|
||||
_autoCaptionOnRecord: boolean;
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
@@ -114,16 +114,6 @@ interface IState {
|
||||
*/
|
||||
sharingEnabled: boolean;
|
||||
|
||||
/**
|
||||
* True if the user requested the service to record audio and video.
|
||||
*/
|
||||
shouldRecordAudioAndVideo: boolean;
|
||||
|
||||
/**
|
||||
* True if the user requested the service to record transcription.
|
||||
*/
|
||||
shouldRecordTranscription: boolean;
|
||||
|
||||
/**
|
||||
* Number of MiB of available space in user's Dropbox account.
|
||||
*/
|
||||
@@ -154,8 +144,6 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
this._onSharingSettingChanged = this._onSharingSettingChanged.bind(this);
|
||||
this._toggleScreenshotCapture = this._toggleScreenshotCapture.bind(this);
|
||||
this._onLocalRecordingSelfChange = this._onLocalRecordingSelfChange.bind(this);
|
||||
this._onTranscriptionChange = this._onTranscriptionChange.bind(this);
|
||||
this._onRecordAudioAndVideoChange = this._onRecordAudioAndVideoChange.bind(this);
|
||||
|
||||
let selectedRecordingService = '';
|
||||
|
||||
@@ -177,8 +165,6 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
isValidating: false,
|
||||
userName: undefined,
|
||||
sharingEnabled: true,
|
||||
shouldRecordAudioAndVideo: true,
|
||||
shouldRecordTranscription: true,
|
||||
spaceLeft: undefined,
|
||||
selectedRecordingService,
|
||||
localRecordingOnlySelf: false
|
||||
@@ -255,30 +241,6 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles transcription switch change.
|
||||
*
|
||||
* @param {boolean} value - The new value.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTranscriptionChange(value: boolean) {
|
||||
this.setState({
|
||||
shouldRecordTranscription: value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles audio and video switch change.
|
||||
*
|
||||
* @param {boolean} value - The new value.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRecordAudioAndVideoChange(value: boolean) {
|
||||
this.setState({
|
||||
shouldRecordAudioAndVideo: value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the dropbox access token and fetches account information.
|
||||
*
|
||||
@@ -335,7 +297,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
_onSubmit() {
|
||||
const {
|
||||
_appKey,
|
||||
_autoTranscribeOnRecord,
|
||||
_autoCaptionOnRecord,
|
||||
_conference,
|
||||
_isDropboxEnabled,
|
||||
_rToken,
|
||||
@@ -347,59 +309,57 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
type?: string;
|
||||
} = {};
|
||||
|
||||
if (this.state.shouldRecordAudioAndVideo) {
|
||||
switch (this.state.selectedRecordingService) {
|
||||
case RECORDING_TYPES.DROPBOX: {
|
||||
if (_isDropboxEnabled && _token) {
|
||||
appData = JSON.stringify({
|
||||
'file_recording_metadata': {
|
||||
'upload_credentials': {
|
||||
'service_name': RECORDING_TYPES.DROPBOX,
|
||||
'token': _token,
|
||||
'r_token': _rToken,
|
||||
'app_key': _appKey
|
||||
}
|
||||
}
|
||||
});
|
||||
attributes.type = RECORDING_TYPES.DROPBOX;
|
||||
} else {
|
||||
dispatch(showErrorNotification({
|
||||
titleKey: 'dialog.noDropboxToken'
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RECORDING_TYPES.JITSI_REC_SERVICE: {
|
||||
switch (this.state.selectedRecordingService) {
|
||||
case RECORDING_TYPES.DROPBOX: {
|
||||
if (_isDropboxEnabled && _token) {
|
||||
appData = JSON.stringify({
|
||||
'file_recording_metadata': {
|
||||
'share': this.state.sharingEnabled
|
||||
'upload_credentials': {
|
||||
'service_name': RECORDING_TYPES.DROPBOX,
|
||||
'token': _token,
|
||||
'r_token': _rToken,
|
||||
'app_key': _appKey
|
||||
}
|
||||
}
|
||||
});
|
||||
attributes.type = RECORDING_TYPES.JITSI_REC_SERVICE;
|
||||
break;
|
||||
}
|
||||
case RECORDING_TYPES.LOCAL: {
|
||||
dispatch(startLocalVideoRecording(this.state.localRecordingOnlySelf));
|
||||
attributes.type = RECORDING_TYPES.DROPBOX;
|
||||
} else {
|
||||
dispatch(showErrorNotification({
|
||||
titleKey: 'dialog.noDropboxToken'
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'confirm.button', attributes)
|
||||
);
|
||||
|
||||
this._toggleScreenshotCapture();
|
||||
_conference?.startRecording({
|
||||
mode: JitsiRecordingConstants.mode.FILE,
|
||||
appData
|
||||
break;
|
||||
}
|
||||
case RECORDING_TYPES.JITSI_REC_SERVICE: {
|
||||
appData = JSON.stringify({
|
||||
'file_recording_metadata': {
|
||||
'share': this.state.sharingEnabled
|
||||
}
|
||||
});
|
||||
attributes.type = RECORDING_TYPES.JITSI_REC_SERVICE;
|
||||
break;
|
||||
}
|
||||
case RECORDING_TYPES.LOCAL: {
|
||||
dispatch(startLocalVideoRecording(this.state.localRecordingOnlySelf));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_autoTranscribeOnRecord || this.state.shouldRecordTranscription) {
|
||||
dispatch(setRequestingSubtitles(true, false));
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'confirm.button', attributes)
|
||||
);
|
||||
|
||||
this._toggleScreenshotCapture();
|
||||
_conference?.startRecording({
|
||||
mode: JitsiRecordingConstants.mode.FILE,
|
||||
appData
|
||||
});
|
||||
|
||||
if (_autoCaptionOnRecord) {
|
||||
dispatch(toggleRequestingSubtitles());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -432,7 +392,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
* @private
|
||||
* @returns {{
|
||||
* _appKey: string,
|
||||
* _autoTranscribeOnRecord: boolean,
|
||||
* _autoCaptionOnRecord: boolean,
|
||||
* _conference: JitsiConference,
|
||||
* _fileRecordingsServiceEnabled: boolean,
|
||||
* _fileRecordingsServiceSharingEnabled: boolean,
|
||||
@@ -452,7 +412,7 @@ export function mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
|
||||
return {
|
||||
_appKey: dropbox.appKey ?? '',
|
||||
_autoTranscribeOnRecord: transcription?.autoTranscribeOnRecord ?? false,
|
||||
_autoCaptionOnRecord: transcription?.autoCaptionOnRecord ?? false,
|
||||
_conference: state['features/base/conference'].conference,
|
||||
_fileRecordingsServiceEnabled: recordingService?.enabled ?? false,
|
||||
_fileRecordingsServiceSharingEnabled: recordingService?.sharingEnabled ?? false,
|
||||
|
||||
@@ -9,7 +9,6 @@ import { _abstractMapStateToProps } from '../../../base/dialog/functions';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants/functions';
|
||||
import { authorizeDropbox, updateDropboxToken } from '../../../dropbox/actions';
|
||||
import { isVpaasMeeting } from '../../../jaas/functions';
|
||||
import { canStartTranscribing } from '../../../subtitles/functions';
|
||||
import { RECORDING_TYPES } from '../../constants';
|
||||
import { supportsLocalRecording } from '../../functions';
|
||||
|
||||
@@ -19,11 +18,6 @@ import { supportsLocalRecording } from '../../functions';
|
||||
*/
|
||||
export interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Whether the local participant can start transcribing.
|
||||
*/
|
||||
_canStartTranscribing: boolean;
|
||||
|
||||
/**
|
||||
* Style of the dialogs feature.
|
||||
*/
|
||||
@@ -117,21 +111,11 @@ export interface IProps extends WithTranslation {
|
||||
*/
|
||||
onLocalRecordingSelfChange?: () => void;
|
||||
|
||||
/**
|
||||
* Callback to change the audio and video recording setting.
|
||||
*/
|
||||
onRecordAudioAndVideoChange: Function;
|
||||
|
||||
/**
|
||||
* Callback to be invoked on sharing setting change.
|
||||
*/
|
||||
onSharingSettingChanged: () => void;
|
||||
|
||||
/**
|
||||
* Callback to change the transcription recording setting.
|
||||
*/
|
||||
onTranscriptionChange: Function;
|
||||
|
||||
/**
|
||||
* The currently selected recording service of type: RECORDING_TYPES.
|
||||
*/
|
||||
@@ -142,16 +126,6 @@ export interface IProps extends WithTranslation {
|
||||
*/
|
||||
sharingSetting: boolean;
|
||||
|
||||
/**
|
||||
* Whether to show the audio and video related content.
|
||||
*/
|
||||
shouldRecordAudioAndVideo: boolean;
|
||||
|
||||
/**
|
||||
* Whether to show the transcription related content.
|
||||
*/
|
||||
shouldRecordTranscription: boolean;
|
||||
|
||||
/**
|
||||
* Number of MiB of available space in user's Dropbox account.
|
||||
*/
|
||||
@@ -163,26 +137,18 @@ export interface IProps extends WithTranslation {
|
||||
userName?: string;
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
|
||||
/**
|
||||
* Whether to show the advanced options or not.
|
||||
*/
|
||||
showAdvancedOptions: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* React Component for getting confirmation to start a recording session.
|
||||
* React Component for getting confirmation to start a file recording session.
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class AbstractStartRecordingDialogContent extends Component<IProps, IState> {
|
||||
class AbstractStartRecordingDialogContent<P extends IProps> extends Component<P> {
|
||||
/**
|
||||
* Initializes a new {@code AbstractStartRecordingDialogContent} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: IProps) {
|
||||
constructor(props: P) {
|
||||
super(props);
|
||||
|
||||
// Bind event handler; it bounds once for every instance.
|
||||
@@ -191,13 +157,6 @@ class AbstractStartRecordingDialogContent extends Component<IProps, IState> {
|
||||
this._onDropboxSwitchChange = this._onDropboxSwitchChange.bind(this);
|
||||
this._onRecordingServiceSwitchChange = this._onRecordingServiceSwitchChange.bind(this);
|
||||
this._onLocalRecordingSwitchChange = this._onLocalRecordingSwitchChange.bind(this);
|
||||
this._onTranscriptionSwitchChange = this._onTranscriptionSwitchChange.bind(this);
|
||||
this._onRecordAudioAndVideoSwitchChange = this._onRecordAudioAndVideoSwitchChange.bind(this);
|
||||
this._onToggleShowOptions = this._onToggleShowOptions.bind(this);
|
||||
|
||||
this.state = {
|
||||
showAdvancedOptions: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +177,7 @@ class AbstractStartRecordingDialogContent extends Component<IProps, IState> {
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidUpdate(prevProps: IProps) {
|
||||
componentDidUpdate(prevProps: P) {
|
||||
// Auto sign-out when the use chooses another recording service.
|
||||
if (prevProps.selectedRecordingService === RECORDING_TYPES.DROPBOX
|
||||
&& this.props.selectedRecordingService !== RECORDING_TYPES.DROPBOX && this.props.isTokenValid) {
|
||||
@@ -226,15 +185,6 @@ class AbstractStartRecordingDialogContent extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the advanced options should be rendered.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onToggleShowOptions() {
|
||||
this.setState({ showAdvancedOptions: !this.state.showAdvancedOptions });
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the file sharing content should be rendered or not.
|
||||
*
|
||||
@@ -258,15 +208,6 @@ class AbstractStartRecordingDialogContent extends Component<IProps, IState> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the save transcription content should be rendered or not.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_canStartTranscribing() {
|
||||
return this.props._canStartTranscribing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the no integrations content should be rendered or not.
|
||||
*
|
||||
@@ -295,26 +236,6 @@ class AbstractStartRecordingDialogContent extends Component<IProps, IState> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for transcription switch change.
|
||||
*
|
||||
* @param {boolean} value - The new value.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTranscriptionSwitchChange(value: boolean | undefined) {
|
||||
this.props.onTranscriptionChange(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for audio and video switch change.
|
||||
*
|
||||
* @param {boolean} value - The new value.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRecordAudioAndVideoSwitchChange(value: boolean | undefined) {
|
||||
this.props.onRecordAudioAndVideoChange(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for onValueChange events from the Switch component.
|
||||
*
|
||||
@@ -418,7 +339,6 @@ export function mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
isVpaas: isVpaasMeeting(state),
|
||||
_canStartTranscribing: canStartTranscribing(state),
|
||||
_hideStorageWarning: Boolean(recordingService?.hideStorageWarning),
|
||||
_isModerator: isLocalParticipantModerator(state),
|
||||
_localRecordingAvailable,
|
||||
|
||||
@@ -98,16 +98,7 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isStartRecordingDisabled() {
|
||||
const {
|
||||
isTokenValid,
|
||||
selectedRecordingService,
|
||||
shouldRecordAudioAndVideo,
|
||||
shouldRecordTranscription
|
||||
} = this.state;
|
||||
|
||||
if (!shouldRecordAudioAndVideo && !shouldRecordTranscription) {
|
||||
return true;
|
||||
}
|
||||
const { isTokenValid, selectedRecordingService } = this.state;
|
||||
|
||||
// Start button is disabled if recording service is only shown;
|
||||
// When validating dropbox token, if that is not enabled, we either always
|
||||
@@ -134,8 +125,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
isValidating,
|
||||
selectedRecordingService,
|
||||
sharingEnabled,
|
||||
shouldRecordAudioAndVideo,
|
||||
shouldRecordTranscription,
|
||||
spaceLeft,
|
||||
userName
|
||||
} = this.state;
|
||||
@@ -153,13 +142,9 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
isTokenValid = { isTokenValid }
|
||||
isValidating = { isValidating }
|
||||
onChange = { this._onSelectedRecordingServiceChanged }
|
||||
onRecordAudioAndVideoChange = { this._onRecordAudioAndVideoChange }
|
||||
onSharingSettingChanged = { this._onSharingSettingChanged }
|
||||
onTranscriptionChange = { this._onTranscriptionChange }
|
||||
selectedRecordingService = { selectedRecordingService }
|
||||
sharingSetting = { sharingEnabled }
|
||||
shouldRecordAudioAndVideo = { shouldRecordAudioAndVideo }
|
||||
shouldRecordTranscription = { shouldRecordTranscription }
|
||||
spaceLeft = { spaceLeft }
|
||||
userName = { userName } />
|
||||
</JitsiScreen>
|
||||
|
||||
@@ -4,15 +4,16 @@ import { Text } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconArrowDown, IconArrowRight } from '../../../../base/icons/svg';
|
||||
import LoadingIndicator from '../../../../base/react/components/native/LoadingIndicator';
|
||||
import Button from '../../../../base/ui/components/native/Button';
|
||||
import Switch from '../../../../base/ui/components/native/Switch';
|
||||
import { BUTTON_TYPES } from '../../../../base/ui/constants.native';
|
||||
import { RECORDING_TYPES } from '../../../constants';
|
||||
import { getRecordingDurationEstimation } from '../../../functions';
|
||||
import AbstractStartRecordingDialogContent, { mapStateToProps } from '../AbstractStartRecordingDialogContent';
|
||||
import AbstractStartRecordingDialogContent, {
|
||||
IProps,
|
||||
mapStateToProps
|
||||
} from '../AbstractStartRecordingDialogContent';
|
||||
import {
|
||||
DROPBOX_LOGO,
|
||||
ICON_CLOUD,
|
||||
@@ -24,7 +25,7 @@ import {
|
||||
/**
|
||||
* The start recording dialog content for the mobile application.
|
||||
*/
|
||||
class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
class StartRecordingDialogContent extends AbstractStartRecordingDialogContent<IProps> {
|
||||
/**
|
||||
* Renders the component.
|
||||
*
|
||||
@@ -40,86 +41,10 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
{ this._renderFileSharingContent() }
|
||||
{ this._renderUploadToTheCloudInfo() }
|
||||
{ this._renderIntegrationsContent() }
|
||||
{ this._renderAdvancedOptions() }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the save transcription switch.
|
||||
*
|
||||
* @returns {React$Component}
|
||||
*/
|
||||
_renderAdvancedOptions() {
|
||||
if (!this._canStartTranscribing()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { showAdvancedOptions } = this.state;
|
||||
const {
|
||||
_dialogStyles,
|
||||
_styles: styles,
|
||||
shouldRecordAudioAndVideo,
|
||||
shouldRecordTranscription,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
style = { styles.header }>
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.showAdvancedOptions') }
|
||||
</Text>
|
||||
<Icon
|
||||
ariaPressed = { showAdvancedOptions }
|
||||
onClick = { this._onToggleShowOptions }
|
||||
role = 'button'
|
||||
size = { 24 }
|
||||
src = { showAdvancedOptions ? IconArrowDown : IconArrowRight } />
|
||||
</View>
|
||||
{showAdvancedOptions && (
|
||||
<>
|
||||
<View
|
||||
key = 'transcriptionSetting'
|
||||
style = { styles.header }>
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.recordTranscription') }
|
||||
</Text>
|
||||
<Switch
|
||||
checked = { shouldRecordTranscription }
|
||||
onChange = { this._onTranscriptionSwitchChange }
|
||||
style = { styles.switch } />
|
||||
</View>
|
||||
<View
|
||||
key = 'audioVideoSetting'
|
||||
style = { styles.header }>
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.recordAudioAndVideo') }
|
||||
</Text>
|
||||
<Switch
|
||||
checked = { shouldRecordAudioAndVideo }
|
||||
onChange = { this._onRecordAudioAndVideoSwitchChange }
|
||||
style = { styles.switch } />
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the content in case no integrations were enabled.
|
||||
*
|
||||
@@ -132,7 +57,6 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
integrationsEnabled,
|
||||
isValidating,
|
||||
selectedRecordingService,
|
||||
shouldRecordAudioAndVideo,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
@@ -145,7 +69,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
? (
|
||||
<Switch
|
||||
checked = { selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE }
|
||||
disabled = { isValidating || !shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
onChange = { this._onRecordingServiceSwitchChange }
|
||||
style = { styles.switch } />
|
||||
) : null;
|
||||
@@ -185,7 +109,6 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
isValidating,
|
||||
onSharingSettingChanged,
|
||||
sharingSetting,
|
||||
shouldRecordAudioAndVideo,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
@@ -205,7 +128,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
</Text>
|
||||
<Switch
|
||||
checked = { sharingSetting }
|
||||
disabled = { isValidating || !shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
onChange = { onSharingSettingChanged }
|
||||
style = { styles.switch } />
|
||||
</View>
|
||||
@@ -314,7 +237,6 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
isTokenValid,
|
||||
isValidating,
|
||||
selectedRecordingService,
|
||||
shouldRecordAudioAndVideo,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
@@ -348,7 +270,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
switchContent = (
|
||||
<Switch
|
||||
checked = { selectedRecordingService === RECORDING_TYPES.DROPBOX }
|
||||
disabled = { isValidating || !shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
onChange = { this._onDropboxSwitchChange }
|
||||
style = { styles.switch } />
|
||||
);
|
||||
|
||||
@@ -12,5 +12,3 @@ export const ICON_CLOUD = 'images/icon-cloud.png';
|
||||
export const ICON_INFO = 'images/icon-info.png';
|
||||
|
||||
export const ICON_USERS = 'images/icon-users.png';
|
||||
|
||||
export const ICON_OPTIONS = 'images/icon-info.png';
|
||||
|
||||
@@ -28,16 +28,7 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isStartRecordingDisabled() {
|
||||
const {
|
||||
isTokenValid,
|
||||
selectedRecordingService,
|
||||
shouldRecordAudioAndVideo,
|
||||
shouldRecordTranscription
|
||||
} = this.state;
|
||||
|
||||
if (!shouldRecordAudioAndVideo && !shouldRecordTranscription) {
|
||||
return true;
|
||||
}
|
||||
const { isTokenValid, selectedRecordingService } = this.state;
|
||||
|
||||
// Start button is disabled if recording service is only shown;
|
||||
// When validating dropbox token, if that is not enabled, we either always
|
||||
@@ -66,8 +57,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
localRecordingOnlySelf,
|
||||
selectedRecordingService,
|
||||
sharingEnabled,
|
||||
shouldRecordAudioAndVideo,
|
||||
shouldRecordTranscription,
|
||||
spaceLeft,
|
||||
userName
|
||||
} = this.state;
|
||||
@@ -93,13 +82,9 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
localRecordingOnlySelf = { localRecordingOnlySelf }
|
||||
onChange = { this._onSelectedRecordingServiceChanged }
|
||||
onLocalRecordingSelfChange = { this._onLocalRecordingSelfChange }
|
||||
onRecordAudioAndVideoChange = { this._onRecordAudioAndVideoChange }
|
||||
onSharingSettingChanged = { this._onSharingSettingChanged }
|
||||
onTranscriptionChange = { this._onTranscriptionChange }
|
||||
selectedRecordingService = { selectedRecordingService }
|
||||
sharingSetting = { sharingEnabled }
|
||||
shouldRecordAudioAndVideo = { shouldRecordAudioAndVideo }
|
||||
shouldRecordTranscription = { shouldRecordTranscription }
|
||||
spaceLeft = { spaceLeft }
|
||||
userName = { userName } />
|
||||
</Dialog>
|
||||
|
||||
@@ -2,8 +2,6 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconArrowDown, IconArrowRight } from '../../../../base/icons/svg';
|
||||
import Container from '../../../../base/react/components/web/Container';
|
||||
import Image from '../../../../base/react/components/web/Image';
|
||||
import LoadingIndicator from '../../../../base/react/components/web/LoadingIndicator';
|
||||
@@ -13,7 +11,10 @@ import Switch from '../../../../base/ui/components/web/Switch';
|
||||
import { BUTTON_TYPES } from '../../../../base/ui/constants.web';
|
||||
import { RECORDING_TYPES } from '../../../constants';
|
||||
import { getRecordingDurationEstimation } from '../../../functions';
|
||||
import AbstractStartRecordingDialogContent, { mapStateToProps } from '../AbstractStartRecordingDialogContent';
|
||||
import AbstractStartRecordingDialogContent, {
|
||||
IProps,
|
||||
mapStateToProps
|
||||
} from '../AbstractStartRecordingDialogContent';
|
||||
import {
|
||||
DROPBOX_LOGO,
|
||||
ICON_CLOUD,
|
||||
@@ -29,7 +30,7 @@ const EMPTY_FUNCTION = () => {
|
||||
/**
|
||||
* The start recording dialog content for the mobile application.
|
||||
*/
|
||||
class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
class StartRecordingDialogContent extends AbstractStartRecordingDialogContent<IProps> {
|
||||
/**
|
||||
* Renders the component.
|
||||
*
|
||||
@@ -48,72 +49,10 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
</>
|
||||
)}
|
||||
{ this._renderLocalRecordingContent() }
|
||||
{ this._renderAdvancedOptions() }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the switch for saving the transcription.
|
||||
*
|
||||
* @returns {React$Component}
|
||||
*/
|
||||
_renderAdvancedOptions() {
|
||||
if (!this._canStartTranscribing()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { showAdvancedOptions } = this.state;
|
||||
const { shouldRecordAudioAndVideo, shouldRecordTranscription, t } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className = 'recording-header-line' />
|
||||
<div
|
||||
className = 'recording-header'
|
||||
onClick = { this._onToggleShowOptions }>
|
||||
<label className = 'recording-title-no-space'>
|
||||
{t('recording.showAdvancedOptions')}
|
||||
</label>
|
||||
<Icon
|
||||
ariaPressed = { showAdvancedOptions }
|
||||
onClick = { this._onToggleShowOptions }
|
||||
role = 'button'
|
||||
size = { 24 }
|
||||
src = { showAdvancedOptions ? IconArrowDown : IconArrowRight } />
|
||||
</div>
|
||||
{showAdvancedOptions && (
|
||||
<>
|
||||
<div className = 'recording-header space-top'>
|
||||
<label
|
||||
className = 'recording-title'
|
||||
htmlFor = 'recording-switch-transcription'>
|
||||
{ t('recording.recordTranscription') }
|
||||
</label>
|
||||
<Switch
|
||||
checked = { shouldRecordTranscription }
|
||||
className = 'recording-switch'
|
||||
id = 'recording-switch-transcription'
|
||||
onChange = { this._onTranscriptionSwitchChange } />
|
||||
</div>
|
||||
<div className = 'recording-header space-top'>
|
||||
<label
|
||||
className = 'recording-title'
|
||||
htmlFor = 'recording-switch-transcription'>
|
||||
{ t('recording.recordAudioAndVideo') }
|
||||
</label>
|
||||
<Switch
|
||||
checked = { shouldRecordAudioAndVideo }
|
||||
className = 'recording-switch'
|
||||
id = 'recording-switch-transcription'
|
||||
onChange = { this._onRecordAudioAndVideoSwitchChange } />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the content in case no integrations were enabled.
|
||||
*
|
||||
@@ -139,7 +78,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
<Switch
|
||||
checked = { selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE }
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating || !this.props.shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
id = 'recording-switch-jitsi'
|
||||
onChange = { this._onRecordingServiceSwitchChange } />
|
||||
) : null;
|
||||
@@ -209,7 +148,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
<Switch
|
||||
checked = { sharingSetting }
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating || !this.props.shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
id = 'recording-switch-share'
|
||||
onChange = { onSharingSettingChanged } />
|
||||
</Container>
|
||||
@@ -355,7 +294,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
checked = { selectedRecordingService
|
||||
=== RECORDING_TYPES.DROPBOX }
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating || !this.props.shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
id = 'recording-switch-integration'
|
||||
onChange = { this._onDropboxSwitchChange } />
|
||||
);
|
||||
@@ -433,7 +372,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
checked = { selectedRecordingService
|
||||
=== RECORDING_TYPES.LOCAL }
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating || !this.props.shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
id = 'recording-switch-local'
|
||||
onChange = { this._onLocalRecordingSwitchChange } />
|
||||
</Container>
|
||||
@@ -457,7 +396,7 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
|
||||
<Switch
|
||||
checked = { Boolean(localRecordingOnlySelf) }
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating || !this.props.shouldRecordAudioAndVideo }
|
||||
disabled = { isValidating }
|
||||
id = 'recording-switch-myself'
|
||||
onChange = { onLocalRecordingSelfChange ?? EMPTY_FUNCTION } />
|
||||
</Container>
|
||||
|
||||
@@ -1,36 +1,19 @@
|
||||
import i18next from 'i18next';
|
||||
|
||||
import { IReduxState, IStore } from '../app/types';
|
||||
import { IReduxState } from '../app/types';
|
||||
import { isMobileBrowser } from '../base/environment/utils';
|
||||
import { isJwtFeatureEnabled } from '../base/jwt/functions';
|
||||
import { JitsiRecordingConstants, browser } from '../base/lib-jitsi-meet';
|
||||
import { getSoundFileSrc } from '../base/media/functions';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getRemoteParticipants,
|
||||
isLocalParticipantModerator
|
||||
} from '../base/participants/functions';
|
||||
import { registerSound, unregisterSound } from '../base/sounds/actions';
|
||||
import { isInBreakoutRoom } from '../breakout-rooms/functions';
|
||||
import { isEnabled as isDropboxEnabled } from '../dropbox/functions';
|
||||
import { extractFqnFromPath } from '../dynamic-branding/functions.any';
|
||||
|
||||
import LocalRecordingManager from './components/Recording/LocalRecordingManager';
|
||||
import {
|
||||
LIVE_STREAMING_OFF_SOUND_ID,
|
||||
LIVE_STREAMING_ON_SOUND_ID,
|
||||
RECORDING_OFF_SOUND_ID,
|
||||
RECORDING_ON_SOUND_ID,
|
||||
RECORDING_STATUS_PRIORITIES,
|
||||
RECORDING_TYPES
|
||||
} from './constants';
|
||||
import { RECORDING_STATUS_PRIORITIES, RECORDING_TYPES } from './constants';
|
||||
import logger from './logger';
|
||||
import {
|
||||
LIVE_STREAMING_OFF_SOUND_FILE,
|
||||
LIVE_STREAMING_ON_SOUND_FILE,
|
||||
RECORDING_OFF_SOUND_FILE,
|
||||
RECORDING_ON_SOUND_FILE
|
||||
} from './sounds';
|
||||
|
||||
/**
|
||||
* Searches in the passed in redux state for an active recording session of the
|
||||
@@ -295,47 +278,3 @@ function isRemoteParticipantRecordingLocally(state: IReduxState) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the audio files based on locale.
|
||||
*
|
||||
* @param {Dispatch<any>} dispatch - The redux dispatch function.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function unregisterRecordingAudioFiles(dispatch: IStore['dispatch']) {
|
||||
dispatch(unregisterSound(LIVE_STREAMING_OFF_SOUND_FILE));
|
||||
dispatch(unregisterSound(LIVE_STREAMING_ON_SOUND_FILE));
|
||||
dispatch(unregisterSound(RECORDING_OFF_SOUND_FILE));
|
||||
dispatch(unregisterSound(RECORDING_ON_SOUND_FILE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the audio files based on locale.
|
||||
*
|
||||
* @param {Dispatch<any>} dispatch - The redux dispatch function.
|
||||
* @param {boolean|undefined} shouldUnregister - Whether the sounds should be unregistered.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function registerRecordingAudioFiles(dispatch: IStore['dispatch'], shouldUnregister?: boolean) {
|
||||
const language = i18next.language;
|
||||
|
||||
if (shouldUnregister) {
|
||||
unregisterRecordingAudioFiles(dispatch);
|
||||
}
|
||||
|
||||
dispatch(registerSound(
|
||||
LIVE_STREAMING_OFF_SOUND_ID,
|
||||
getSoundFileSrc(LIVE_STREAMING_OFF_SOUND_FILE, language)));
|
||||
|
||||
dispatch(registerSound(
|
||||
LIVE_STREAMING_ON_SOUND_ID,
|
||||
getSoundFileSrc(LIVE_STREAMING_ON_SOUND_FILE, language)));
|
||||
|
||||
dispatch(registerSound(
|
||||
RECORDING_OFF_SOUND_ID,
|
||||
getSoundFileSrc(RECORDING_OFF_SOUND_FILE, language)));
|
||||
|
||||
dispatch(registerSound(
|
||||
RECORDING_ON_SOUND_ID,
|
||||
getSoundFileSrc(RECORDING_ON_SOUND_FILE, language)));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import {
|
||||
playSound,
|
||||
stopSound
|
||||
registerSound,
|
||||
stopSound,
|
||||
unregisterSound
|
||||
} from '../base/sounds/actions';
|
||||
import { TRACK_ADDED } from '../base/tracks/actionTypes';
|
||||
import { showErrorNotification, showNotification } from '../notifications/actions';
|
||||
@@ -42,11 +44,15 @@ import {
|
||||
} from './constants';
|
||||
import {
|
||||
getResourceId,
|
||||
getSessionById,
|
||||
registerRecordingAudioFiles,
|
||||
unregisterRecordingAudioFiles
|
||||
getSessionById
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
import {
|
||||
LIVE_STREAMING_OFF_SOUND_FILE,
|
||||
LIVE_STREAMING_ON_SOUND_FILE,
|
||||
RECORDING_OFF_SOUND_FILE,
|
||||
RECORDING_ON_SOUND_FILE
|
||||
} from './sounds';
|
||||
|
||||
/**
|
||||
* StateListenerRegistry provides a reliable way to detect the leaving of a
|
||||
@@ -79,12 +85,29 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
|
||||
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT:
|
||||
registerRecordingAudioFiles(dispatch);
|
||||
dispatch(registerSound(
|
||||
LIVE_STREAMING_OFF_SOUND_ID,
|
||||
LIVE_STREAMING_OFF_SOUND_FILE));
|
||||
|
||||
dispatch(registerSound(
|
||||
LIVE_STREAMING_ON_SOUND_ID,
|
||||
LIVE_STREAMING_ON_SOUND_FILE));
|
||||
|
||||
dispatch(registerSound(
|
||||
RECORDING_OFF_SOUND_ID,
|
||||
RECORDING_OFF_SOUND_FILE));
|
||||
|
||||
dispatch(registerSound(
|
||||
RECORDING_ON_SOUND_ID,
|
||||
RECORDING_ON_SOUND_FILE));
|
||||
|
||||
break;
|
||||
|
||||
case APP_WILL_UNMOUNT:
|
||||
unregisterRecordingAudioFiles(dispatch);
|
||||
dispatch(unregisterSound(LIVE_STREAMING_OFF_SOUND_ID));
|
||||
dispatch(unregisterSound(LIVE_STREAMING_ON_SOUND_ID));
|
||||
dispatch(unregisterSound(RECORDING_OFF_SOUND_ID));
|
||||
dispatch(unregisterSound(RECORDING_ON_SOUND_ID));
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -17,11 +17,6 @@ interface IProps {
|
||||
*/
|
||||
_password?: string;
|
||||
|
||||
/**
|
||||
* Number of digits used in the room-lock password.
|
||||
*/
|
||||
_passwordNumberOfDigits?: number;
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} which requires a password.
|
||||
*
|
||||
@@ -93,15 +88,6 @@ class PasswordRequiredPrompt extends Component<IProps, IState> {
|
||||
*/
|
||||
render() {
|
||||
const { password } = this.state;
|
||||
const { _passwordNumberOfDigits } = this.props;
|
||||
const textInputProps: any = {
|
||||
secureTextEntry: true
|
||||
};
|
||||
|
||||
if (_passwordNumberOfDigits) {
|
||||
textInputProps.keyboardType = 'numeric';
|
||||
textInputProps.maxLength = _passwordNumberOfDigits;
|
||||
}
|
||||
|
||||
return (
|
||||
<InputDialog
|
||||
@@ -110,7 +96,9 @@ class PasswordRequiredPrompt extends Component<IProps, IState> {
|
||||
messageKey = { password ? 'dialog.incorrectRoomLockPassword' : undefined }
|
||||
onCancel = { this._onCancel }
|
||||
onSubmit = { this._onSubmit }
|
||||
textInputProps = { textInputProps }
|
||||
textInputProps = {{
|
||||
secureTextEntry: true
|
||||
}}
|
||||
titleKey = 'dialog.password' />
|
||||
);
|
||||
}
|
||||
@@ -154,11 +142,8 @@ class PasswordRequiredPrompt extends Component<IProps, IState> {
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { roomPasswordNumberOfDigits } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_password: state['features/base/conference'].password,
|
||||
_passwordNumberOfDigits: roomPasswordNumberOfDigits
|
||||
_password: state['features/base/conference'].password
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,18 @@ export const REMOVE_TRANSCRIPT_MESSAGE = 'REMOVE_TRANSCRIPT_MESSAGE';
|
||||
*/
|
||||
export const UPDATE_TRANSCRIPT_MESSAGE = 'UPDATE_TRANSCRIPT_MESSAGE';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which indicates that a transcript with an
|
||||
* given message_id to be added or updated is received.
|
||||
*
|
||||
* {
|
||||
* type: UPDATE_TRANSLATION_LANGUAGE,
|
||||
* transcriptMessageID: string,
|
||||
* newTranscriptMessage: Object
|
||||
* }
|
||||
*/
|
||||
export const UPDATE_TRANSLATION_LANGUAGE = 'UPDATE_TRANSLATION_LANGUAGE';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which indicates that the user pressed the
|
||||
* ClosedCaption button, to either enable or disable subtitles based on the
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { DEFAULT_LANGUAGE } from '../base/i18n/i18next';
|
||||
|
||||
import {
|
||||
ENDPOINT_MESSAGE_RECEIVED,
|
||||
REMOVE_TRANSCRIPT_MESSAGE,
|
||||
SET_REQUESTING_SUBTITLES,
|
||||
TOGGLE_REQUESTING_SUBTITLES,
|
||||
UPDATE_TRANSCRIPT_MESSAGE
|
||||
UPDATE_TRANSCRIPT_MESSAGE,
|
||||
UPDATE_TRANSLATION_LANGUAGE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
@@ -81,23 +80,29 @@ export function toggleRequestingSubtitles() {
|
||||
* Signals that the local user has enabled or disabled the subtitles.
|
||||
*
|
||||
* @param {boolean} enabled - The new state of the subtitles.
|
||||
* @param {boolean} displaySubtitles - Whether to display subtitles or not.
|
||||
* @param {string} language - The language of the subtitles.
|
||||
* @returns {{
|
||||
* type: SET_REQUESTING_SUBTITLES,
|
||||
* enabled: boolean,
|
||||
* displaySubtitles: boolean,
|
||||
* language: string
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setRequestingSubtitles(
|
||||
enabled: boolean,
|
||||
displaySubtitles = true,
|
||||
language: string | null = `translation-languages:${DEFAULT_LANGUAGE}`) {
|
||||
export function setRequestingSubtitles(enabled: boolean) {
|
||||
return {
|
||||
type: SET_REQUESTING_SUBTITLES,
|
||||
displaySubtitles,
|
||||
enabled,
|
||||
language
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that the local user has selected language for the translation.
|
||||
*
|
||||
* @param {string} value - The selected language for translation.
|
||||
* @returns {{
|
||||
* type: UPDATE_TRANSLATION_LANGUAGE
|
||||
* }}
|
||||
*/
|
||||
export function updateTranslationLanguage(value: string) {
|
||||
return {
|
||||
type: UPDATE_TRANSLATION_LANGUAGE,
|
||||
value
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ export * from './actions.any';
|
||||
/**
|
||||
* Signals that the local user has toggled the LanguageSelector button.
|
||||
*
|
||||
* @returns {Function}
|
||||
* @returns {{
|
||||
* type: UPDATE_TRANSLATION_LANGUAGE
|
||||
* }}
|
||||
*/
|
||||
export function toggleLanguageSelectorDialog() {
|
||||
return function(dispatch: IStore['dispatch']) {
|
||||
|
||||
@@ -9,12 +9,7 @@ import { IReduxState } from '../../app/types';
|
||||
export interface IAbstractCaptionsProps {
|
||||
|
||||
/**
|
||||
* Whether local participant is displaying subtitles.
|
||||
*/
|
||||
_displaySubtitles: boolean;
|
||||
|
||||
/**
|
||||
* Whether local participant is requesting subtitles.
|
||||
* Whether local participant is requesting to see subtitles.
|
||||
*/
|
||||
_requestingSubtitles: boolean;
|
||||
|
||||
@@ -39,9 +34,9 @@ export class AbstractCaptions<P extends IAbstractCaptionsProps> extends Componen
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render(): any {
|
||||
const { _displaySubtitles, _requestingSubtitles, _transcripts } = this.props;
|
||||
const { _requestingSubtitles, _transcripts } = this.props;
|
||||
|
||||
if (!_requestingSubtitles || !_displaySubtitles || !_transcripts || !_transcripts.size) {
|
||||
if (!_requestingSubtitles || !_transcripts || !_transcripts.size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -100,7 +95,7 @@ function _constructTranscripts(state: IReduxState): Map<string, string> {
|
||||
|
||||
for (const [ id, transcriptMessage ] of _transcriptMessages) {
|
||||
if (transcriptMessage) {
|
||||
let text = `${transcriptMessage.participant.name}: `;
|
||||
let text = `${transcriptMessage.participantName}: `;
|
||||
|
||||
if (transcriptMessage.final) {
|
||||
text += transcriptMessage.final;
|
||||
@@ -130,11 +125,10 @@ function _constructTranscripts(state: IReduxState): Map<string, string> {
|
||||
* }}
|
||||
*/
|
||||
export function _abstractMapStateToProps(state: IReduxState) {
|
||||
const { _displaySubtitles, _requestingSubtitles } = state['features/subtitles'];
|
||||
const { _requestingSubtitles } = state['features/subtitles'];
|
||||
const transcripts = _constructTranscripts(state);
|
||||
|
||||
return {
|
||||
_displaySubtitles,
|
||||
_requestingSubtitles,
|
||||
|
||||
// avoid re-renders by setting to prop new empty Map instances.
|
||||
|
||||
@@ -2,13 +2,13 @@ import { createToolbarEvent } from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { MEET_FEATURES } from '../../base/jwt/constants';
|
||||
import { isLocalParticipantModerator } from '../../base/participants/functions';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../base/toolbox/components/AbstractButton';
|
||||
import { maybeShowPremiumFeatureDialog } from '../../jaas/actions';
|
||||
import { canStartTranscribing } from '../functions';
|
||||
|
||||
export interface IAbstractProps extends AbstractButtonProps {
|
||||
|
||||
_language: string | null;
|
||||
_language: string;
|
||||
|
||||
/**
|
||||
* Whether the local participant is currently requesting subtitles.
|
||||
@@ -103,10 +103,13 @@ export class AbstractClosedCaptionButton
|
||||
*/
|
||||
export function _abstractMapStateToProps(state: IReduxState, ownProps: IAbstractProps) {
|
||||
const { _requestingSubtitles, _language } = state['features/subtitles'];
|
||||
const { transcription } = state['features/base/config'];
|
||||
const { isTranscribing } = state['features/transcribing'];
|
||||
|
||||
// if the participant is moderator, it can enable transcriptions and if
|
||||
// transcriptions are already started for the meeting, guests can just show them
|
||||
const { visible = canStartTranscribing(state) } = ownProps;
|
||||
const { visible = Boolean(transcription?.enabled
|
||||
&& (isLocalParticipantModerator(state) || isTranscribing)) } = ownProps;
|
||||
|
||||
return {
|
||||
_requestingSubtitles,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { ComponentType, useCallback } from 'react';
|
||||
import React, { ComponentType, useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
TRANSLATION_LANGUAGES,
|
||||
TRANSLATION_LANGUAGES_HEAD
|
||||
} from '../../base/i18n/i18next';
|
||||
import { setRequestingSubtitles } from '../actions.any';
|
||||
import { setRequestingSubtitles, updateTranslationLanguage } from '../actions.any';
|
||||
|
||||
|
||||
export interface IAbstractLanguageSelectorDialogProps {
|
||||
dispatch: IStore['dispatch'];
|
||||
language: string | null;
|
||||
language: string;
|
||||
listItems: Array<any>;
|
||||
onLanguageSelected: (e: string) => void;
|
||||
subtitles: string;
|
||||
@@ -30,10 +30,10 @@ export interface IAbstractLanguageSelectorDialogProps {
|
||||
const AbstractLanguageSelectorDialog = (Component: ComponentType<IAbstractLanguageSelectorDialogProps>) => () => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const noLanguageLabel = 'transcribing.subtitlesOff';
|
||||
const off = 'transcribing.subtitlesOff';
|
||||
|
||||
const [ subtitles, setSubtiles ] = useState(off);
|
||||
const language = useSelector((state: IReduxState) => state['features/subtitles']._language);
|
||||
const subtitles = language ?? noLanguageLabel;
|
||||
|
||||
const transcription = useSelector((state: IReduxState) => state['features/base/config'].transcription);
|
||||
const translationLanguagesHead = transcription?.translationLanguagesHead ?? TRANSLATION_LANGUAGES_HEAD;
|
||||
@@ -42,7 +42,7 @@ const AbstractLanguageSelectorDialog = (Component: ComponentType<IAbstractLangua
|
||||
// The off and the head languages are always on the top of the list. But once you are selecting
|
||||
// a language from the translationLanguages, that language is moved under the fixedItems list,
|
||||
// until a new languages is selected. FixedItems keep their positions.
|
||||
const fixedItems = [ noLanguageLabel, ...languagesHead ];
|
||||
const fixedItems = [ off, ...languagesHead ];
|
||||
const translationLanguages = transcription?.translationLanguages ?? TRANSLATION_LANGUAGES;
|
||||
const languages = translationLanguages
|
||||
.map((lang: string) => `translation-languages:${lang}`)
|
||||
@@ -58,12 +58,14 @@ const AbstractLanguageSelectorDialog = (Component: ComponentType<IAbstractLangua
|
||||
};
|
||||
});
|
||||
|
||||
const onLanguageSelected = useCallback((value: string) => {
|
||||
const selectedLanguage = value === noLanguageLabel ? null : value;
|
||||
const enabled = Boolean(selectedLanguage);
|
||||
const displaySubtitles = enabled;
|
||||
useEffect(() => {
|
||||
language ? setSubtiles(language) : setSubtiles(off);
|
||||
}, []);
|
||||
|
||||
dispatch(setRequestingSubtitles(enabled, displaySubtitles, selectedLanguage));
|
||||
const onLanguageSelected = useCallback((e: string) => {
|
||||
setSubtiles(e);
|
||||
dispatch(updateTranslationLanguage(e));
|
||||
dispatch(setRequestingSubtitles(e !== off));
|
||||
}, [ language ]);
|
||||
|
||||
return (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user