Compare commits

...

42 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
8eed42c273 fix(virtual-backgrounds) add segmentation model license information
Fixes: https://github.com/jitsi/jitsi-meet/issues/8792
2021-04-07 17:15:17 +02:00
tmoldovan8x8
e803e8cfd9 feat(ios): adds ios screensharing enabled flag 2021-04-07 16:28:26 +03:00
tudordan7
e5277deed5 chore(deps) lib-jitsi-meet@latest
* fix(rtc) Fix setting effects while not in a conference.

3cd9d31b97...cd53f249c5
2021-04-07 13:12:36 +02:00
Tudor D. Pop
8b315846b9 feat(premeeting-screen) add virtual background functionality 2021-04-07 11:29:54 +02:00
Jaya Allamsetty
c687f41a89 chore(deps) lib-jitsi-meet@latest
* feat(RTC): Signal video type and availability to bridge.

dddbab99f1...3cd9d31b97
2021-04-06 17:46:04 -04:00
Jonathan Lennox
31c0ba4481 Load-test: emulate jitsi-meet stage view behavior, if selected. (#8957) 2021-04-06 16:31:26 -04:00
Calinteodor
fc3a743372 fix(ios) keyboard no longer covers message board and input 2021-04-06 12:07:24 +02:00
damencho
8b038716a5 chore(deps) lib-jitsi-meet@latest
* fix: Fixes error for undefined error, on happening on p2p kick.

2e598a4bda...dddbab99f1
2021-04-05 16:49:58 -05:00
Jonathan Lennox
9662b2ae67 Load test: send video constraints only after ICE is connected. (#8952) 2021-04-05 17:17:25 -04:00
Jonathan Lennox
6275439a91 Load-test: Fix getId call. (#8941) 2021-04-05 12:03:54 -04:00
Vlad Piersec
d9693117f2 fix(Toolbar, rn): Button overflow in landscape orientation 2021-04-05 13:54:44 +03:00
Jaya Allamsetty
21382ea6d5 chore(deps) lib-jitsi-meet@latest
* Get rid of stats debug message, fix typo with codec type.
* fix(receiveVideoController): Do a deep copy of constraints for comparsion.
* fix(codec-selection): Fix codec selection for unified plan browsers.

93af5ada95...2e598a4bda
2021-04-02 16:18:44 -04:00
JohnProv
6df67694d1 Update main-nl.json (#8938)
Remove keys in main-nl but not in main.
2021-04-02 12:01:32 -05:00
JohnProv
08756bc6d0 Update main-nl.json (#8937)
* Update main-nl.json

Add some translated keys.

* Update main-nl.json

Fix

* Update main-nl.json

Fix typo

* Update main-nl.json

Fix
2021-04-02 11:25:23 -05:00
Mihai-Andrei Uscat
1b1d650b75 fix(MoreTab): Fix languages not being scrollable on mobile 2021-04-02 13:38:02 +03:00
Jaya Allamsetty
b1eff72394 chore(deps) lib-jitsi-meet@latest
* fix(receiveVideoController): Do not send redundant video constraints to the bridge.
* feat(stats): Add a new bridge message "EndpointStats" for stats. Use the new Colibri message "EndpointStats" for broadcasting the local stats. The bridge then will be able to filter the endpoint stats and send them only to the interested parties instead of broadcasting it to all the endpoints in the call.
* Test RTCRtpReceiver.getCapabilities before using

2b94da12e8...93af5ada95
2021-04-01 10:44:22 -04:00
Jonathan Lennox
357bbd1158 Load test: emulate Jitsi-Meet's lastN and selectParticipant behavior. (#8926) 2021-04-01 10:30:23 -04:00
Arnaud (Martient) Leherpeur
0ca47e9ffb fix (lang): update french and canadian french i18n
change "cryptage" to "chiffrement"
2021-04-01 08:29:12 -05:00
Дамян Минков
1123b4f2fe fix: Adds Portuguese to listed languages 2021-04-01 08:27:49 -05:00
tmoldovan8x8
1224597ede feat(e2ee): auto turns on e2ee when one participant enabled it 2021-04-01 12:34:01 +03:00
Avram Tudor
58b7663a97 Merge pull request #8866 from jitsi/tavram/sip-invite
feat(sipcall) implement sip invite
2021-04-01 11:39:31 +03:00
Christoph Settgast
cf8ab5e13b fix(lang) Differentiate prejoin and lobby better in German translation
Signed-off-by: Christoph Settgast <csett86@web.de>
2021-03-31 15:46:05 -05:00
Tudor-Ovidiu Avram
f99c919416 code review changes 2021-03-31 15:51:53 +03:00
Tudor-Ovidiu Avram
ae21a09bd6 feat(sipcall) implement sip invite 2021-03-31 09:53:55 +03:00
Tudor D. Pop
39011d8fd3 feat(virtual-background) persist settings 2021-03-30 23:27:44 +02:00
Johnny998
77f1a24344 Update main-sk.json
Translated a few missing strings.
2021-03-30 08:41:32 -05:00
tudordan7
3453e49182 fix(virtual-background): Hide scrollbar on loading action. 2021-03-30 13:43:57 +02:00
tmoldovan8x8
b1d7debfb9 feat(e2ee): adds sounds for e2ee enabling/disabling 2021-03-30 12:59:32 +03:00
Дамян Минков
b826fc1d5a fix: Correct some missing comas in config.js. 2021-03-30 08:51:43 +02:00
Jaya Allamsetty
c5626e99e9 chore(deps) lib-jitsi-meet@latest
* feat(stats): Get audio levels for the top 5 speakers only.

1249681a0e...43c589f409
2021-03-29 17:21:46 -04:00
Christoph Wiechert
ae28fcc12f Fix: used deprecated onmousewheel event
https://developer.mozilla.org/en-US/docs/Web/API/Element/mousewheel_event
2021-03-29 10:53:13 -05:00
chipechop
987760abbd Update main-it.json (#8795)
* Update main-it.json

Added roughly 20 missinig lines

* Update main-it.json

Fixed two typos, left behind...
2021-03-29 10:53:02 -05:00
KyungheeKo
a3b364f8d7 lang: Update korean translation (#8879)
* Update main-ko.json

update korean translation

* Update main-ko.json

fix comma error

* Update languages-ko.json

add korean translation
2021-03-29 10:52:50 -05:00
JohnProv
2ea317d721 Update main-nl.json (#8891)
* Update main-nl.json

Add missing keys for virtualBackground

* Update main-nl.json
2021-03-29 10:52:22 -05:00
Vlad Piersec
eb41a306a6 fix(lobby): Knocking participants list for small widths 2021-03-29 09:47:11 -05:00
Vlad Piersec
3426290bf2 fix(captions): Lift captions upper when invite box is shown & fix icon 2021-03-29 09:09:21 -05:00
Tudor D. Pop
dfd33521bf fix(virtual-background): Fixes upload virtual background on Firefox
Fixes: #8892
2021-03-29 14:28:22 +02:00
Hristo Terezov
be3bc75403 chore(deps) lib-jitsi-meet@latest
* fix(caps): features update event is not emitted.

0e180efdfa...1249681a0e
2021-03-26 17:43:54 -05:00
Jaya Allamsetty
4621fad832 fix(large-video): Always pin screenshare to large-video if it exists.
Set higher preference for screenshare over dominant speaker when trying to elect a participant for large-video. This prevents the dominant speaker from taking over the stage when a user toggles tile view on and off while a screenshare is in progress.
2021-03-26 09:14:03 -04:00
tmoldovan8x8
e4b34e1c89 feat(rn): makes InputDialog textInput autoFocus 2021-03-26 10:51:47 +02:00
damencho
0067f6b077 fix: Fixes lobby when allowners is enabled. 2021-03-25 15:20:49 -06:00
JohnProv
989044b3a9 fix(lang) update Dutch translation 2021-03-25 17:43:52 +01:00
46 changed files with 767 additions and 236 deletions

View File

@@ -123,7 +123,7 @@ var config = {
// opusMaxAverageBitrate: 20000,
// Enables support for opus-red (redundancy for Opus).
// enableOpusRed: false
// enableOpusRed: false,
// Video
@@ -498,7 +498,7 @@ var config = {
// If set to true, it will prefer to use H.264 for P2P calls (if H.264
// is supported). This setting is deprecated, use preferredCodec instead.
// preferH264: true
// preferH264: true,
// Provides a way to set the video codec preference on the p2p connection. Acceptable
// codec values are 'VP8', 'VP9' and 'H264'.
@@ -540,7 +540,7 @@ var config = {
// The interval at which rtcstats will poll getStats, defaults to 1000ms.
// If the value is set to 0 getStats won't be polled and the rtcstats client
// will only send data related to RTCPeerConnection events.
// rtcstatsPolIInterval: 1000
// rtcstatsPolIInterval: 1000,
// Array of script URLs to load as lib-jitsi-meet "analytics handlers".
// scriptURLs: [
@@ -689,13 +689,13 @@ var config = {
// disableTileView: true,
// Hides the conference subject
// hideConferenceSubject: true
// hideConferenceSubject: true,
// Hides the conference timer.
// hideConferenceTimer: true,
// Hides the participants stats
// hideParticipantsStats: true
// hideParticipantsStats: true,
// Sets the conference subject
// subject: 'Conference Subject',

View File

@@ -9,7 +9,7 @@
.spinner {
margin: 30px;
}
.joining-message {
margin: 10px;
}
@@ -49,7 +49,7 @@
position: fixed;
top: 20;
transition: top 1s ease;
z-index: 100;
z-index: $toolbarZ + 1;
&.toolbox-visible {
// Same as toolbox subject position
@@ -62,31 +62,6 @@
padding: 15px
}
ul {
list-style-type: none;
padding: 0 15px 15px 15px;
li {
align-items: center;
display: flex;
flex-direction: row;
margin: 8px 0;
.details {
display: flex;
flex: 1;
flex-direction: column;
justify-content: space-evenly;
margin: 0 30px 0 10px;
}
button {
align-self: unset;
margin: 0 5px;
}
}
}
button {
align-self: stretch;
margin: 8px 0;
@@ -116,3 +91,50 @@
}
}
}
.knocking-participants-container {
list-style-type: none;
max-height: 600px;
overflow-y: scroll;
padding: 0 15px 15px 15px;
}
.knocking-participant {
align-items: center;
display: flex;
flex-direction: row;
margin: 8px 0;
.details {
display: flex;
flex: 1;
flex-direction: column;
justify-content: space-evenly;
margin: 0 30px 0 10px;
}
button {
align-self: unset;
margin: 0 5px;
}
}
@media (max-width: 300px) {
#knocking-participant-list {
margin: 0;
text-align: center;
width: 100%;
.avatar {
display: none;
}
}
.knocking-participant {
flex-direction: column;
.details {
margin: 0;
}
}
}

View File

@@ -1,10 +1,11 @@
.transcription-subtitles{
bottom: 10%;
.transcription-subtitles {
bottom: $newToolbarSize + 40px;
font-size: 16px;
font-weight: 1000;
left: 50%;
max-width: 50vw;
opacity: 0.80;
overflow-wrap: break-word;
pointer-events: none;
position: absolute;
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
@@ -14,6 +15,11 @@
transform: translateX(-50%);
z-index: $subtitlesZ;
&.lifted {
// Lift subtitle above toolbar+invite box.
bottom: $newToolbarSize + 112px + 40px;
}
span {
background: black;
}

View File

@@ -51,6 +51,10 @@
margin-right: 5px;
}
}
.modal-dialog-form .virtual-background-loading {
overflow: hidden;
}
.file-upload-btn {
display: none;
}

View File

@@ -35,6 +35,7 @@
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
[builder setFeatureFlag:@"resolution" withValue:@(360)];
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
builder.welcomePageEnabled = YES;

View File

@@ -1,27 +1,52 @@
{
"en": "영어",
"af": "",
"af": "아프리칸스어",
"ar": "아랍어",
"az": "아제르바이잔어",
"bg": "불가리어",
"cs": "체코어",
"ca": "카탈루냐어",
"cs": "체코어",
"da": "덴마크어",
"de": "독일어",
"el": "그리스어",
"enGB": "영어(영국)",
"eo": "에스페란토어",
"es": "스페인어",
"esUS": "스페인어(라틴 아메리카)",
"et": "에스토니아어",
"eu": "바스크어",
"fi": "핀란드어",
"fr": "프랑스어",
"frCA": "프랑스어(캐나다)",
"he": "히브리어",
"mr":"마라티어",
"hr": "크로아티아어",
"hu": "헝가리어",
"hy": "아르메니아어",
"id": "인도네시아어",
"it": "이탈리아어",
"ja": "일본어",
"kab": "커바일어",
"ko": "한국어",
"nb": "노르웨이어",
"oc": "",
"lt": "리투아니아어",
"ml": "말라얄람어",
"lv": "라트비아어",
"nl": "네덜란드어",
"oc": "오크어",
"fa": "페르시아어",
"pl": "폴란드어",
"ptBR": "포르투갈어(브라질)",
"ru": "러시아어",
"ro": "루마니아어",
"sc": "사르데냐어",
"sk": "슬로바키아어",
"sl": "슬로베니아어",
"sr": "세르비아어",
"sv": "스웨덴어",
"th": "태국어",
"tr": "터키어",
"uk": "우크라이나어",
"vi": "베트남어",
"zhCN": "중국어(중국)"
}
"zhCN": "중국어(중국)",
"zhTW": "중국어(대만)"
}

View File

@@ -34,6 +34,7 @@
"oc": "Occitan",
"fa": "Persian",
"pl": "Polish",
"pt": "Portuguese",
"ptBR": "Portuguese (Brazil)",
"ru": "Russian",
"ro": "Romanian",

View File

@@ -562,8 +562,8 @@
"linkCopied": "Link in die Zwischenablage kopiert",
"lookGood": "Ihr Mikrofon scheint zu funktionieren.",
"or": "oder",
"premeeting": "Vorraum",
"showScreen": "Konferenzvorraum aktivieren",
"premeeting": "Vorschau",
"showScreen": "Konferenzvorschau aktivieren",
"startWithPhone": "Mit Telefonaudio starten",
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
"videoOnlyError": "Videofehler:",

View File

@@ -201,9 +201,9 @@
"dismiss": "Rejeter",
"displayNameRequired": "Bonjour ! Quel est votre nom ?",
"done": "Terminé",
"e2eeDescription": "Le cryptage de Bout-en-Bout est actuellement EXPERIMENTAL. Veuillez garder à l'esprit que l'activation du cryptage de Bout-en-Bout désactivera les services fournis côté serveur tels que : l'enregistrement, la diffusion en direct et la participation par téléphone. Gardez également à l'esprit que la réunion ne fonctionnera que pour les personnes qui se joignent à partir de navigateurs prenant en charge les flux insérables.",
"e2eeLabel": "Activer le cryptage de Bout-en-Bout",
"e2eeWarning": "ATTENTION : Tous les participants de cette réunion ne semblent pas prendre en charge le chiffrement de Bout-en-Bout. Si vous activez le cryptage, ils ne pourront ni vous voir, ni vous entendre.",
"e2eeDescription": "Le chiffrement de Bout-en-Bout est actuellement EXPERIMENTAL. Veuillez garder à l'esprit que l'activation du chiffrement de Bout-en-Bout désactivera les services fournis côté serveur tels que : l'enregistrement, la diffusion en direct et la participation par téléphone. Gardez également à l'esprit que la réunion ne fonctionnera que pour les personnes qui se joignent à partir de navigateurs prenant en charge les flux insérables.",
"e2eeLabel": "Activer le chiffrement de Bout-en-Bout",
"e2eeWarning": "ATTENTION : Tous les participants de cette réunion ne semblent pas prendre en charge le chiffrement de Bout-en-Bout. Si vous activez le chiffrement, ils ne pourront ni vous voir, ni vous entendre.",
"enterDisplayName": "Merci de saisir votre nom ici",
"error": "Erreur",
"externalInstallationMsg": "Vous devez installer notre extension de partage de bureau.",
@@ -326,7 +326,7 @@
"title": "Document partagé"
},
"e2ee": {
"labelToolTip": "L'audio et la vidéo de cette conférence sont cryptés de Bout-en-Bout"
"labelToolTip": "L'audio et la vidéo de cette conférence sont chiffrés de Bout-en-Bout"
},
"embedMeeting": {
"title": "Intégrer cette réunion"
@@ -757,7 +757,7 @@
"documentClose": "Fermer le document partagé",
"documentOpen": "Ouvrir le document partagé",
"download": "Télécharger nos applications",
"e2ee": "Cryptage de Bout-en-Bout",
"e2ee": "Chiffrement de Bout-en-Bout",
"embedMeeting": "Intégrer la réunion",
"enterFullScreen": "Afficher en plein écran",
"enterTileView": "Accéder au mode mosaïque",

View File

@@ -718,7 +718,7 @@
"join": "Toucher pour rejoindre",
"roomname": "Entrer le nom de la salle"
},
"appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de conférence vidéo entièrement cryptée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",
"appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de conférence vidéo entièrement chiffrée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",
"audioVideoSwitch": {
"audio": "Téléphone",
"video": "Vidéo"

View File

@@ -61,6 +61,7 @@
"today": "Oggi"
},
"chat": {
"enter": "Entra nella conversazione",
"error": "Errore: il tuo messaggio non è stato inviato. Motivo: {{error}}",
"fieldPlaceHolder": "Scrivi qui il tuo messaggio",
"messagebox": "Digitare un messaggio",
@@ -240,12 +241,19 @@
"muteEveryoneElseTitle": "Zittisco tutti eccetto {{whom}}?",
"muteEveryoneDialog": "Sei sicuro di voler zittire tutti? Non potrai riattivar loro il microfono, ma loro potranno farlo in qualsiasi momento.",
"muteEveryoneTitle": "Zittisco tutti?",
"muteEveryoneElsesVideoDialog": "Una volta spente le videocamere, non potrai riaccenderle, ma ogni partecipante potrà farlo in ogni momento.",
"muteEveryoneElsesVideoTitle": "Spegnere tutte le videocamere, tranne quella di {{whom}}?",
"muteEveryonesVideoDialog": "Sei sicuro di voler spegnere le videocamere di tutti? Non potrai riaccenderle, ma ogni partecipante potrà farlo in ogni momento.",
"muteEveryonesVideoTitle": "Vuoi spegnere le videocamere di tutti?",
"muteEveryoneSelf": "te stesso",
"muteEveryoneStartMuted": "Tutti cominciano a microfono spento, d'adesso in avanti",
"muteParticipantBody": "Non sarai in grado di riattivare il loro microfono, ma loro potranno riattivarlo in qualsiasi momento.",
"muteParticipantButton": "Zittisci",
"muteParticipantDialog": "Sei sicuro di voler zittire questo partecipante? Sarà lui a dovere riattivare l'audio, per parlare.",
"muteParticipantTitle": "Zittisco questo partecipante?",
"muteParticipantsVideoButton": "Spegnere videocamera",
"muteParticipantsVideoTitle": "Vuoi spegnere la videocamera di questo partecipante?",
"muteParticipantsVideoBody": "Una volta spenta la videocamera, non potrai riaccenderla, ma lui potrà riattivarla in qualsiasi momento.",
"Ok": "OK",
"passwordLabel": "La riunione è stata bloccata da un partecipante. Immetti la $t(lockRoomPassword) per collegarti, per favore.",
"passwordNotSupported": "Impostare una $t(lockRoomPassword) non è supportato.",
@@ -305,6 +313,7 @@
"unlockRoom": "Togli la $t(lockRoomPassword) alla riunione",
"user": "utente",
"userPassword": "password utente",
"videoLink": "Collegamento video",
"WaitForHostMsg": "La riunione <b>{{room}}</b> non è ancora cominciata. Se sei l'organizzatore, per favore autenticati. Altrimenti, aspetta l'arrivo dell'organizzatore.",
"WaitForHostMsgWOk": "La riunione <b>{{room}}</b> non è ancora cominciata. Se sei l'organizzatore, allora premi OK per autenticarti. Altrimenti, aspetta l'arrivo dell'organizzatore.",
"WaitingForHost": "In attesa dell'organizzatore...",
@@ -323,6 +332,11 @@
"embedMeeting": {
"title": "Incorpora questa riunione"
},
"virtualBackground": {
"title": "Sfondi",
"enableBlur": "Attiva sfocatura",
"removeBackground": "Togli sfondo"
},
"feedback": {
"average": "Media",
"bad": "Scadente",
@@ -483,6 +497,8 @@
"mutedTitle": "Hai l'audio disattivato!",
"mutedRemotelyTitle": "Ti è stato disattivato l'audio da {{participantDisplayName}}!",
"mutedRemotelyDescription": "Puoi sempre attivare il microfono, quando vuoi parlare. Spegni il microfono quando hai finito, per non introdurre rumori di fondo nella riunione.",
"videoMutedRemotelyTitle": "La videocamera ti è stata spenta da {{participantDisplayName}}!",
"videoMutedRemotelyDescription": "Puoi riaccenderla in qualsiasi momento.",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) è stata tolta da un altro partecipante",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) è stata messa da un altro partecipante",
"raisedHand": "{{name}} vorrebbe intervenire.",
@@ -715,12 +731,16 @@
"moreOptions": "Più opzioni",
"mute": "Attiva/disattiva audio",
"muteEveryone": "Zittisci tutti",
"muteEveryoneElse": "Zittisci tutti gli altri",
"muteEveryonesVideo": "Spegni le videocamere di tutti",
"muteEveryoneElsesVideo": "Spegni le videocamere di tutti gli altri",
"pip": "Attiva/disattiva immagine nellimmagine",
"privateMessage": "Invia messaggio privato",
"profile": "Modifica profilo",
"raiseHand": "Attiva/disattiva alzata di mano",
"recording": "Attiva/disattiva registrazione",
"remoteMute": "Zittisci partecipante",
"remoteVideoMute": "Spegni videocamera del partecipante",
"security": "Impostazioni di sicurezza",
"Settings": "Attiva/disattiva impostazioni",
"sharedvideo": "Attiva/disattiva condivisione YouTube",
@@ -733,9 +753,10 @@
"toggleCamera": "Cambia videocamera",
"toggleFilmstrip": "Attiva/disattiva pellicola",
"videomute": "Attiva/disattiva videocamera",
"videoblur": "Attiva/disattiva offuscamento video"
"selectBackground": "Scegli sfondo"
},
"addPeople": "Aggiungi persone alla chiamata",
"audioSettings": "Impostazioni audio",
"audioOnlyOff": "Disabilita modalità per banda limitata",
"audioOnlyOn": "Abilita modalità per banda limitata",
"audioRoute": "Scegli l'uscita audio",
@@ -765,6 +786,7 @@
"moreOptions": "Più opzioni",
"mute": "Attiva / Disattiva microfono",
"muteEveryone": "Zittisci tutti",
"muteEveryonesVideo": "Spegni videocamera di tutti",
"noAudioSignalTitle": "Non arrivano suoni dal tuo microfono!",
"noAudioSignalDesc": "Se non l'hai disabilitato intenzionalmente nelle impostazioni, prova a cambiare dispositivo di input.",
"noAudioSignalDescSuggestion": "Se non l'hai disabilitato intenzionalmente nelle impostazioni, prova a scegliere il dispositivo consigliato.",
@@ -793,7 +815,6 @@
"tileViewToggle": "Vedi tutti i partecipanti insieme, o uno solo",
"toggleCamera": "Cambia videocamera",
"videomute": "Attiva / Disattiva videocamera",
"startvideoblur": "Sfoca lo sfondo del video",
"stopvideoblur": "Non sfocare lo sfondo video"
},
"transcribing": {
@@ -849,13 +870,16 @@
"videothumbnail": {
"connectionInfo": "Info connessione",
"domute": "Disattiva audio",
"domuteOthers": "Zittisci tutti gli altri",
"domuteVideo": "Disattiva video",
"domuteOthers": "Zittisci tutti gli altri",
"domuteVideoOfOthers": "Disattiva video di tutti gli altri",
"flip": "Rifletti",
"grantModerator": "Autorizza moderatore",
"kick": "Espelli",
"moderator": "Moderatore",
"mute": "Il partecipante ha il microfono spento",
"muted": "Audio disattivato",
"videoMuted": "Video disattivato",
"remoteControl": "Avvia/ferma il controllo remoto",
"show": "Mostra in primo piano",
"videomute": "Il partecipante ha la videocamera spenta"

View File

@@ -57,10 +57,11 @@
"ongoingMeeting": "진행중인 회의",
"permissionButton": "설정 열기",
"permissionMessage": "앱에 회의를 나열하려면 캘린더 권한이 필요합니다",
"refresh": "달력 새로고침",
"refresh": "캘린더 새로고침",
"today": "오늘"
},
"chat": {
"enter": "채팅방 입장",
"error": "오류 : 메시지가 전송되지 않았습니다. 이유 : {{error}}",
"fieldPlaceHolder": "메세지를 여기에 입력하세요",
"messagebox": "메시지 입력",
@@ -212,7 +213,7 @@
"lockMessage": "회의를 비공개하지 못했습니다",
"lockRoom": "회의 추가 $t(lockRoomPasswordUppercase)",
"lockTitle": "비공개 실패",
"logoutQuestion": "로그 아웃하고 컨퍼런스를 중지하시겠습니까?",
"logoutQuestion": "로그아웃하고 컨퍼런스를 중지하시겠습니까?",
"logoutTitle": "로그아웃",
"maxUsersLimitReached": "회의의 최대 참가자 수에 도달했습니다. 회의 소유자에게 연락하거나 나중에 다시 시도하십시오!",
"maxUsersLimitReachedTitle": "최대 참가자 수에 도달했습니다.",
@@ -222,10 +223,23 @@
"micNotSendingDataTitle": "시스템 설정에 의해 마이크가 음소거되었습니다.",
"micPermissionDeniedError": "마이크를 사용할 수있는 권한을 부여하지 않았습니다. 회의에 계속 참여할 수는 있지만 다른 사람들은 듣지 않습니다. 검색 주소창의 카메라 버튼을 사용하여 문제를 해결하십시오.",
"micUnknownError": "알 수 없는 이유로 마이크를 사용할 수 없습니다",
"muteEveryoneElseDialog": "당신이 다른 사람들의 음소거를 해제 할 수는 없지만 언제든지 다른 사람들은 스스로 음소거를 해제할 수 있습니다.",
"muteEveryoneElseTitle": "{{whom}} 를 제외하고 전부 음소거 하시겠습니까?",
"muteEveryoneDialog": "모든 참가자를 음소거 하시겠습니까? 당신이 다른 사람들의 음소거를 해제 할 수는 없지만 언제든지 다른 사람들은 스스로 음소거를 해제할 수 있습니다.",
"muteEveryoneTitle": "모두 음소거 하시겠습니까?",
"muteEveryoneElsesVideoDialog": "당신이 다른 사람들의 카메라를 다시 켤 수는 없지만 언제든지 다른 사람들은 스스로 카메라를 켤 수 있습니다.",
"muteEveryoneElsesVideoTitle": "{{whom}} 를 제외하고 전부 카메라를 비활성화 하시겠습니까?",
"muteEveryonesVideoDialog": "모든 참가자의 카메라를 비활성화 하시겠습니까? 당신이 다른 사람들의 카메라를 다시 켤 수는 없지만 언제든지 다른 사람들은 스스로 카메라를 켤 수 있습니다.",
"muteEveryonesVideoDialogOk": "비활성화",
"muteEveryonesVideoTitle": "모든 카메라를 비활성화 하시겠습니까?",
"muteEveryoneStartMuted": "지금부터 모두 음소거 됩니다.",
"muteParticipantBody": "당신이 다른 사람들의 음소거를 해제 할 수는 없지만 언제든지 다른 사람들은 스스로 음소거를 해제할 수 있습니다.",
"muteParticipantButton": "음소거",
"muteParticipantDialog": "",
"muteParticipantDialog": "이 참가자를 음소거 하시겠습니까? 당신이 다른 사람들의 음소거를 해제 할 수는 없지만 언제든지 다른 사람들은 스스로 음소거를 해제할 수 있습니다.",
"muteParticipantTitle": "이 참가자를 음소거 하시겠습니까?",
"muteParticipantsVideoButton": "카메라 비활성화",
"muteParticipantsVideoTitle": "이 참가자의 카메라를 비활성화 하시겠습니까?",
"muteParticipantsVideoBody": "당신이 다른 사람들의 카메라를 다시 켤 수는 없지만 언제든지 다른 사람들은 스스로 카메라를 켤 수 있습니다.",
"Ok": "확인",
"passwordLabel": "잠긴 회의입니다. 회의에 참여하려면 비밀번호를 입력하세요.",
"passwordNotSupported": "회의 비밀번호 설정은 지원되지 않습니다",
@@ -233,7 +247,9 @@
"passwordRequired": "비밀번호 필수",
"popupError": "브라우저가이 사이트의 팝업 창을 차단하고 있습니다. 브라우저의 보안 설정에서 팝업을 활성화하고 다시 시도하십시오.",
"popupErrorTitle": "팝업 차단됨",
"readMore": "더보기",
"recording": "녹화",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "라이브 스트리밍 중에는 사용하실 수 없습니다.",
"recordingDisabledForGuestTooltip": "게스트는 녹화를 시작할 수 없습니다.",
"recordingDisabledTooltip": "녹화가 비활성화 되었습니다.",
"rejoinNow": "지금 재가입",
@@ -297,6 +313,14 @@
"documentSharing": {
"title": "문서 공유"
},
"virtualBackground": {
"title": "배경",
"enableBlur": "흐린 배경 활성화",
"removeBackground": "배경 제거",
"uploadImage": "이미지 업로드",
"pleaseWait": "잠시만 기다려주세요...",
"none": "없음"
},
"feedback": {
"average": "보통",
"bad": "나쁨",
@@ -322,12 +346,12 @@
"dialANumber": "회의에 참여하려면이 번호 중 하나를 누른 다음 PIN을 입력하십시오.",
"dialInConferenceID": "PIN:",
"dialInNotSupported": "죄송합니다. 현재 전화를 걸 수 없습니다.",
"dialInNumber": "Dial-in:",
"dialInNumber": "전화 접속:",
"dialInSummaryError": "지금 전화 접속 정보를 가져 오는 중에 오류가 발생했습니다. 나중에 다시 시도하십시오.",
"dialInTollFree": "",
"genericError": "일반적인 오류가 발생했습니다",
"inviteLiveStream": "이 회의의 실시간 스트림을 보려면이 링크를 클릭하십시오: {{url}}",
"invitePhone": "",
"invitePhone": "폰으로 참여하려면, 이것을 누르십시오: {{number}},,{{conferenceID}}#\n",
"invitePhoneAlternatives": "",
"inviteURLFirstPartGeneral": "회의에 초대되었습니다.",
"inviteURLFirstPartPersonal": "{{name}}이 회의에 초대하였습니다.\n",
@@ -406,9 +430,9 @@
},
"localRecording": {
"clientState": {
"off": "",
"on": "",
"unknown": ""
"off": "꺼짐",
"on": "켜짐",
"unknown": "알 수 없음"
},
"dialogTitle": "",
"duration": "",
@@ -417,7 +441,7 @@
"label": "",
"labelToolTip": "",
"localRecording": "",
"me": "",
"me": "",
"messages": {
"engaged": "",
"finished": "",
@@ -454,6 +478,8 @@
"mutedTitle": "음소거 상태입니다!",
"mutedRemotelyTitle": "{{participantDisplayName}}에 의해 음소거되었습니다!",
"mutedRemotelyDescription": "말할 준비가되면 언제든지 음소거를 해제 할 수 있습니다.",
"videoMutedRemotelyTitle": "{{participantDisplayName}}에 의해 카메라가 비활성화되었습니다!",
"videoMutedRemotelyDescription": "언제든지 카메라를 다시 켤 수 있습니다.",
"passwordRemovedRemotely": "다른 참가자가 $t(lockRoomPasswordUppercase)를 제거했습니다.",
"passwordSetRemotely": "다른 참가자가 $t(lockRoomPasswordUppercase)를 설정했습니다.",
"raisedHand": "{{name}}님이 말하고 싶어합니다.",
@@ -518,6 +544,12 @@
"sectionList": {
"pullToRefresh": "당겨서 새로고침"
},
"security": {
"about": "회의에 $t(lockRoomPassword)를 추가할 수 있습니다. 참가자는 회의에 참여하기 위해 $t(lockRoomPassword)를 입력해야합니다.",
"aboutReadOnly": "방장은 회의에 $t(lockRoomPassword)를 추가할 수 있습니다. 참가자는 회의에 참여하기 위해 $t(lockRoomPassword)를 입력해야합니다.",
"insecureRoomNameWarning": "원하지 않은 참가자가 회의에 참여할 수 있습니다. 보안 옵션 버튼을 통해 회의를 보호하는 것을 고려하십시오.",
"securityOptions": "보안 옵션"
},
"settings": {
"calendar": {
"about": "{{appName}} 캘린더 통합은 예정된 일정을 읽을 수 있도록 캘린더에 안전하게 액세스하는 데 사용됩니다.",
@@ -582,40 +614,49 @@
},
"toolbar": {
"accessibilityLabel": {
"audioOnly": "",
"audioOnly": "음성 전용 모드 전환",
"audioRoute": "음성 장비 선택하기",
"callQuality": "",
"cc": "",
"chat": "",
"document": "",
"callQuality": "비디오 품질 관리",
"cc": "자막 사용 전환",
"chat": "채팅창 보이기 전환",
"document": "문서 전환",
"feedback": "피드백 남기기",
"fullScreen": "",
"hangup": "",
"invite": "",
"kick": "",
"fullScreen": "전체 화면 전환",
"hangup": "떠나기",
"help": "도움말",
"invite": "사용자 초대",
"kick": "참가자 추방",
"lobbyButton": "로비 모드 활성화/비활성화",
"localRecording": "",
"lockRoom": "",
"lockRoom": "회의 비밀번호 전환",
"moreActions": "",
"moreActionsMenu": "",
"mute": "",
"mute": "음소거 전환",
"muteEveryone": "모두 음소거",
"muteEveryoneElse": "다른 사람 모두 음소거",
"muteEveryonesVideo": "모든 카메라 비활성화",
"muteEveryoneElsesVideo": "다른 사람의 카메라 모두 비활성화",
"pip": "",
"profile": "",
"raiseHand": "",
"recording": "",
"remoteMute": "",
"Settings": "",
"sharedvideo": "",
"shareRoom": "",
"shareYourScreen": "",
"privateMessage": "비공개 메세지 보내기",
"profile": "프로필 수정",
"raiseHand": "손 들기",
"recording": "녹화 전환",
"remoteMute": "참가자 음소거",
"Settings": "설정 전환",
"sharedvideo": "YouTube 비디오 공유 전환",
"shareRoom": "초대하기",
"shareYourScreen": "화면 공유 전환",
"shortcuts": "단축키 전환",
"show": "",
"speakerStats": "",
"tileView": "",
"speakerStats": "접속자 통계 전환",
"tileView": "타일뷰 전환",
"toggleCamera": "카메라 전환",
"videomute": "",
"videoblur": ""
"videomute": "비디오 비활성화 전환",
"videoblur": "",
"selectBackground": "배경 선택"
},
"addPeople": "통화에 사용자 추가",
"audioSettings": "오디오 설정",
"audioOnlyOff": "음성전용 모드 끄기",
"audioOnlyOn": "음성전용 모드 끄기",
"audioRoute": "음성 장비 선택하기",
@@ -632,6 +673,7 @@
"exitTileView": "타일보기 종료",
"feedback": "피드백 남기기",
"hangup": "떠나기",
"help": "도움말",
"invite": "초대",
"login": "로그인",
"logout": "로그아웃",
@@ -646,6 +688,7 @@
"profile": "프로필 수정",
"raiseHand": "말하기 요청/해제",
"raiseYourHand": "손 들어주세요",
"security": "보안 옵션",
"Settings": "설정",
"sharedvideo": "YouTube 비디오 공유",
"shareRoom": "초대하기",
@@ -655,11 +698,13 @@
"startSubtitles": "자막 시작",
"stopScreenSharing": "화면 공유 중지",
"stopSubtitles": "자막 중지",
"stopSharedVideo": "UouTube 비디오 공유 중지",
"stopSharedVideo": "YouTube 비디오 공유 중지",
"talkWhileMutedPopup": "음소거 상태입니다.",
"tileViewToggle": "타일뷰 전환",
"toggleCamera": "카메라 전환",
"videomute": "카메라 시작/중지",
"videoSettings": "비디오 설정",
"selectBackground": "배경 선택",
"startvideoblur": "내 배경을 흐리게",
"stopvideoblur": "배경 흐림 비활성화"
},

View File

@@ -204,9 +204,6 @@
"done": "Gereed",
"e2eeDescription": "Eind-tot-Eind-Versleuteling is momenteel EXPERIMENTEEL. Houd er rekening mee dat inschakelen van eind-tot-eind-versleuteling de door de server geleverde services zal uitschakelen zoals: opnemen, livestreamen en deelname via telefoon. Houd er ook rekening mee dat de vergadering alleen zal werken voor personen die deelnemen vanaf browsers met ondersteuning voor insertable streams.",
"e2eeLabel": "Sleutel",
"e2eeNoKey": "Geen",
"e2eeToggleSet": "Sleutel instellen",
"e2eeSet": "Instellen",
"e2eeWarning": "WAARSCHUWING: Niet alle deelnemers in deze vergadering lijken ondersteuning te hebben voor eind-tot-eind-versleuteling. Als u het inschakelt zullen zij u niet kunnen zien of horen.",
"enterDisplayName": "Voer hier uw naam in",
"error": "Fout",
@@ -268,7 +265,6 @@
"readMore": "meer",
"recording": "Opnemen",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Niet mogelijk tijdens een livestream",
"recordingDisabledForGuestTooltip": "Gasten kunnen geen opnamen starten.",
"recordingDisabledTooltip": "Opname starten uitgeschakeld.",
"rejoinNow": "Nu opnieuw deelnemen",
"remoteControlAllowedMessage": "{{user}} heeft uw verzoek om extern beheer geaccepteerd.",
@@ -301,7 +297,6 @@
"shareVideoTitle": "Een video delen",
"shareYourScreen": "Uw scherm delen",
"shareYourScreenDisabled": "Schermdeling is uitgeschakeld.",
"shareYourScreenDisabledForGuest": "Gasten kunnen hun scherm niet delen.",
"startLiveStreaming": "Livestream starten",
"startRecording": "Opname starten",
"startRemoteControlErrorMessage": "Er is een fout opgetreden tijdens het starten van de sessie van extern beheer.",
@@ -322,7 +317,6 @@
"videoLink": "Video link",
"WaitForHostMsg": "De vergadering <b>{{room}}</b> is nog niet gestart. Authenticeer uzelf als u de host bent. Anders wacht u tot de host aanwezig is.",
"WaitForHostMsgWOk": "De vergadering <b>{{room}}</b> is nog niet gestart. Als u de host bent, drukt u op 'OK' om uzelf te authenticeren. Anders wacht u tot de host aanwezig is.",
"WaitingForHost": "Wachten op de host...",
"Yes": "Ja",
"yourEntireScreen": "Uw gehele scherm"
},
@@ -338,6 +332,14 @@
"embedMeeting": {
"title": "Deze vergadering embedden"
},
"virtualBackground": {
"title": "Achtergronden",
"enableBlur": "Vervagen inschakelen",
"removeBackground": "Verwijder achtergrond",
"uploadImage": "Afbeelding uploaden",
"pleaseWait": "Even geduld a.u.b...",
"none": "Geen"
},
"feedback": {
"average": "Gemiddeld",
"bad": "Slecht",
@@ -412,8 +414,7 @@
"toggleFilmstrip": "Videominiaturen weergeven of verbergen",
"toggleScreensharing": "Wisselen tussen camera en schermdeling",
"toggleShortcuts": "Sneltoetsen weergeven of verbergen",
"videoMute": "Uw camera starten of stoppen",
"videoQuality": "Kwaliteit van gesprek beheren"
"videoMute": "Uw camera starten of stoppen"
},
"liveStreaming": {
"limitNotificationDescriptionWeb": "Vanwege een grote vraag zal uw stream beperkt worden tot {{limit}} min. Voor ongelimiteerd streamen, probeer <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
@@ -693,7 +694,7 @@
"chat": "Chatvenster in- of uitschakelen",
"document": "Gedeeld document in- of uitschakelen",
"download": "Download onze apps",
"e2ee": "Eind-tot-eind-versleuteling",
"embedMeeting": "Vergadering inbedden",
"feedback": "Feedback achterlaten",
"fullScreen": "Volledig scherm in- of uitschakelen",
"grantModerator": "Moderatorrechten verlenen",
@@ -727,9 +728,10 @@
"toggleCamera": "Camera wisselen",
"toggleFilmstrip": "Filmstrip in- of uitschakelen",
"videomute": "Video dempen in- of uitschakelen",
"videoblur": "Video vervagen in- of uitschakelen"
"selectBackground": "Achtergrond selecteren"
},
"addPeople": "Personen aan uw gesprek toevoegen",
"audioSettings": "Audio-instellingen",
"audioOnlyOff": "Lage bandbreedte-modus uitschakelen",
"audioOnlyOn": "Lage bandbreedte-modus inschakelen",
"audioRoute": "Het afspeelapparaat selecteren",
@@ -741,6 +743,7 @@
"documentOpen": "Gedeeld document openen",
"download": "Download onze apps",
"e2ee": "Eind-tot-eind-versleuteling",
"embedMeeting": "Vergadering inbedden",
"enterFullScreen": "Volledig scherm weergeven",
"enterTileView": "Tegelweergave openen",
"exitFullScreen": "Volledig scherm sluiten",
@@ -758,6 +761,7 @@
"moreOptions": "Meer opties",
"mute": "Dempen / Dempen opheffen",
"muteEveryone": "Iedereen dempen",
"muteEveryonesVideo": "Camera's van iedereen uitzetten",
"noAudioSignalTitle": "Er komt geen invoer van uw microfoon!",
"noAudioSignalDesc": "Als u niet met opzet hebt gedempt vanuit systeeminstellingen of hardware, overweeg dan van apparaat te wisselen.",
"noAudioSignalDescSuggestion": "Als u niet met opzet hebt gedempt vanuit systeeminstellingen of hardware, overweeg dan over te schakelen naar het gesuggereerde apparaat.",
@@ -771,7 +775,7 @@
"profile": "Uw profiel bewerken",
"raiseHand": "Uw hand opsteken / laten zakken",
"raiseYourHand": "Uw hand opsteken",
"security": "Beveiligingsoptions",
"security": "Beveiligingsopties",
"Settings": "Instellingen",
"sharedvideo": "Een YouTube-video delen",
"shareRoom": "Iemand uitnodigen",
@@ -786,7 +790,8 @@
"tileViewToggle": "Tegelweergave in- of uitschakelen",
"toggleCamera": "Camera in- of uitschakelen",
"videomute": "Camera starten / stoppen",
"startvideoblur": "Vervaag mijn achtergrond",
"videoSettings": "Instellingen van camera",
"selectBackground": "Achtergrond selecteren",
"stopvideoblur": "Achtergrond vervagen uitschakelen"
},
"transcribing": {
@@ -835,8 +840,6 @@
"ld": "LD",
"ldTooltip": "U bekijkt video in lage resolutie",
"lowDefinition": "Lage resolutie",
"onlyAudioAvailable": "Alleen audio is beschikbaar",
"onlyAudioSupported": "In deze browser wordt alleen audio ondersteund.",
"sd": "SD",
"sdTooltip": "U bekijkt video in standaard resolutie",
"standardDefinition": "Standaard resolutie"
@@ -871,6 +874,8 @@
"getHelp": "Hulp krijgen",
"go": "GAAN",
"goSmall": "GAAN",
"headerTitle": "Jitsi Meet",
"headerSubtitle": "Veilige vergaderingen van hoge kwaliteit",
"info": "Informatie",
"join": "AANMAKEN / DEELNEMEN",
"moderatedMessage": "Of <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">boek een vergadering URL</a> van tevoren waar u de enige moderator bent.",
@@ -883,6 +888,7 @@
"roomname": "Voer naam van ruimte in",
"roomnameHint": "Voer de naam of URL in van de ruimte die u wilt betreden. U kunt een naam verzinnen, maar laat deze wel weten aan de andere deelnemers, zodat zij dezelfde naam invoeren.",
"sendFeedback": "Feedback versturen",
"startMeeting": "Vergadering starten",
"terms": "Voorwaarden",
"title": "Veilige, volledig uitgeruste en geheel gratis videovergaderingen"
},

View File

@@ -113,6 +113,7 @@
"maxEnabledResolution": "send max",
"more": "Zobraziť viac",
"packetloss": "Strata paketov:",
"participant_id": "ID účastníka:",
"quality": {
"good": "Dobré",
"inactive": "Neaktívne",
@@ -316,10 +317,13 @@
"e2ee": {
"labelToolTip": "Zvuková a obrazová komunikácia je koncovo šifrovaná"
},
"embedMeeting": {
"title": "Vložiť toto stretnutie"
},
"feedback": {
"average": "Priemerný",
"bad": "Zlý",
"detailsLabel": "Povedzte nám viac.",
"detailsLabel": "Povedzte nám viac",
"good": "Dobrý",
"rateExperience": "Ohodnoťte dojem",
"veryBad": "Veľmi zlý",
@@ -762,7 +766,8 @@
"toggleCamera": "Zmeniť kameru",
"videomute": "Vypnúť / Zapnúť kameru",
"startvideoblur": "Rozmazať pozadie",
"stopvideoblur": "Ukončiť rozmazanie pozadia"
"stopvideoblur": "Ukončiť rozmazanie pozadia",
"selectBackground": "Vybrať pozadie"
},
"transcribing": {
"ccButtonTooltip": "titulky vypnuť/zapnúť",
@@ -827,7 +832,9 @@
"muted": "Vypnutý mikrofón",
"remoteControl": "Vzdialené ovládanie",
"show": "Ukázať v popredí",
"videomute": "Účastník vypol kameru"
"videomute": "Účastník vypol kameru",
"videoMuted": "Vypnutá kamera",
"domuteVideoOfOthers": "Vypnúť kamery ostatným"
},
"welcomepage": {
"accessibilityLabel": {

View File

@@ -28,6 +28,7 @@
"shareInvite": "Share meeting invitation",
"shareLink": "Share the meeting link to invite others",
"shareStream": "Share the live streaming link",
"sip": "SIP: {{address}}",
"telephone": "Telephone: {{number}}",
"title": "Invite people to this meeting",
"yahooEmail": "Yahoo Email"

4
package-lock.json generated
View File

@@ -10513,8 +10513,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#0e180efdfa46a048ae1276cfbdf9cf8e051405c7",
"from": "github:jitsi/lib-jitsi-meet#0e180efdfa46a048ae1276cfbdf9cf8e051405c7",
"version": "github:jitsi/lib-jitsi-meet#cd53f249c532fdceb296c37facc4acc846677a9e",
"from": "github:jitsi/lib-jitsi-meet#cd53f249c532fdceb296c37facc4acc846677a9e",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "1.0.3",

View File

@@ -54,7 +54,7 @@
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#0e180efdfa46a048ae1276cfbdf9cf8e051405c7",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#cd53f249c532fdceb296c37facc4acc846677a9e",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.21",
"moment": "2.29.1",

View File

@@ -96,6 +96,7 @@ class InputDialog extends BaseDialog<Props, State> {
{ t(this.props.contentKey) }
</Text>
<TextInput
autoFocus = { true }
onChangeText = { this._onChangeText }
style = { _dialogStyles.field }
underlineColorAndroid = { FIELD_UNDERLINE }

View File

@@ -86,6 +86,12 @@ export const INVITE_ENABLED = 'invite.enabled';
*/
export const IOS_RECORDING_ENABLED = 'ios.recording.enabled';
/**
* Flag indicating if screen sharing should be enabled in iOS.
* Default: disabled (false).
*/
export const IOS_SCREENSHARING_ENABLED = 'ios.screensharing.enabled';
/**
* Flag indicating if kickout is enabled.
* Default: enabled (true).

View File

@@ -1,3 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.66671 3.66665H18.3334V18.3333H3.66671V3.66665ZM1.83337 3.66665C1.83337 2.65412 2.65419 1.83331 3.66671 1.83331H18.3334C19.3459 1.83331 20.1667 2.65412 20.1667 3.66665V18.3333C20.1667 19.3458 19.3459 20.1666 18.3334 20.1666H3.66671C2.65419 20.1666 1.83337 19.3458 1.83337 18.3333V3.66665ZM6.87046 13.9461C7.31535 14.1924 7.82776 14.3156 8.40771 14.3156C8.74932 14.3156 9.08298 14.2719 9.40871 14.1845C9.73443 14.0891 10.0204 13.962 10.2667 13.8031C10.4018 13.7237 10.4971 13.6363 10.5527 13.541C10.6163 13.4377 10.648 13.3185 10.648 13.1835C10.648 13.0166 10.6043 12.8776 10.517 12.7664C10.4296 12.6552 10.3223 12.5996 10.1952 12.5996C10.1078 12.5996 10.0204 12.6194 9.93304 12.6591C9.84565 12.6909 9.74237 12.7426 9.62321 12.8141C9.43254 12.9173 9.25776 13.0008 9.09887 13.0643C8.94793 13.1199 8.77315 13.1477 8.57454 13.1477C8.08198 13.1477 7.70065 12.9888 7.43054 12.6711C7.16837 12.3453 7.03729 11.8846 7.03729 11.2887C7.03729 10.685 7.16837 10.2242 7.43054 9.9064C7.70065 9.58067 8.08198 9.41781 8.57454 9.41781C8.7811 9.41781 8.96382 9.44959 9.12271 9.51315C9.2816 9.56876 9.44843 9.6482 9.62321 9.75148C9.71854 9.80709 9.81387 9.85476 9.90921 9.89448C10.0045 9.9342 10.0959 9.95406 10.1833 9.95406C10.3183 9.95406 10.4256 9.90242 10.505 9.79915C10.5924 9.68792 10.6361 9.54492 10.6361 9.37015C10.6361 9.11592 10.509 8.9054 10.2548 8.73856C10.0165 8.58762 9.7384 8.46845 9.42062 8.38106C9.11079 8.29367 8.80096 8.24998 8.49112 8.24998C7.90323 8.24998 7.38287 8.37709 6.93004 8.63131C6.47721 8.88554 6.12368 9.24701 5.86946 9.71573C5.62318 10.1765 5.50004 10.7088 5.50004 11.3126C5.50004 11.9163 5.61921 12.4446 5.85754 12.8975C6.09587 13.3503 6.43351 13.6999 6.87046 13.9461ZM12.6891 13.9461C13.134 14.1924 13.6464 14.3156 14.2264 14.3156C14.568 14.3156 14.9017 14.2719 15.2274 14.1845C15.5531 14.0891 15.8391 13.962 16.0854 13.8031C16.2204 13.7237 16.3158 13.6363 16.3714 13.541C16.4349 13.4377 16.4667 13.3185 16.4667 13.1835C16.4667 13.0166 16.423 12.8776 16.3356 12.7664C16.2483 12.6552 16.141 12.5996 16.0139 12.5996C15.9265 12.5996 15.8391 12.6194 15.7517 12.6591C15.6643 12.6909 15.5611 12.7426 15.4419 12.8141C15.2512 12.9173 15.0764 13.0008 14.9176 13.0643C14.7666 13.1199 14.5918 13.1477 14.3932 13.1477C13.9007 13.1477 13.5193 12.9888 13.2492 12.6711C12.9871 12.3453 12.856 11.8846 12.856 11.2887C12.856 10.685 12.9871 10.2242 13.2492 9.9064C13.5193 9.58067 13.9007 9.41781 14.3932 9.41781C14.5998 9.41781 14.7825 9.44959 14.9414 9.51315C15.1003 9.56876 15.2671 9.6482 15.4419 9.75148C15.5372 9.80709 15.6326 9.85476 15.7279 9.89448C15.8232 9.9342 15.9146 9.95406 16.002 9.95406C16.137 9.95406 16.2443 9.90242 16.3237 9.79915C16.4111 9.68792 16.4548 9.54492 16.4548 9.37015C16.4548 9.11592 16.3277 8.9054 16.0735 8.73856C15.8351 8.58762 15.5571 8.46845 15.2393 8.38106C14.9295 8.29367 14.6196 8.24998 14.3098 8.24998C13.7219 8.24998 13.2016 8.37709 12.7487 8.63131C12.2959 8.88554 11.9424 9.24701 11.6881 9.71573C11.4419 10.1765 11.3187 10.7088 11.3187 11.3126C11.3187 11.9163 11.4379 12.4446 11.6762 12.8975C11.9146 13.3503 12.2522 13.6999 12.6891 13.9461Z" />
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33335 3.33335H16.6667V16.6667H3.33335V3.33335ZM1.66669 3.33335C1.66669 2.41288 2.41288 1.66669 3.33335 1.66669H16.6667C17.5872 1.66669 18.3334 2.41288 18.3334 3.33335V16.6667C18.3334 17.5872 17.5872 18.3334 16.6667 18.3334H3.33335C2.41288 18.3334 1.66669 17.5872 1.66669 16.6667V3.33335ZM6.24585 12.6784C6.6503 12.9022 7.11613 13.0142 7.64335 13.0142C7.95391 13.0142 8.25724 12.9745 8.55335 12.895C8.84946 12.8084 9.10946 12.6928 9.33335 12.5484C9.45613 12.4761 9.5428 12.3967 9.59335 12.31C9.65113 12.2161 9.68002 12.1078 9.68002 11.985C9.68002 11.8334 9.6403 11.707 9.56085 11.6059C9.48141 11.5047 9.38391 11.4542 9.26835 11.4542C9.18891 11.4542 9.10946 11.4722 9.03002 11.5084C8.95058 11.5372 8.85669 11.5842 8.74835 11.6492C8.57502 11.7431 8.41613 11.8189 8.27169 11.8767C8.13446 11.9272 7.97558 11.9525 7.79502 11.9525C7.34724 11.9525 7.00058 11.8081 6.75502 11.5192C6.51669 11.2231 6.39752 10.8042 6.39752 10.2625C6.39752 9.71363 6.51669 9.29474 6.75502 9.00585C7.00058 8.70974 7.34724 8.56169 7.79502 8.56169C7.9828 8.56169 8.14891 8.59058 8.29335 8.64835C8.4378 8.69891 8.58946 8.77113 8.74835 8.86502C8.83502 8.91558 8.92169 8.95891 9.00835 8.99502C9.09502 9.03113 9.17808 9.04919 9.25752 9.04919C9.3803 9.04919 9.4778 9.00224 9.55002 8.90835C9.62946 8.80724 9.66919 8.67724 9.66919 8.51835C9.66919 8.28724 9.55363 8.09585 9.32252 7.94419C9.10585 7.80696 8.85308 7.69863 8.56419 7.61919C8.28252 7.53974 8.00085 7.50002 7.71919 7.50002C7.18474 7.50002 6.71169 7.61558 6.30002 7.84669C5.88835 8.0778 5.56696 8.40641 5.33585 8.83252C5.11196 9.25141 5.00002 9.7353 5.00002 10.2842C5.00002 10.8331 5.10835 11.3134 5.32502 11.725C5.54169 12.1367 5.84863 12.4545 6.24585 12.6784ZM11.5356 12.6784C11.94 12.9022 12.4058 13.0142 12.9331 13.0142C13.2436 13.0142 13.547 12.9745 13.8431 12.895C14.1392 12.8084 14.3992 12.6928 14.6231 12.5484C14.7458 12.4761 14.8325 12.3967 14.8831 12.31C14.9408 12.2161 14.9697 12.1078 14.9697 11.985C14.9697 11.8334 14.93 11.707 14.8506 11.6059C14.7711 11.5047 14.6736 11.4542 14.5581 11.4542C14.4786 11.4542 14.3992 11.4722 14.3197 11.5084C14.2403 11.5372 14.1464 11.5842 14.0381 11.6492C13.8647 11.7431 13.7058 11.8189 13.5614 11.8767C13.4242 11.9272 13.2653 11.9525 13.0847 11.9525C12.637 11.9525 12.2903 11.8081 12.0447 11.5192C11.8064 11.2231 11.6872 10.8042 11.6872 10.2625C11.6872 9.71363 11.8064 9.29474 12.0447 9.00585C12.2903 8.70974 12.637 8.56169 13.0847 8.56169C13.2725 8.56169 13.4386 8.59058 13.5831 8.64835C13.7275 8.69891 13.8792 8.77113 14.0381 8.86502C14.1247 8.91558 14.2114 8.95891 14.2981 8.99502C14.3847 9.03113 14.4678 9.04919 14.5472 9.04919C14.67 9.04919 14.7675 9.00224 14.8397 8.90835C14.9192 8.80724 14.9589 8.67724 14.9589 8.51835C14.9589 8.28724 14.8433 8.09585 14.6122 7.94419C14.3956 7.80696 14.1428 7.69863 13.8539 7.61919C13.5722 7.53974 13.2906 7.50002 13.0089 7.50002C12.4745 7.50002 12.0014 7.61558 11.5897 7.84669C11.1781 8.0778 10.8567 8.40641 10.6256 8.83252C10.4017 9.25141 10.2897 9.7353 10.2897 10.2842C10.2897 10.8331 10.3981 11.3134 10.6147 11.725C10.8314 12.1367 11.1383 12.4545 11.5356 12.6784Z" />
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,7 +1,7 @@
// @flow
import React, { PureComponent } from 'react';
import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
import { KeyboardAvoidingView, Platform, SafeAreaView } from 'react-native';
import { ColorSchemeRegistry } from '../../color-scheme';
import { HeaderWithNavigation, SlidingView } from '../../react';
@@ -106,7 +106,12 @@ class JitsiModal extends PureComponent<Props> {
position = { position }
show = { _show }>
<KeyboardAvoidingView
behavior = 'height'
behavior =
{
Platform.OS === 'ios'
? 'padding' : 'height'
}
enabled = { true }
style = { [
_headerStyles.page,
_styles.page,

View File

@@ -1,6 +1,7 @@
// @flow
import UIEvents from '../../../../service/UI/UIEvents';
import { toggleE2EE } from '../../e2ee/actions';
import { NOTIFICATION_TIMEOUT, showNotification } from '../../notifications';
import { CALLING, INVITED } from '../../presence-status';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
@@ -207,7 +208,7 @@ StateListenerRegistry.register(
(conference, store) => {
if (conference) {
const propertyHandlers = {
'e2eeEnabled': (participant, value) => _e2eeUpdated(store, conference, participant.getId(), value),
'e2ee.enabled': (participant, value) => _e2eeUpdated(store, conference, participant.getId(), value),
'features_e2ee': (participant, value) =>
store.dispatch(participantUpdated({
conference,
@@ -270,12 +271,16 @@ StateListenerRegistry.register(
* @param {Function} dispatch - The Redux dispatch function.
* @param {Object} conference - The conference for which we got an update.
* @param {string} participantId - The ID of the participant from which we got an update.
* @param {boolean} newValue - The new value of the E2EE enabled status.
* @param {boolean} newValue - The new value of the E2EE enabled status.
* @returns {void}
*/
function _e2eeUpdated({ dispatch }, conference, participantId, newValue) {
const e2eeEnabled = newValue === 'true';
if (e2eeEnabled) {
dispatch(toggleE2EE(e2eeEnabled));
}
dispatch(participantUpdated({
conference,
id: participantId,
@@ -383,7 +388,7 @@ function _maybePlaySounds({ getState, dispatch }, action) {
*/
function _participantJoinedOrUpdated(store, next, action) {
const { dispatch, getState } = store;
const { participant: { avatarURL, e2eeEnabled, email, id, local, name, raisedHand } } = action;
const { participant: { avatarURL, email, id, local, name, raisedHand } } = action;
// Send an external update of the local participant's raised hand state
// if a new raised hand state is defined in the action.
@@ -398,16 +403,6 @@ function _participantJoinedOrUpdated(store, next, action) {
}
}
// Send an external update of the local participant's E2EE enabled state
// if a new state is defined in the action.
if (typeof e2eeEnabled !== 'undefined') {
if (local) {
const { conference } = getState()['features/base/conference'];
conference && conference.setLocalParticipantProperty('e2eeEnabled', e2eeEnabled);
}
}
// Allow the redux update to go through and compare the old avatar
// to the new avatar and emit out change events if necessary.
const result = next(action);

View File

@@ -3,6 +3,8 @@
import React, { PureComponent } from 'react';
import { AudioSettingsButton, VideoSettingsButton } from '../../../../toolbox/components/web';
import { VideoBackgroundButton } from '../../../../virtual-background';
import { checkBlurSupport } from '../../../../virtual-background/functions';
import { Avatar } from '../../../avatar';
import { allowUrlSharing } from '../../functions';
@@ -114,6 +116,7 @@ export default class PreMeetingScreen extends PureComponent<Props> {
<div className = 'toolbox-content-items'>
<AudioSettingsButton visible = { true } />
<VideoSettingsButton visible = { true } />
<VideoBackgroundButton visible = { checkBlurSupport() } />
</div>
</div>
</div>

View File

@@ -0,0 +1,15 @@
// @flow
/**
* The identifier of the sound to be played when e2ee is disabled.
*
* @type {string}
*/
export const E2EE_OFF_SOUND_ID = 'E2EE_OFF_SOUND';
/**
* The identifier of the sound to be played when e2ee is enabled.
*
* @type {string}
*/
export const E2EE_ON_SOUND_ID = 'E2EE_ON_SOUND';

View File

@@ -1,12 +1,16 @@
// @flow
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
import { getCurrentConference } from '../base/conference';
import { getLocalParticipant, participantUpdated } from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { TOGGLE_E2EE } from './actionTypes';
import { toggleE2EE } from './actions';
import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID } from './constants';
import logger from './logger';
import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
/**
* Middleware that captures actions related to E2EE.
@@ -16,10 +20,25 @@ import logger from './logger';
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
switch (action.type) {
case APP_WILL_MOUNT:
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:
dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
dispatch(unregisterSound(E2EE_ON_SOUND_ID));
break;
case TOGGLE_E2EE: {
const conference = getCurrentConference(getState);
if (conference) {
if (conference && conference.isE2EEEnabled() !== action.enabled) {
logger.debug(`E2EE will be ${action.enabled ? 'enabled' : 'disabled'}`);
conference.toggleE2EE(action.enabled);
@@ -31,6 +50,10 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
id: participant.id,
local: true
}));
const soundID = action.enabled ? E2EE_ON_SOUND_ID : E2EE_OFF_SOUND_ID;
dispatch(playSound(soundID));
}
break;

View File

@@ -0,0 +1,15 @@
// @flow
/**
* The name of the bundled audio file which will be played when e2ee is disabled.
*
* @type {string}
*/
export const E2EE_OFF_SOUND_FILE = 'e2eeOff.mp3';
/**
* The name of the bundled audio file which will be played when e2ee is enabled.
*
* @type {string}
*/
export const E2EE_ON_SOUND_FILE = 'e2eeOn.mp3';

View File

@@ -3,7 +3,7 @@
import type { Dispatch } from 'redux';
import { getInviteURL } from '../base/connection';
import { getParticipants } from '../base/participants';
import { getLocalParticipant, getParticipants } from '../base/participants';
import { inviteVideoRooms } from '../videosipgw';
import {
@@ -18,7 +18,8 @@ import {
import {
getDialInConferenceID,
getDialInNumbers,
invitePeopleAndChatRooms
invitePeopleAndChatRooms,
inviteSipEndpoints
} from './functions';
import logger from './logger';
@@ -102,7 +103,9 @@ export function invite(
inviteServiceCallFlowsUrl
} = state['features/base/config'];
const inviteUrl = getInviteURL(state);
const { sipInviteUrl } = state['features/base/config'];
const { jwt } = state['features/base/jwt'];
const { name: displayName } = getLocalParticipant(state);
// First create all promises for dialing out.
const phoneNumbers
@@ -164,6 +167,20 @@ export function invite(
invitesLeftToSend
= invitesLeftToSend.filter(({ type }) => type !== 'videosipgw');
const sipEndpoints
= invitesLeftToSend.filter(({ type }) => type === 'sip');
conference && inviteSipEndpoints(
sipEndpoints,
sipInviteUrl,
jwt,
conference.options.name,
displayName
);
invitesLeftToSend
= invitesLeftToSend.filter(({ type }) => type !== 'sip');
return (
Promise.all(allInvitePromises)
.then(() => invitesLeftToSend));

View File

@@ -12,7 +12,8 @@ import {
getInviteResultsForQuery,
getInviteTypeCounts,
isAddPeopleEnabled,
isDialOutEnabled
isDialOutEnabled,
isSipInviteEnabled
} from '../../functions';
import logger from '../../logger';
@@ -38,6 +39,11 @@ export type Props = {
*/
_dialOutEnabled: boolean,
/**
* Whether or not to allow sip invites.
*/
_sipInviteEnabled: boolean,
/**
* The JWT token.
*/
@@ -96,7 +102,7 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
/**
* Invite people and numbers to the conference. The logic works by inviting
* numbers, people/rooms, and videosipgw in parallel. All invitees are
* numbers, people/rooms, sip endpoints and videosipgw in parallel. All invitees are
* stored in an array. As each invite succeeds, the invitee is removed
* from the array. After all invites finish, close the modal if there are
* no invites left to send. If any are left, that means an invite failed
@@ -214,7 +220,8 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
_dialOutEnabled: dialOutEnabled,
_jwt: jwt,
_peopleSearchQueryTypes: peopleSearchQueryTypes,
_peopleSearchUrl: peopleSearchUrl
_peopleSearchUrl: peopleSearchUrl,
_sipInviteEnabled: sipInviteEnabled
} = this.props;
const options = {
addPeopleEnabled,
@@ -222,7 +229,8 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
dialOutEnabled,
jwt,
peopleSearchQueryTypes,
peopleSearchUrl
peopleSearchUrl,
sipInviteEnabled
};
return getInviteResultsForQuery(query, options);
@@ -259,6 +267,7 @@ export function _mapStateToProps(state: Object) {
_dialOutEnabled: isDialOutEnabled(state),
_jwt: state['features/base/jwt'].jwt,
_peopleSearchQueryTypes: peopleSearchQueryTypes,
_peopleSearchUrl: peopleSearchUrl
_peopleSearchUrl: peopleSearchUrl,
_sipInviteEnabled: isSipInviteEnabled(state)
};
}

View File

@@ -285,7 +285,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
*/
_parseQueryResults(response = []) {
const { t, _dialOutEnabled } = this.props;
const users = response.filter(item => item.type !== 'phone');
const users = response.filter(item => item.type !== 'phone' && item.type !== 'sip');
const userDisplayItems = [];
for (const user of users) {
@@ -348,9 +348,25 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
};
});
const sipAddresses = response.filter(item => item.type === 'sip');
const sipDisplayItems = sipAddresses.map(sip => {
return {
filterValues: [
sip.address
],
content: t('addPeople.sip', { address: sip.address }),
description: '',
item: sip,
value: sip.address
};
});
return [
...userDisplayItems,
...numberDisplayItems
...numberDisplayItems,
...sipDisplayItems
];
}

View File

@@ -42,3 +42,9 @@ export const OUTGOING_CALL_RINGING_SOUND_ID = 'OUTGOING_CALL_RINGING_SOUND_ID';
* @type {string}
*/
export const OUTGOING_CALL_START_SOUND_ID = 'OUTGOING_CALL_START_SOUND_ID';
/**
* Regex for matching sip addresses.
*/
// eslint-disable-next-line max-len
export const SIP_ADDRESS_REGEX = /^[a-zA-Z]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/;

View File

@@ -10,6 +10,7 @@ import { toState } from '../base/redux';
import { doGetJSON, parseURIString } from '../base/util';
import { isVpaasMeeting } from '../billing-counter/functions';
import { SIP_ADDRESS_REGEX } from './constants';
import logger from './logger';
declare var $: Function;
@@ -122,6 +123,11 @@ export type GetInviteResultsOptions = {
*/
peopleSearchUrl: string,
/**
* Whether or not to check sip invites.
*/
sipInviteEnabled: boolean,
/**
* The jwt token to pass to the search service.
*/
@@ -149,6 +155,7 @@ export function getInviteResultsForQuery(
dialOutEnabled,
peopleSearchQueryTypes,
peopleSearchUrl,
sipInviteEnabled,
jwt
} = options;
@@ -233,6 +240,13 @@ export function getInviteResultsForQuery(
});
}
if (sipInviteEnabled && isASipAddress(text)) {
results.push({
type: 'sip',
address: text
});
}
return results;
});
}
@@ -374,6 +388,21 @@ export function isDialOutEnabled(state: Object): boolean {
&& conference && conference.isSIPCallingSupported();
}
/**
* Determines if inviting sip endpoints is enabled or not.
*
* @param {Object} state - Current state.
* @returns {boolean} Indication of whether dial out is currently enabled.
*/
export function isSipInviteEnabled(state: Object): boolean {
const { sipInviteUrl } = state['features/base/config'];
const { features = {} } = getLocalParticipant(state);
return state['features/base/jwt'].jwt
&& Boolean(sipInviteUrl)
&& String(features['sip-outbound-call']) === 'true';
}
/**
* Checks whether a string looks like it could be for a phone number.
*
@@ -392,6 +421,16 @@ function isMaybeAPhoneNumber(text: string): boolean {
return Boolean(digits.length);
}
/**
* Checks whether a string matches a sip address format.
*
* @param {string} text - The text to check.
* @returns {boolean} True if provided text matches a sip address format.
*/
function isASipAddress(text: string): boolean {
return SIP_ADDRESS_REGEX.test(text);
}
/**
* RegExp to use to determine if some text might be a phone number.
*
@@ -764,3 +803,47 @@ export function isSharingEnabled(sharingFeature: string) {
|| typeof interfaceConfig.SHARING_FEATURES === 'undefined'
|| (interfaceConfig.SHARING_FEATURES.length && interfaceConfig.SHARING_FEATURES.indexOf(sharingFeature) > -1);
}
/**
* Sends a post request to an invite service.
*
* @param {Array} inviteItems - The list of the "sip" type items to invite.
* @param {string} sipInviteUrl - The invite service that generates the invitation.
* @param {string} jwt - The jwt token.
* @param {string} roomName - The name to the conference.
* @param {string} displayName - The user display name.
* @returns {Promise} - The promise created by the request.
*/
export function inviteSipEndpoints( // eslint-disable-line max-params
inviteItems: Array<Object>,
sipInviteUrl: string,
jwt: string,
roomName: string,
displayName: string
): Promise<void> {
if (inviteItems.length === 0) {
return Promise.resolve();
}
return fetch(
`${sipInviteUrl}?token=${jwt}`,
{
body: JSON.stringify({
callParams: {
callUrlInfo: {
baseUrl: window.location.origin,
callName: roomName
}
},
sipClientParams: {
displayName,
sipAddress: inviteItems.map(item => item.address)
}
}),
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}
);
}

View File

@@ -129,49 +129,46 @@ function _electLastVisibleRemoteVideo(tracks) {
* @returns {(string|undefined)}
*/
function _electParticipantInLargeVideo(state) {
// 1. If a participant is pinned, they will be shown in the LargeVideo (
// regardless of whether they are local or remote).
// 1. If a participant is pinned, they will be shown in the LargeVideo
// (regardless of whether they are local or remote).
const participants = state['features/base/participants'];
let participant = participants.find(p => p.pinned);
let id = participant && participant.id;
if (!id) {
// 2. No participant is pinned so get the dominant speaker. But the
// local participant won't be displayed in LargeVideo even if she is
// the dominant speaker.
participant = participants.find(p => p.dominantSpeaker && !p.local);
id = participant && participant.id;
if (!id) {
// 3. There is no dominant speaker so select the remote participant
// who last had visible video.
const tracks = state['features/base/tracks'];
const videoTrack = _electLastVisibleRemoteVideo(tracks);
id = videoTrack && videoTrack.participantId;
if (!id) {
// 4. It's possible there is no participant with visible video.
// This can happen for a number of reasons:
// - there is only one participant (i.e. the local user),
// - other participants joined with video muted.
// As a last resort, pick the last participant who joined the
// conference (regardless of whether they are local or
// remote).
//
// HOWEVER: We don't want to show poltergeist or other bot type participants on stage
// automatically, because it's misleading (users may think they are already
// joined and maybe speaking).
for (let i = participants.length; i > 0 && !participant; i--) {
const p = participants[i - 1];
!p.botType && (participant = p);
}
id = participant && participant.id;
}
}
if (participant) {
return participant.id;
}
return id;
// 2. Next, pick the most recent remote screenshare that was added to the conference.
const remoteScreenShares = state['features/video-layout'].remoteScreenShares;
if (remoteScreenShares?.length) {
return remoteScreenShares[remoteScreenShares.length - 1];
}
// 3. Next, pick the dominant speaker (other than self).
participant = participants.find(p => p.dominantSpeaker && !p.local);
if (participant) {
return participant.id;
}
// 4. Next, pick the most recent participant with video.
const tracks = state['features/base/tracks'];
const videoTrack = _electLastVisibleRemoteVideo(tracks);
if (videoTrack) {
return videoTrack.participantId;
}
// 5. As a last resort, select the participant that joined last (other than poltergist or other bot type
// participants).
for (let i = participants.length; i > 0 && !participant; i--) {
const p = participants[i - 1];
!p.botType && (participant = p);
}
if (participant) {
return participant.id;
}
return participants.find(p => p.local)?.id;
}

View File

@@ -42,9 +42,11 @@ class KnockingParticipantList extends AbstractKnockingParticipantList<Props> {
<span className = 'title'>
{ t('lobby.knockingParticipantList') }
</span>
<ul>
<ul className = 'knocking-participants-container'>
{ _participants.map(p => (
<li key = { p.id }>
<li
className = 'knocking-participant'
key = { p.id }>
<Avatar
displayName = { p.name }
size = { 48 }

View File

@@ -689,7 +689,7 @@ export function resume() {
area.mouseup(event => dispatch(mouseClicked(EVENTS.mouseup, event)));
area.dblclick(event => dispatch(mouseClicked(EVENTS.mousedblclick, event)));
area.contextmenu(() => false);
area[0].onmousewheel = event => {
area[0].onwheel = event => {
event.preventDefault();
event.stopPropagation();
dispatch(mouseScrolled(event));
@@ -739,7 +739,7 @@ export function pause() {
area.off('mousedown');
area.off('mousemove');
area.off('mouseup');
area[0].onmousewheel = undefined;
area[0].onwheel = undefined;
}
$(window).off('keydown');

View File

@@ -10,6 +10,7 @@ import React from 'react';
import { AbstractDialogTab } from '../../../base/dialog';
import type { Props as AbstractDialogTabProps } from '../../../base/dialog';
import { translate } from '../../../base/i18n';
import TouchmoveHack from '../../../chat/components/web/TouchmoveHack';
/**
* The type of the React {@code Component} props of {@link MoreTab}.
@@ -185,21 +186,23 @@ class MoreTab extends AbstractDialogTab<Props, State> {
{ t('settings.language') }
</div>
<div className = 'dropdown-menu'>
<DropdownMenu
isOpen = { this.state.isLanguageSelectOpen }
onOpenChange = { this._onLanguageDropdownOpenChange }
shouldFitContainer = { true }
trigger = { currentLanguage
? t(`languages:${currentLanguage}`)
: '' }
triggerButtonProps = {{
shouldFitContainer: true
}}
triggerType = 'button'>
<DropdownItemGroup>
{ languageItems }
</DropdownItemGroup>
</DropdownMenu>
<TouchmoveHack isModal = { true }>
<DropdownMenu
isOpen = { this.state.isLanguageSelectOpen }
onOpenChange = { this._onLanguageDropdownOpenChange }
shouldFitContainer = { true }
trigger = { currentLanguage
? t(`languages:${currentLanguage}`)
: '' }
triggerButtonProps = {{
shouldFitContainer: true
}}
triggerType = 'button'>
<DropdownItemGroup>
{ languageItems }
</DropdownItemGroup>
</DropdownMenu>
</TouchmoveHack>
</div>
</div>
);

View File

@@ -21,4 +21,16 @@ How to test on SIMD:
More details:
- [WebAssembly](https://webassembly.org/)
- [WebAssembly SIMD](https://github.com/WebAssembly/simd)
- [TFLite](https://blog.tensorflow.org/2020/07/accelerating-tensorflow-lite-xnnpack-integration.html)
- [TFLite](https://blog.tensorflow.org/2020/07/accelerating-tensorflow-lite-xnnpack-integration.html)
## LICENSE
The mdoels vendored here were downloaded early January (they were available as early as the 4th), before Google switched the license away from Apache 2. Thus we understand they are not covered by the new license which according to the [model card](https://drive.google.com/file/d/1lnP1bRi9CSqQQXUHa13159vLELYDgDu0/view) dates from the 21st of January.
We are not lawyers so do get legal advise if in doubt.
References:
- Model license discussion: https://github.com/tensorflow/tfjs/issues/4177
- Current vendored model is discovered: https://github.com/tensorflow/tfjs/issues/4177#issuecomment-753934631
- License change is noticed: https://github.com/tensorflow/tfjs/issues/4177#issuecomment-771536641

View File

@@ -7,9 +7,17 @@ import { connect } from '../../base/redux';
import {
_abstractMapStateToProps,
AbstractCaptions,
type AbstractCaptionsProps as Props
type AbstractCaptionsProps
} from './AbstractCaptions';
type Props = {
/**
* Whether the subtitles container is lifted above the invite box.
*/
_isLifted: boolean
} & AbstractCaptionsProps;
/**
* React {@code Component} which can display speech-to-text results from
* Jigasi as subtitles.
@@ -45,12 +53,30 @@ class Captions
*/
_renderSubtitlesContainer(
paragraphs: Array<React$Element<*>>): React$Element<*> {
const className = this.props._isLifted ? 'transcription-subtitles lifted' : 'transcription-subtitles';
return (
<div className = 'transcription-subtitles' >
<div className = { className } >
{ paragraphs }
</div>
);
}
}
export default connect(_abstractMapStateToProps)(Captions);
/**
* Maps (parts of) the redux state to the associated {@code }'s
* props.
*
* @param {Object} state - The redux state.
* @private
* @returns {Object}
*/
function mapStateToProps(state) {
return {
..._abstractMapStateToProps(state),
_isLifted: state['features/base/participants'].length < 2
};
}
export default connect(mapStateToProps)(Captions);

View File

@@ -4,6 +4,7 @@ import React from 'react';
import { findNodeHandle, NativeModules, Platform } from 'react-native';
import { ScreenCapturePickerView } from 'react-native-webrtc';
import { getFeatureFlag, IOS_SCREENSHARING_ENABLED } from '../../../base/flags';
import { translate } from '../../../base/i18n';
import { IconShareDesktop } from '../../../base/icons';
import { connect } from '../../../base/redux';
@@ -121,11 +122,13 @@ class ScreenSharingIosButton extends AbstractButton<Props, *> {
* }}
*/
function _mapStateToProps(state): Object {
const enabled = getFeatureFlag(state, IOS_SCREENSHARING_ENABLED, false);
return {
_screensharing: isLocalVideoTrackDesktop(state),
// TODO: this should work on iOS 12 too, but our trick to show the picker doesn't work.
visible: Platform.OS === 'ios' && Platform.Version.split('.')[0] >= 14
visible: enabled && Platform.OS === 'ios' && Platform.Version.split('.')[0] >= 14
};
}

View File

@@ -79,7 +79,7 @@ const styles = {
flexDirection: 'column',
flexGrow: 0,
width: '100%',
maxWidth: 500,
maxWidth: 580,
marginLeft: 'auto',
marginRight: 'auto'
}

View File

@@ -139,7 +139,7 @@ function VirtualBackground({ dispatch, t }: Props) {
titleKey = { 'virtualBackground.title' }
width = 'small'>
{loading ? (
<div>
<div className = 'virtual-background-loading'>
<span className = 'loading-content-text'>{t('virtualBackground.pleaseWait')}</span>
<Spinner
isCompleting = { false }

View File

@@ -25,12 +25,13 @@ export function checkBlurSupport() {
* @param {Blob} blob - The link to add info with.
* @returns {Promise<string>}
*/
export const blobToData = (blob: Blob): Promise<string> => new Promise(resolve => {
const reader = new FileReader();
export const blobToData = (blob: Blob): Promise<string> =>
new Promise(resolve => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.toString());
reader.readAsDataURL(blob);
});
reader.onloadend = () => resolve(reader.result.toString());
reader.readAsDataURL(blob);
});
/**
* Convert blob to base64.
@@ -52,28 +53,35 @@ export const toDataURL = async (url: string) => {
* @param {Object} base64image - Base64 image extraction.
* @param {number} width - Value for resizing the image width.
* @param {number} height - Value for resizing the image height.
* @returns {Object} Returns the canvas output.
* @returns {Promise<string>}
*
*/
export async function resizeImage(base64image: any, width: number = 1920, height: number = 1080) {
const img = document.createElement('img');
export function resizeImage(base64image: any, width: number = 1920, height: number = 1080): Promise<string> {
img.src = base64image;
/* eslint-disable no-empty-function */
img.onload = await function() {};
// In order to work on Firefox browser we need to handle the asynchronous nature of image loading; We need to use
// a promise mechanism. The reason why it 'works' without this mechanism in Chrome is actually 'by accident' because
// the image happens to be in the cache and the browser is able to deliver the uncompressed/decoded image
// before using the image in the drawImage call.
return new Promise(resolve => {
const img = document.createElement('img');
// Create an off-screen canvas.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
img.onload = function() {
// Create an off-screen canvas.
const canvas = document.createElement('canvas');
// Set its dimension to target size.
canvas.width = width;
canvas.height = height;
// Set its dimension to target size.
const context = canvas.getContext('2d');
// Draw source image into the off-screen canvas.
// TODO: keep aspect ratio and implement object-fit: cover.
ctx.drawImage(img, 0, 0, width, height);
canvas.width = width;
canvas.height = height;
// Encode image to data-uri with base64 version of compressed image.
return canvas.toDataURL('image/jpeg', 0.5);
// Draw source image into the off-screen canvas.
// TODO: keep aspect ratio and implement object-fit: cover.
context.drawImage(img, 0, 0, width, height);
// Encode image to data-uri with base64 version of compressed image.
resolve(canvas.toDataURL('image/jpeg', 0.5));
};
img.src = base64image;
});
}

View File

@@ -1,9 +1,16 @@
// @flow
import { ReducerRegistry } from '../base/redux';
import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
import { BACKGROUND_ENABLED, SET_VIRTUAL_BACKGROUND } from './actionTypes';
const STORE_NAME = 'features/virtual-background';
/**
* Sets up the persistence of the feature {@code virtual-background}.
*/
PersistenceRegistry.register(STORE_NAME, true);
/**
* Reduces redux actions which activate/deactivate virtual background image, or
* indicate if the virtual image background is activated/deactivated. The
@@ -15,7 +22,7 @@ import { BACKGROUND_ENABLED, SET_VIRTUAL_BACKGROUND } from './actionTypes';
* @returns {State} The next redux state that is the result of reducing the
* specified action.
*/
ReducerRegistry.register('features/virtual-background', (state = {}, action) => {
ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
const { virtualSource, isVirtualBackground, backgroundEffectEnabled } = action;
switch (action.type) {

View File

@@ -3,6 +3,7 @@ import 'jquery';
import { setConfigFromURLParams } from '../../react/features/base/config/functions';
import { parseURLParams } from '../../react/features/base/util/parseURLParams';
import { parseURIString } from '../../react/features/base/util/uri';
import { validateLastNLimits, limitLastN } from '../../react/features/base/lastn/functions';
setConfigFromURLParams(config, {}, {}, window.location);
@@ -13,6 +14,7 @@ const {
remoteVideo = isHuman,
remoteAudio = isHuman,
autoPlayVideo = config.testing.noAutoPlayVideo !== true,
stageView = config.disableTileView
} = params;
let {
@@ -23,6 +25,8 @@ const { room: roomName } = parseURIString(window.location.toString());
let connection = null;
let connected = false;
let room = null;
let numParticipants = 1;
@@ -32,6 +36,8 @@ const remoteTracks = {};
let maxFrameHeight = 0;
let selectedParticipant = null;
window.APP = {
conference: {
getStats() {
@@ -82,7 +88,8 @@ window.APP = {
localVideo,
remoteVideo,
remoteAudio,
autoPlayVideo
autoPlayVideo,
stageView
};
}
};
@@ -91,14 +98,23 @@ window.APP = {
* Simple emulation of jitsi-meet's screen layout behavior
*/
function updateMaxFrameHeight() {
if (!connected) {
return;
}
let newMaxFrameHeight;
if (numParticipants <= 2) {
newMaxFrameHeight = 720;
} else if (numParticipants <= 4) {
newMaxFrameHeight = 360;
} else {
newMaxFrameHeight = 180;
if (stageView) {
newMaxFrameHeight = 2160;
}
else {
if (numParticipants <= 2) {
newMaxFrameHeight = 720;
} else if (numParticipants <= 4) {
newMaxFrameHeight = 360;
} else {
newMaxFrameHeight = 180;
}
}
if (room && maxFrameHeight !== newMaxFrameHeight) {
@@ -108,10 +124,108 @@ function updateMaxFrameHeight() {
}
/**
*
* Simple emulation of jitsi-meet's lastN behavior
*/
function updateLastN() {
if (!connected) {
return;
}
let lastN = typeof config.channelLastN === 'undefined' ? -1 : config.channelLastN;
const limitedLastN = limitLastN(numParticipants, validateLastNLimits(config.lastNLimits));
if (limitedLastN !== undefined) {
lastN = lastN === -1 ? limitedLastN : Math.min(limitedLastN, lastN);
}
if (lastN === room.getLastN()) {
return;
}
room.setLastN(lastN);
}
/**
* Helper function to query whether a participant ID is a valid ID
* for stage view.
*/
function isValidStageViewParticipant(id) {
return (id !== room.myUserId() && room.getParticipantById(id));
}
/**
* Simple emulation of jitsi-meet's stage view participant selection behavior.
* Doesn't take into account pinning or screen sharing, and the initial behavior
* is slightly different.
* @returns Whether the selected participant changed.
*/
function selectStageViewParticipant(selected, previous) {
let newSelectedParticipant;
if (isValidStageViewParticipant(selected)) {
newSelectedParticipant = selected;
}
else {
newSelectedParticipant = previous.find(isValidStageViewParticipant);
}
if (newSelectedParticipant && newSelectedParticipant !== selectedParticipant) {
selectedParticipant = newSelectedParticipant;
return true;
}
return false;
}
/**
* Simple emulation of jitsi-meet's selectParticipants behavior
*/
function selectParticipants() {
if (!connected) {
return;
}
if (stageView) {
if (selectedParticipant) {
room.selectParticipants([selectedParticipant]);
}
}
else {
/* jitsi-meet's current Tile View behavior. */
const ids = room.getParticipants().map(participant => participant.getId());
room.selectParticipants(ids);
}
}
/**
* Called when number of participants changes.
*/
function setNumberOfParticipants() {
$('#participants').text(numParticipants);
if (!stageView) {
selectParticipants();
updateMaxFrameHeight();
}
updateLastN();
}
/**
* Called when ICE connects
*/
function onConnectionEstablished() {
connected = true;
selectParticipants();
updateMaxFrameHeight();
updateLastN();
}
/**
* Handles dominant speaker changed.
* @param id
*/
function onDominantSpeakerChanged(selected, previous) {
if (selectStageViewParticipant(selected, previous)) {
selectParticipants();
}
updateMaxFrameHeight();
}
@@ -194,6 +308,16 @@ function onStartMuted() {
}, 2000);
}
/**
*
* @param id
*/
function onUserJoined(id) {
numParticipants++;
setNumberOfParticipants();
remoteTracks[id] = [];
}
/**
*
* @param id
@@ -224,12 +348,12 @@ function onConnectionSuccess() {
room.on(JitsiMeetJS.events.conference.STARTED_MUTED, onStartMuted);
room.on(JitsiMeetJS.events.conference.TRACK_ADDED, onRemoteTrack);
room.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);
room.on(JitsiMeetJS.events.conference.USER_JOINED, id => {
numParticipants++;
setNumberOfParticipants();
remoteTracks[id] = [];
});
room.on(JitsiMeetJS.events.conference.CONNECTION_ESTABLISHED, onConnectionEstablished);
room.on(JitsiMeetJS.events.conference.USER_JOINED, onUserJoined);
room.on(JitsiMeetJS.events.conference.USER_LEFT, onUserLeft);
if (stageView) {
room.on(JitsiMeetJS.events.conference.DOMINANT_SPEAKER_CHANGED, onDominantSpeakerChanged);
}
const devices = [];

View File

@@ -1,6 +1,7 @@
local filters = require 'util.filters';
local jid = require "util.jid";
local jid_bare = require "util.jid".bare;
local jid_host = require "util.jid".host;
local um_is_admin = require "core.usermanager".is_admin;
local util = module:require "util";
local is_healthcheck_room = util.is_healthcheck_room;
@@ -116,12 +117,24 @@ function filter_stanza(stanza)
return stanza;
end
-- we want to filter presences only on this host for allowners and skip anything like lobby etc.
local host_from = jid_host(stanza.attr.from);
if host_from ~= module.host then
return stanza;
end
local bare_to = jid_bare(stanza.attr.to);
if stanza:get_error() and joining_moderator_participants[bare_to] then
-- pre-join succeeded but joined did not so we need to clear cache
joining_moderator_participants[bare_to] = nil;
return stanza;
end
local muc_x = stanza:get_child('x', MUC_NS..'#user');
if not muc_x then
return stanza;
end
local bare_to = jid_bare(stanza.attr.to);
if joining_moderator_participants[bare_to] and presence_check_status(muc_x, '110') then
-- skip the local presence for participant
return nil;

BIN
sounds/e2eeOff.mp3 Normal file

Binary file not shown.

BIN
sounds/e2eeOn.mp3 Normal file

Binary file not shown.