Compare commits

...

29 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
33 changed files with 445 additions and 91 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

@@ -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

@@ -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

@@ -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"
},
@@ -420,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>.",
@@ -701,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",
@@ -735,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",
@@ -749,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",
@@ -766,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.",
@@ -794,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": {
@@ -843,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"
@@ -879,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.",
@@ -891,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#43c589f4096e0e123f92eca46d4c317f731ebc18",
"from": "github:jitsi/lib-jitsi-meet#43c589f4096e0e123f92eca46d4c317f731ebc18",
"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#43c589f4096e0e123f92eca46d4c317f731ebc18",
"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

@@ -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,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

@@ -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

@@ -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

@@ -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 = [];

BIN
sounds/e2eeOff.mp3 Normal file

Binary file not shown.

BIN
sounds/e2eeOn.mp3 Normal file

Binary file not shown.