From 1ad9046a386fc60c39319fe2b8ea50395fb4bb7f Mon Sep 17 00:00:00 2001 From: Avram Tudor Date: Fri, 20 Aug 2021 11:53:11 +0300 Subject: [PATCH] Improve premeeting screens ux (#9726) * feat(prejoin) move invite to toolbar section * feat(premeeting) redesign prejoin and lobby screens * code review changes * fix prejoin flicker and avatar id * fix password error message and native lobby dialog close position --- css/_mixins.scss | 10 - css/_prejoin.scss | 153 ----------- css/_toolbars.scss | 2 +- css/_variables.scss | 6 + css/main.scss | 6 +- css/{ => premeeting}/_connection-status.scss | 35 +-- css/premeeting/_device-status.scss | 35 +++ css/{ => premeeting}/_lobby.scss | 29 +- css/premeeting/_main.scss | 7 + css/{ => premeeting}/_prejoin-dialog.scss | 0 css/premeeting/_prejoin-third-party.scss | 39 +++ css/premeeting/_prejoin.scss | 73 +++++ css/{ => premeeting}/_premeeting-screens.scss | 254 +++++++++--------- lang/main-de.json | 1 - lang/main-eo.json | 2 +- lang/main-eu.json | 1 - lang/main-fr.json | 1 - lang/main-id.json | 2 +- lang/main-pt.json | 1 - lang/main-ptBR.json | 1 - lang/main-sq.json | 1 - lang/main-zhTW.json | 1 - lang/main.json | 8 +- react/features/app/components/App.web.js | 8 +- .../base/conference/middleware.web.js | 5 +- react/features/base/icons/svg/check-solid.svg | 3 + react/features/base/icons/svg/index.js | 1 + .../components/web/ConnectionStatus.js | 54 ++-- .../components/web/CopyMeetingUrl.js | 67 ----- .../components/web/PreMeetingScreen.js | 116 +++----- .../base/premeeting/components/web/Preview.js | 49 +++- react/features/base/premeeting/functions.js | 11 - .../components/native/Conference.js | 14 +- .../conference/components/web/Conference.js | 23 +- .../add-people-dialog/web/InviteButton.js | 44 +++ .../components/add-people-dialog/web/index.js | 1 + react/features/lobby/actionTypes.js | 5 + react/features/lobby/actions.any.js | 26 ++ react/features/lobby/actions.web.js | 20 -- .../lobby/components/AbstractLobbyScreen.js | 8 + .../lobby/components/native/LobbyScreen.js | 19 +- .../lobby/components/web/LobbyScreen.js | 29 +- react/features/lobby/functions.js | 10 + react/features/lobby/reducer.js | 7 + react/features/prejoin/actions.js | 5 +- react/features/prejoin/components/Prejoin.js | 225 +++++----------- .../features/prejoin/components/PrejoinApp.js | 43 ++- .../prejoin/components/PrejoinThirdParty.js | 87 ++++++ .../components/preview/DeviceStatus.js | 13 +- react/features/prejoin/constants.js | 19 ++ react/features/prejoin/functions.js | 14 +- react/features/prejoin/middleware.js | 22 +- react/features/settings/actions.js | 3 +- .../toolbox/components/web/Toolbox.js | 43 ++- .../components/VideoBackgroundButton.js | 4 +- static/prejoin.html | 8 +- 56 files changed, 866 insertions(+), 808 deletions(-) delete mode 100644 css/_prejoin.scss rename css/{ => premeeting}/_connection-status.scss (60%) create mode 100644 css/premeeting/_device-status.scss rename css/{ => premeeting}/_lobby.scss (85%) create mode 100644 css/premeeting/_main.scss rename css/{ => premeeting}/_prejoin-dialog.scss (100%) create mode 100644 css/premeeting/_prejoin-third-party.scss create mode 100644 css/premeeting/_prejoin.scss rename css/{ => premeeting}/_premeeting-screens.scss (51%) create mode 100644 react/features/base/icons/svg/check-solid.svg delete mode 100644 react/features/base/premeeting/components/web/CopyMeetingUrl.js create mode 100644 react/features/invite/components/add-people-dialog/web/InviteButton.js create mode 100644 react/features/prejoin/components/PrejoinThirdParty.js create mode 100644 react/features/prejoin/constants.js diff --git a/css/_mixins.scss b/css/_mixins.scss index 51a4a42f3..11ec2a6e7 100644 --- a/css/_mixins.scss +++ b/css/_mixins.scss @@ -206,13 +206,3 @@ bottom: 0; width: 35%; } - -/** - * Resizes elements width to fill the whole screen width with some margin - */ -@mixin adjust-for-max-width($width, $margin) { - @media (max-width: $width) { - margin: 0 $margin; - width: $width - 2 * $margin; - } -} diff --git a/css/_prejoin.scss b/css/_prejoin.scss deleted file mode 100644 index 86f89afc7..000000000 --- a/css/_prejoin.scss +++ /dev/null @@ -1,153 +0,0 @@ -.prejoin { - - &-input-area { - margin: 0 auto; - text-align: center; - - &-label { - display: block; - margin-bottom: 5px; - color: #ffffff; - font-weight: 300; - font-size: 15px; - line-height: 24px; - } - } - - &-title { - color: #fff; - font-size: 24px; - line-height: 32px; - margin-bottom: 16px; - } - - &-text-btns { - display: flex; - justify-content: space-between; - } - - &-input-label { - color: #A4B8D1; - font-size: 13px; - line-height: 20px; - margin-top: 32px 0 8px 0; - text-align: center; - width: 100%; - } - - &-checkbox { - border: 0; - height: 16px; - margin-right: 8px; - padding: 0; - width: 16px; - } - - &-checkbox-container { - margin-bottom: 14px; - width: 100%; - } - - &-error { - color: white; - background-color: rgba(225, 45, 45, 0.6); - border-radius: 3px; - width: 100%; - padding: 2px; - box-sizing: border-box; - margin-top: 4px; - font-size: 13px; - text-align: center; - } -} - -@mixin name-placeholder { - color: #fff; - font-weight: 300; - opacity: 0.6; -} - -.prejoin-preview { - &-status { - align-items: center; - align-self: stretch; - bottom: 0; - color: #fff; - display: flex; - font-size: 13px; - min-height: 24px; - justify-content: center; - position: absolute; - text-align: center; - width: 100%; - z-index: 1; - - &--warning { - background: rgba(241, 173, 51, 1); - } - &--ok { - background: rgba(49, 183, 106, 1); - } - } - - &-icon { - background-position: center; - background-repeat: no-repeat; - display: inline-block; - height: 16px; - margin-right: 8px; - width: 16px; - } - - &-error-desc { - margin-right: 4px; - color: #fff; - font-weight: bold; - } - - .settings-button-container { - width: 49px; - margin: 0 8px; - } - - &-dropdown-btns { - width: 320px; - padding: 8px 0; - - @include adjust-for-max-width(320px, 8px); - } - - &-dropdown-btn { - align-items: center; - color: #1C2025; - cursor: pointer; - display: flex; - height: 40px; - font-size: 15px; - line-height: 24px; - padding: 0 16px; - - &:hover { - background-color: #DAEBFA; - } - } - - &-dropdown-icon { - display: inline-block; - margin-right: 16px; - - & > svg { - fill: #1C2025; - } - } - - &-dropdown-container { - margin-top: 16px; - - & > div:nth-child(2) { - background: #fff; - padding: 0; - } - } - -} diff --git a/css/_toolbars.scss b/css/_toolbars.scss index 99c95a8c9..3ee0f3ce5 100644 --- a/css/_toolbars.scss +++ b/css/_toolbars.scss @@ -334,7 +334,7 @@ border-radius: 0; display: flex; justify-content: space-evenly; - padding: 6px 0; + padding: 8px 0; width: 100%; } diff --git a/css/_variables.scss b/css/_variables.scss index 1b0a6a49b..f8fae8a97 100644 --- a/css/_variables.scss +++ b/css/_variables.scss @@ -264,3 +264,9 @@ $chromeExtensionBannerRightInMeeeting: 10px; */ $smallScreen: 700px; $verySmallScreen: 500px; + +/** +* Prejoin / premeeting screen +*/ + +$prejoinDefaultContentWidth: 336px; \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index 810209183..98e2ebbad 100644 --- a/css/main.scss +++ b/css/main.scss @@ -79,7 +79,6 @@ $flagsImagePath: "../images/"; @import 'filmstrip/vertical_filmstrip'; @import 'filmstrip/vertical_filmstrip_overrides'; @import 'labels'; -@import 'lobby'; @import 'unsupported-browser/main'; @import 'modals/invite/add-people'; @import 'deep-linking/main'; @@ -95,15 +94,12 @@ $flagsImagePath: "../images/"; @import 'meter'; @import 'audio-preview'; @import 'video-preview'; -@import 'prejoin'; -@import 'prejoin-dialog'; +@import 'premeeting/main'; @import 'country-picker'; @import 'modals/invite/invite_more'; @import 'modals/security/security'; -@import 'premeeting-screens'; @import 'e2ee'; @import 'responsive'; -@import 'connection-status'; @import 'drawer'; @import 'participants-pane'; @import 'reactions-menu'; diff --git a/css/_connection-status.scss b/css/premeeting/_connection-status.scss similarity index 60% rename from css/_connection-status.scss rename to css/premeeting/_connection-status.scss index b0ebacdec..52ff1d232 100644 --- a/css/_connection-status.scss +++ b/css/premeeting/_connection-status.scss @@ -1,32 +1,24 @@ .con-status { + border-radius: 6px; + color: #fff; + font-size: 12px; + letter-spacing: 0.16px; + line-height: 16px; position: absolute; - top: 24px; width: 100%; - z-index: $toolbarZ + 3; - - &-container { - border-radius: 3px; - color: #fff; - font-size: 13px; - line-height: 13px; - margin: 0 auto; - width: 320px; - - @include adjust-for-max-width(320px, 8px); - } &-header { - background: rgba(28, 32, 37, .5); + background-color: rgba(0, 0, 0, 0.7); align-items: center; display: flex; - justify-content: space-between; + padding: 8px 12px; } &-circle { border-radius: 50%; display: inline-block; padding: 4px; - margin: 8px; + margin-right: 16px; } &--good { @@ -42,14 +34,7 @@ } &-arrow { - height: 36px; - width: 36px; - border-radius: 3px; - margin-left: 8px; - margin-right: 2px; - display: flex; - align-items: center; - justify-content: center; + margin-left: auto; transition: background-color 0.16s ease-out; &--up { @@ -70,7 +55,7 @@ } &-details { - background: rgba(28, 32, 37, .5); + background-color: rgba(0, 0, 0, 0.7); border-top: 1px solid #5E6D7A; padding: 16px; transition: opacity 0.16s ease-out; diff --git a/css/premeeting/_device-status.scss b/css/premeeting/_device-status.scss new file mode 100644 index 000000000..aff964244 --- /dev/null +++ b/css/premeeting/_device-status.scss @@ -0,0 +1,35 @@ +.device { + &-status { + align-items: center; + align-self: stretch; + color: #fff; + display: flex; + font-size: 14px; + font-weight: 400; + justify-content: center; + line-height: 20px; + margin-top: 8px; + padding: 6px; + text-align: center; + } + + &-icon { + background-position: center; + background-repeat: no-repeat; + display: inline-block; + height: 16px; + margin-right: 10px; + width: 16px; + + &--warning { + svg path { + fill: rgba(241, 173, 51, 1); + } + } + &--ok { + svg path { + fill: #189b55; + } + } + } +} diff --git a/css/_lobby.scss b/css/premeeting/_lobby.scss similarity index 85% rename from css/_lobby.scss rename to css/premeeting/_lobby.scss index f9c93588a..3264a92cc 100644 --- a/css/_lobby.scss +++ b/css/premeeting/_lobby.scss @@ -1,18 +1,21 @@ -#lobby-screen { - .content { +.lobby-screen { + font-size: 16px; + font-weight: 400; + line-height: 26px; - .container { - align-items: center; - display: flex; - flex-direction: column; + &-content { + align-items: center; + display: flex; + flex-direction: column; - .spinner { - margin: 30px; - } + .spinner { + margin: 8px; + } - .joining-message { - margin: 10px; - } + .joining-message { + color: white; + margin: 24px auto; + text-align: center; } } } @@ -68,7 +71,7 @@ button { align-self: stretch; - margin: 8px 0; + margin-bottom: 8px 0; padding: 12px; transition: .2s transform ease; diff --git a/css/premeeting/_main.scss b/css/premeeting/_main.scss new file mode 100644 index 000000000..fbf2b0242 --- /dev/null +++ b/css/premeeting/_main.scss @@ -0,0 +1,7 @@ +@import 'connection-status'; +@import 'device-status'; +@import 'lobby'; +@import 'premeeting-screens'; +@import 'prejoin'; +@import 'prejoin-dialog'; +@import 'prejoin-third-party'; diff --git a/css/_prejoin-dialog.scss b/css/premeeting/_prejoin-dialog.scss similarity index 100% rename from css/_prejoin-dialog.scss rename to css/premeeting/_prejoin-dialog.scss diff --git a/css/premeeting/_prejoin-third-party.scss b/css/premeeting/_prejoin-third-party.scss new file mode 100644 index 000000000..efde129ee --- /dev/null +++ b/css/premeeting/_prejoin-third-party.scss @@ -0,0 +1,39 @@ +$sidePanelWidth: 300px; + +.prejoin-third-party { + flex-direction: column-reverse; + + .content { + height: auto; + margin: 0 auto; + + .new-toolbox { + width: auto; + } + } + + #preview { + background-color: transparent; + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + + .avatar { + display: none; + } + } + + &.splash { + .content { + margin-left: calc((100% - #{$prejoinDefaultContentWidth} + #{$sidePanelWidth}) / 2) + } + } + + &.guest { + .content { + margin-bottom: auto; + } + } +} diff --git a/css/premeeting/_prejoin.scss b/css/premeeting/_prejoin.scss new file mode 100644 index 000000000..ba1fd0244 --- /dev/null +++ b/css/premeeting/_prejoin.scss @@ -0,0 +1,73 @@ +.prejoin { + &-input-area { + width: 100%; + } + + &-checkbox-container { + margin-bottom: 16px; + width: 100%; + text-align: center; + } + + &-error { + color: white; + background-color: #E04757; + border-radius: 6px; + padding: 4px; + box-sizing: border-box; + margin-bottom: 16px; + margin-top: -8px; + font-size: 12px; + text-align: center; + width: 100%; + } +} + +.prejoin-preview { + &-dropdown-btns { + padding: 8px 0; + width: calc(100% - 48px); + } + + &-dropdown-btn { + align-items: center; + color: #1C2025; + cursor: pointer; + display: flex; + height: 40px; + font-size: 15px; + line-height: 24px; + padding: 0 16px; + + &:hover { + background-color: #DAEBFA; + } + } + + &-dropdown-icon { + display: inline-block; + margin-right: 16px; + + & > svg { + fill: #1C2025; + } + } + + &-dropdown-container { + position: relative; + width: 100%; + + /** + * Override default InlineDialog behaviour, since it does not play nicely with relative widths + */ + & > div:nth-child(2) { + background: #fff; + padding: 0; + position: absolute !important; + top: 48px !important; + transform: none !important; + width: 100%; + } + } + } + \ No newline at end of file diff --git a/css/_premeeting-screens.scss b/css/premeeting/_premeeting-screens.scss similarity index 51% rename from css/_premeeting-screens.scss rename to css/premeeting/_premeeting-screens.scss index 9ff9a7f1d..7efef32a9 100644 --- a/css/_premeeting-screens.scss +++ b/css/premeeting/_premeeting-screens.scss @@ -1,47 +1,27 @@ -/** - * Shared style for full screen local track based dialogs/modals. - */ .premeeting-screen { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - } - - .premeeting-screen { - align-items: stretch; - background: radial-gradient(50% 50% at 50% 50%, #2A3A4B 20.83%, #1E2A36 100%); + background: #292929; + bottom: 0; display: flex; - flex-direction: column; font-size: 1.3em; + left: 0; + position: absolute; + right: 0; + top: 0; z-index: $toolbarZ + 1; - &-avatar { - background-color: #A4B8D1; - margin-bottom: 24px; - - text { - fill: black; - font-size: 26px; - font-weight: 400; - } - } - .action-btn { - border-radius: 3px; + border-radius: 6px; box-sizing: border-box; color: #fff; cursor: pointer; display: inline-block; - font-size: 15px; + font-size: 14px; line-height: 24px; + margin-bottom: 16px; padding: 7px 16px; position: relative; text-align: center; - width: 320px; - - @include adjust-for-max-width(320px, 8px); + width: 100%; &.primary { background: #0376DA; @@ -49,8 +29,8 @@ } &.secondary { - background: transparent; - border: 1px solid #5E6D7A; + background: #3D3D3D; + border: 1px solid transparent; } &.text { @@ -96,130 +76,150 @@ .content { align-items: center; + box-sizing: border-box; display: flex; - flex: 1; flex-direction: column; - justify-content: flex-end; - padding-bottom: 24px; + flex-shrink: 0; + height: 100%; + margin: 0 110px; + padding: 24px 0 16px; + position: relative; + width: $prejoinDefaultContentWidth; z-index: $toolbarZ + 2; - .title { - color: #fff; - font-size: 24px; - line-height: 32px; - margin-bottom: 16px; - } - - .copy-meeting { + &-controls { align-items: center; - cursor: pointer; - color: #fff; display: flex; flex-direction: column; - font-size: 15px; - font-weight: 300; - justify-content: center; - line-height: 24px; - margin-bottom: 16px; + margin: auto; + width: 100%; - .url { - background: rgba(28, 32, 37, 0.5); - border-radius: 4px; - display: flex; - padding: 8px 10px; - transition: background 0.16s ease-out; - - &:hover { - background: #1C2025; + .title { + color: #fff; + font-size: 28px; + font-weight: 600; + letter-spacing: -0.015; + line-height: 36px; + margin-bottom: 32px; + text-align: center; + } + + input.field { + background-color: white; + border: none; + outline: none; + border-radius: 6px; + font-size: 14px; + line-height: 20px; + margin-bottom: 16px; + color: #1C2025; + padding: 10px 16px; + text-align: center; + width: 100%; + + &.error { + border: 1px solid #E04757; } - - &.done { - background: #31B76A; - } - - .jitsi-icon { - margin-left: 10px; + + &.focused { + box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white; } } - .copy-button{ - width: 298px; - } - .copy-meeting-text { - width: 266px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + #new-toolbox { + bottom: 0; + margin-bottom: 16px; + position: relative; + transition: none; - &:hover { - align-self: stretch; - } - - textarea { - border-width: 0; - height: 0; - opacity: 0; - padding: 0; - width: 0; - } - } - - input.field { - background-color: white; - border: none; - outline: none; - border-radius: 3px; - font-size: 15px; - line-height: 24px; - color: #1C2025; - padding: 8px 0; - text-align: center; - width: 320px; - - @include adjust-for-max-width(320px, 8px); - - &.error { - box-shadow: 0px 0px 4px 3px rgba(225, 45, 45, 0.4); - } - - &.focused { - box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white; + .toolbox-content, + .toolbox-content-wrapper, + .toolbox-content-items { + box-sizing: border-box; + width: 100%; + } } } } - .media-btn-container { - display: flex; - justify-content: center; - margin: 24px 0 16px 0; - width: 100%; - - &> div { - margin: 0 12px; + @media (max-width: 1000px) { + flex-direction: column-reverse; + + .content { + height: auto; + margin: 0 auto; } + + .con-status { + margin: 24px auto; + position: fixed; + top: 0; + width: $prejoinDefaultContentWidth; + } + } + + @media (max-width: 400px) { + .content { + padding: 16px; + width: 100%; + + .title { + font-size: 20px; + line-height: 28px; + letter-spacing: -0.012; + margin-bottom: 24px; + } + } + + .con-status { + margin: 16px; + width: calc(100% - 32px); + } + + input.field { + font-size: 16px; + padding: 14px 16px; + } + + .action-btn { + font-size: 16px; + padding: 11px 16px; + } + + .toolbox-content-items { + border-radius: 0; + display: flex; + justify-content: space-evenly; + padding: 8px 0; + } + } + + input::placeholder { + color: #040404; } } #preview { + background: #040404; + display: flex; + align-items: center; + justify-content: center; height: 100%; - position: absolute; width: 100%; - &.no-video { - background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF; - text-align: center; - } - .avatar { - background: #A4B8D1; - margin: 0 auto; + background: #0045B3; + + text { + fill: white; + font-size: 26px; + font-weight: 400; + } } video { height: 100%; object-fit: cover; - position: absolute; width: 100%; } } @@ -241,16 +241,14 @@ } .toggle-button { - border-radius: 3px; + border-radius: 6px; cursor: pointer; color: #fff; font-size: 13px; height: 40px; margin: 0 auto; transition: background 0.16s ease-out; - width: 320px; - @include adjust-for-max-width(320px, 8px); @include flex-centered(); svg { diff --git a/lang/main-de.json b/lang/main-de.json index 493586420..a56763abc 100644 --- a/lang/main-de.json +++ b/lang/main-de.json @@ -209,7 +209,6 @@ "e2eeLabel": "Ende-zu-Ende-Verschlüsselung aktivieren", "e2eeWarning": "WARNUNG: Nicht alle Personen dieser Konferenz scheinen Ende-zu-Ende-Verschlüsselung zu unterstützen. Wenn Sie diese aktivieren, können die entsprechenden Personen nichts mehr sehen oder hören.", "enterDisplayName": "Bitte geben Sie hier Ihren Namen ein", - "enterDisplayNameToJoin" : "Benutzername für Konferenz eingeben" , "embedMeeting": "Besprechung einbetten", "error": "Fehler", "gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.", diff --git a/lang/main-eo.json b/lang/main-eo.json index c9dff5fbb..25966fe0c 100644 --- a/lang/main-eo.json +++ b/lang/main-eo.json @@ -179,7 +179,7 @@ "e2eeLabel": "Ŝlosilo", "e2eeTitle": "Tutvoja ĉifrado", "e2eeWarning": "

ATENTIGO: Ne ĉiuj partoprenantoj en ĉi tiu kunveno ŝajnas havi subtenon de tutvoja ĉifrado. Se vi ŝaltos ĝin, ili ne povos vidi aŭ aŭdi vin.

", - "enterDisplayName": "Please enter your name here", + "enterDisplayName": "Enter your name here", "error": "Eraro", "externalInstallationMsg": "Vi devas instali nian ekranvidadan kromprogramon.", "externalInstallationTitle": "Kromprogramo bezonata", diff --git a/lang/main-eu.json b/lang/main-eu.json index c6183534e..da0917cde 100644 --- a/lang/main-eu.json +++ b/lang/main-eu.json @@ -203,7 +203,6 @@ "e2eeLabel": "Aktibatu puntutik punturako zifratzea", "e2eeWarning": "OHARRA: bileraren partaide guztiek ezin dute puntutik punturako zifratzea erabili. Aukera hau aktibatzen baduzu, batzuk ezingo zaituzte ikusi eta entzun.", "enterDisplayName": "Sartu zure izena hemen", - "enterDisplayNameToJoin": "Mesedez idatzi zure izena bileran sartzeko", "embedMeeting": "Kapsulatu bilera", "error": "Errorea", "gracefulShutdown": "Zerbitzua ez dago erabilgarri mantentze-lanak direla eta. Saiatu berriro beranduago.", diff --git a/lang/main-fr.json b/lang/main-fr.json index ab516c6a5..5bf72e0f2 100644 --- a/lang/main-fr.json +++ b/lang/main-fr.json @@ -213,7 +213,6 @@ "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", - "enterDisplayNameToJoin": "Merci de saisir votre nom pour rejoindre", "embedMeeting": "Intégrer la réunion", "error": "Erreur", "gracefulShutdown": "Notre service est actuellement en maintenance. Veuillez réessayer plus tard.", diff --git a/lang/main-id.json b/lang/main-id.json index f8428aefd..9311fa326 100644 --- a/lang/main-id.json +++ b/lang/main-id.json @@ -175,7 +175,7 @@ "dismiss": "Dismiss", "displayNameRequired": "Hi! What’s your name?", "done": "Done", - "enterDisplayName": "Please enter your name here", + "enterDisplayName": "Enter your name here", "error": "Error", "externalInstallationMsg": "You need to install our desktop sharing extension.", "externalInstallationTitle": "Extension required", diff --git a/lang/main-pt.json b/lang/main-pt.json index da870cb04..1bf43d3be 100644 --- a/lang/main-pt.json +++ b/lang/main-pt.json @@ -197,7 +197,6 @@ "e2eeLabel": "Habilitar encriptação de ponta a ponta", "e2eeWarning": "AVISO: Nem todos os participantes neste encontro parecem ter apoio para a encriptação de ponta a ponta. Se o permitir, eles não o poderão ver nem ouvir.", "enterDisplayName": "Digite o seu nome aqui", - "enterDisplayNameToJoin": "Por favor, digite o seu nome para participar", "embedMeeting": "Embutir reunião", "error": "Erro", "gracefulShutdown": "O nosso serviço está atualmente em manutenção. Por favor, tente novamente mais tarde.", diff --git a/lang/main-ptBR.json b/lang/main-ptBR.json index ce12390ad..e309ed099 100644 --- a/lang/main-ptBR.json +++ b/lang/main-ptBR.json @@ -209,7 +209,6 @@ "e2eeLabel": "Enable End-to-End Encryption", "e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.", "enterDisplayName": "Digite seu nome aqui", - "enterDisplayNameToJoin": "Digite seu nome para participar", "embedMeeting": "Reunião em formato compacto", "error": "Erro", "gracefulShutdown": "Nosso serviço está em manutenção. Tente novamente mais tarde.", diff --git a/lang/main-sq.json b/lang/main-sq.json index 62a86fae0..fe23c036b 100644 --- a/lang/main-sq.json +++ b/lang/main-sq.json @@ -209,7 +209,6 @@ "e2eeLabel": "Aktivizo Fshehtëzim Skaj-më-Skaj", "e2eeWarning": "KUJDES: Jo të gjithë pjesëmarrësit në këtë takim duket të kenë mbulim për fshehtëzim Skaj-më-Skaj. Në e aktivizofshi, ata s’do të jenë në gjendje t’ju shohin apo dëgjojnë.", "enterDisplayName": "Ju lutemi, jepni këtu emrin tuaj", - "enterDisplayNameToJoin": "Që të merrni pjesë, ju lutemi, jepni emrin tuaj", "embedMeeting": "Trupëzoni takim", "error": "Gabim", "gracefulShutdown": "Shërbimi ynë është aktualisht i ndërprerë, për punë mirëmbajtjeje. Ju lutemi, riprovoni më vonë.", diff --git a/lang/main-zhTW.json b/lang/main-zhTW.json index 05bdf4894..677f7b30d 100644 --- a/lang/main-zhTW.json +++ b/lang/main-zhTW.json @@ -209,7 +209,6 @@ "e2eeLabel": "啟用端對端加密", "e2eeWarning": "警告:看來不是每位此會議的參與者都有啟用端對端加密,如果您啟用了,他們可能無法看/聽到您。", "enterDisplayName": "請在此輸入您自己的名字", - "enterDisplayNameToJoin": "請輸入您的名字以加入", "embedMeeting": "嵌入會議", "error": "錯誤", "gracefulShutdown": "我們的服務目前關閉維護中,請稍後再試。", diff --git a/lang/main.json b/lang/main.json index 2eb32677c..e45b91de8 100644 --- a/lang/main.json +++ b/lang/main.json @@ -212,8 +212,7 @@ "e2eeDescription": "End-to-End Encryption is currently EXPERIMENTAL. Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.", "e2eeLabel": "Enable End-to-End Encryption", "e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.", - "enterDisplayName": "Please enter your name here", - "enterDisplayNameToJoin": "Please enter your name to join", + "enterDisplayName": "Enter your name here", "embedMeeting": "Embed meeting", "error": "Error", "gracefulShutdown": "Our service is currently down for maintenance. Please try again later.", @@ -694,7 +693,7 @@ "joinWithoutAudio": "Join without audio", "initiated": "Call initiated", "linkCopied": "Link copied to clipboard", - "lookGood": "It sounds like your microphone is working properly", + "lookGood": "Your microphone is working properly", "or": "or", "premeeting": "Pre meeting", "showScreen": "Enable pre meeting screen", @@ -1121,7 +1120,7 @@ "admitAll": "Admit all", "knockingParticipantList": "Knocking participant list", "allow": "Allow", - "backToKnockModeButton": "No password, ask to join instead", + "backToKnockModeButton": "Ask to join", "dialogTitle": "Lobby mode", "disableDialogContent": "Lobby mode is currently enabled. This feature ensures that unwanted participants can't join your meeting. Do you want to disable it?", "disableDialogSubmit": "Disable", @@ -1131,6 +1130,7 @@ "enableDialogText": "Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator.", "enterPasswordButton": "Enter meeting password", "enterPasswordTitle": "Enter password to join meeting", + "errorMissingPassword": "Please enter the meeting password", "invalidPassword": "Invalid password", "joiningMessage": "You'll join the meeting as soon as someone accepts your request", "joinWithPasswordMessage": "Trying to join with password, please wait...", diff --git a/react/features/app/components/App.web.js b/react/features/app/components/App.web.js index f19edb7f9..b8c03a458 100644 --- a/react/features/app/components/App.web.js +++ b/react/features/app/components/App.web.js @@ -43,9 +43,11 @@ export class App extends AbstractApp { */ _renderDialogContainer() { return ( - - - + + + + + ); } } diff --git a/react/features/base/conference/middleware.web.js b/react/features/base/conference/middleware.web.js index c61f657e8..778c01377 100644 --- a/react/features/base/conference/middleware.web.js +++ b/react/features/base/conference/middleware.web.js @@ -1,21 +1,20 @@ // @flow import { setPrejoinPageVisibility, setSkipPrejoinOnReload } from '../../prejoin'; +import { PREJOIN_SCREEN_STATES } from '../../prejoin/constants'; import { JitsiConferenceErrors } from '../lib-jitsi-meet'; import { MiddlewareRegistry } from '../redux'; import { CONFERENCE_FAILED, CONFERENCE_JOINED } from './actionTypes'; import './middleware.any'; -declare var APP: Object; - MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { const { enableForcedReload } = getState()['features/base/config']; switch (action.type) { case CONFERENCE_JOINED: { if (enableForcedReload) { - dispatch(setPrejoinPageVisibility(false)); + dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN)); dispatch(setSkipPrejoinOnReload(false)); } diff --git a/react/features/base/icons/svg/check-solid.svg b/react/features/base/icons/svg/check-solid.svg new file mode 100644 index 000000000..bf948de1b --- /dev/null +++ b/react/features/base/icons/svg/check-solid.svg @@ -0,0 +1,3 @@ + + + diff --git a/react/features/base/icons/svg/index.js b/react/features/base/icons/svg/index.js index fc270d53a..bb03001d0 100644 --- a/react/features/base/icons/svg/index.js +++ b/react/features/base/icons/svg/index.js @@ -26,6 +26,7 @@ export { default as IconChat } from './chat.svg'; export { default as IconChatSend } from './send.svg'; export { default as IconChatUnread } from './chat-unread.svg'; export { default as IconCheck } from './check.svg'; +export { default as IconCheckSolid } from './check-solid.svg'; export { default as IconClose } from './close.svg'; export { default as IconCloseCircle } from './close-circle.svg'; export { default as IconCloseX } from './close-x.svg'; diff --git a/react/features/base/premeeting/components/web/ConnectionStatus.js b/react/features/base/premeeting/components/web/ConnectionStatus.js index d1bb781a9..468609d33 100644 --- a/react/features/base/premeeting/components/web/ConnectionStatus.js +++ b/react/features/base/premeeting/components/web/ConnectionStatus.js @@ -79,37 +79,35 @@ function ConnectionStatus({ connectionDetails, t, connectionType }: Props) { return (
-
-
-
- -
- {t(connectionText)} +
+
+ size = { 16 } + src = { icon } />
-
- {detailsText}
+ {t(connectionText)} +
+
+ {detailsText}
); } diff --git a/react/features/base/premeeting/components/web/CopyMeetingUrl.js b/react/features/base/premeeting/components/web/CopyMeetingUrl.js deleted file mode 100644 index a84875e76..000000000 --- a/react/features/base/premeeting/components/web/CopyMeetingUrl.js +++ /dev/null @@ -1,67 +0,0 @@ -// @flow - -import React, { Component } from 'react'; - -import CopyMeetingLinkSection - from '../../../../invite/components/add-people-dialog/web/CopyMeetingLinkSection'; -import { getCurrentConferenceUrl } from '../../../connection'; -import { translate } from '../../../i18n'; -import { connect } from '../../../redux'; - -type Props = { - - /** - * The meeting url. - */ - url: string, - - /** - * Used for translation. - */ - t: Function, - - /** - * Used to determine if invitation link should be automatically copied - * after creating a meeting. - */ - _enableAutomaticUrlCopy: boolean, -}; - - -/** - * Component used to copy meeting url on prejoin page. - */ -class CopyMeetingUrl extends Component { - - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - return ( -
- -
- ); - } -} - -/** - * Maps (parts of) the redux state to the React {@code Component} props. - * - * @param {Object} state - The redux state. - * @returns {Object} - */ -function mapStateToProps(state) { - const { enableAutomaticUrlCopy } = state['features/base/config']; - const { customizationReady } = state['features/dynamic-branding']; - - return { - url: customizationReady ? getCurrentConferenceUrl(state) : '', - _enableAutomaticUrlCopy: enableAutomaticUrlCopy || false - }; -} - -export default connect(mapStateToProps)(translate(CopyMeetingUrl)); diff --git a/react/features/base/premeeting/components/web/PreMeetingScreen.js b/react/features/base/premeeting/components/web/PreMeetingScreen.js index 47a7d2e76..c4ffc2a09 100644 --- a/react/features/base/premeeting/components/web/PreMeetingScreen.js +++ b/react/features/base/premeeting/components/web/PreMeetingScreen.js @@ -2,14 +2,10 @@ 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'; +import DeviceStatus from '../../../../prejoin/components/preview/DeviceStatus'; +import { Toolbox } from '../../../../toolbox/components/web'; import ConnectionStatus from './ConnectionStatus'; -import CopyMeetingUrl from './CopyMeetingUrl'; import Preview from './Preview'; type Props = { @@ -17,12 +13,12 @@ type Props = { /** * Children component(s) to be rendered on the screen. */ - children: React$Node, + children?: React$Node, /** - * Footer to be rendered for the page (if any). + * Additional CSS class names to set on the icon container. */ - footer?: React$Node, + className?: string, /** * The name of the participant. @@ -35,25 +31,25 @@ type Props = { showCopyUrlButton: boolean, /** - * Indicates whether the avatar should be shown when video is off + * Indicates whether the device status should be shown */ - showAvatar: boolean, - - /** - * Indicates whether the label and copy url action should be shown - */ - showConferenceInfo: boolean, - - /** - * Title of the screen. - */ - title: string, + showDeviceStatus: boolean, /** * The 'Skip prejoin' button to be rendered (if any). */ skipPrejoinButton?: React$Node, + /** + * Title of the screen. + */ + title?: string, + + /** + * Override for default toolbar buttons + */ + toolbarButtons?: Array, + /** * True if the preview overlay should be muted, false otherwise. */ @@ -62,14 +58,11 @@ type Props = { /** * The video track to render as preview (if omitted, the default local track will be rendered). */ - videoTrack?: Object, - - /** - * Array with the buttons which this Toolbox should display. - */ - visibleButtons?: Array + videoTrack?: Object } +const buttons = [ 'microphone', 'camera', 'select-background', 'invite', 'settings' ]; + /** * Implements a pre-meeting screen that can be used at various pre-meeting phases, for example * on the prejoin screen (pre-connection) or lobby (post-connection). @@ -81,9 +74,8 @@ export default class PreMeetingScreen extends PureComponent { * @static */ static defaultProps = { - showAvatar: true, showCopyUrlButton: true, - showConferenceInfo: true + showToolbox: true }; /** @@ -93,57 +85,37 @@ export default class PreMeetingScreen extends PureComponent { */ render() { const { - name, - showAvatar, - showConferenceInfo, - showCopyUrlButton, + children, + className, + showDeviceStatus, + skipPrejoinButton, title, + toolbarButtons, videoMuted, - videoTrack, - visibleButtons + videoTrack } = this.props; - const showSharingButton = allowUrlSharing() && showCopyUrlButton; + + const containerClassName = `premeeting-screen ${className ? className : ''}`; return ( -
- +
+
+ + +
+

+ { title } +

+ { children } + + { skipPrejoinButton } + { showDeviceStatus && } +
+
+ -
- {showAvatar && videoMuted && ( - - )} - {showConferenceInfo && ( - <> -

- { title } -

- {showSharingButton ? : null} - - )} - { this.props.children } -
-
-
- - - { ((visibleButtons && visibleButtons.includes('select-background')) - || (visibleButtons && visibleButtons.includes('videobackgroundblur'))) - && } -
-
-
- { this.props.skipPrejoinButton } - { this.props.footer } -
); } diff --git a/react/features/base/premeeting/components/web/Preview.js b/react/features/base/premeeting/components/web/Preview.js index 8cf895dc3..cb6013361 100644 --- a/react/features/base/premeeting/components/web/Preview.js +++ b/react/features/base/premeeting/components/web/Preview.js @@ -2,17 +2,30 @@ import React from 'react'; +import { getDisplayName } from '../../../../base/settings'; +import { Avatar } from '../../../avatar'; import { Video } from '../../../media'; +import { getLocalParticipant } from '../../../participants'; import { connect } from '../../../redux'; import { getLocalVideoTrack } from '../../../tracks'; export type Props = { + /** + * Local participant id + */ + _participantId: string, + /** * Flag controlling whether the video should be flipped or not. */ flipVideo: boolean, + /** + * The name of the user that is about to join. + */ + name: string, + /** * Flag signaling the visibility of camera preview. */ @@ -31,20 +44,27 @@ export type Props = { * @returns {ReactElement} */ function Preview(props: Props) { - const { videoMuted, videoTrack, flipVideo } = props; + const { _participantId, flipVideo, name, videoMuted, videoTrack } = props; const className = flipVideo ? 'flipVideoX' : ''; - if (!videoMuted && videoTrack) { - return ( -
-
- ); - } - - return null; + return ( +
+ {!videoMuted && videoTrack + ? ( +
+ ); } /** @@ -55,8 +75,13 @@ function Preview(props: Props) { * @returns {Props} */ function _mapStateToProps(state, ownProps) { + const name = getDisplayName(state); + const { id: _participantId } = getLocalParticipant(state); + return { + _participantId, flipVideo: state['features/base/settings'].localFlipX, + name, videoMuted: ownProps.videoTrack ? ownProps.videoMuted : state['features/base/media'].video.muted, videoTrack: ownProps.videoTrack || (getLocalVideoTrack(state['features/base/tracks']) || {}).jitsiTrack }; diff --git a/react/features/base/premeeting/functions.js b/react/features/base/premeeting/functions.js index 275bca19f..88f7de2dc 100644 --- a/react/features/base/premeeting/functions.js +++ b/react/features/base/premeeting/functions.js @@ -213,14 +213,3 @@ export function getConnectionData(state: Object) { connectionDetails: [] }; } - -/** - * Returns if url sharing is enabled in interface configuration. - * - * @returns {boolean} - */ -export function allowUrlSharing() { - return typeof interfaceConfig === 'undefined' - || typeof interfaceConfig.SHARING_FEATURES === 'undefined' - || (interfaceConfig.SHARING_FEATURES.length && interfaceConfig.SHARING_FEATURES.indexOf('url') > -1); -} diff --git a/react/features/conference/components/native/Conference.js b/react/features/conference/components/native/Conference.js index 4700c0693..c51ec0d66 100644 --- a/react/features/conference/components/native/Conference.js +++ b/react/features/conference/components/native/Conference.js @@ -22,6 +22,8 @@ import { import { AddPeopleDialog, CalleeInfoContainer } from '../../../invite'; import { LargeVideo } from '../../../large-video'; import { KnockingParticipantList } from '../../../lobby'; +import { LobbyScreen } from '../../../lobby/components/native'; +import { getIsLobbyVisible } from '../../../lobby/functions'; import { BackButtonRegistry } from '../../../mobile/back-button'; import { ParticipantsPane } from '../../../participants-pane/components/native'; import { Captions } from '../../../subtitles'; @@ -98,6 +100,11 @@ type Props = AbstractProps & { */ _toolboxVisible: boolean, + /** + * Indicates whether the lobby screen should be visible. + */ + _showLobby: boolean, + /** * The redux {@code dispatch} function. */ @@ -154,7 +161,11 @@ class Conference extends AbstractConference { * @returns {ReactElement} */ render() { - const { _fullscreenEnabled } = this.props; + const { _fullscreenEnabled, _showLobby } = this.props; + + if (_showLobby) { + return ; + } return ( @@ -427,6 +438,7 @@ function _mapStateToProps(state) { _largeVideoParticipantId: state['features/large-video'].participantId, _pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED), _reducedUI: reducedUI, + _showLobby: getIsLobbyVisible(state), _toolboxVisible: isToolboxVisible(state) }; } diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index 764d94197..e12753650 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -15,9 +15,10 @@ import { Filmstrip } from '../../../filmstrip'; import { CalleeInfoContainer } from '../../../invite'; import { LargeVideo } from '../../../large-video'; import { KnockingParticipantList, LobbyScreen } from '../../../lobby'; +import { getIsLobbyVisible } from '../../../lobby/functions'; import { ParticipantsPane } from '../../../participants-pane/components/web'; import { getParticipantsPaneOpen } from '../../../participants-pane/functions'; -import { Prejoin, isPrejoinPageVisible } from '../../../prejoin'; +import { Prejoin, isPrejoinPageVisible, isPrejoinPageLoading } from '../../../prejoin'; import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web'; import { Toolbox } from '../../../toolbox/components/web'; import { LAYOUTS, getCurrentLayout } from '../../../video-layout'; @@ -70,11 +71,6 @@ type Props = AbstractProps & { */ _backgroundAlpha: number, - /** - * Returns true if the 'lobby screen' is visible. - */ - _isLobbyScreenVisible: boolean, - /** * If participants pane is visible or not. */ @@ -96,6 +92,11 @@ type Props = AbstractProps & { */ _roomName: string, + /** + * If lobby page is visible or not. + */ + _showLobby: boolean, + /** * If prejoin page is visible or not. */ @@ -207,9 +208,9 @@ class Conference extends AbstractConference { */ render() { const { - _isLobbyScreenVisible, _isParticipantsPaneVisible, _layoutClassName, + _showLobby, _showPrejoin } = this.props; @@ -237,7 +238,7 @@ class Conference extends AbstractConference {
- { _showPrejoin || _isLobbyScreenVisible || } + { _showPrejoin || _showLobby || } { this.renderNotificationsContainer() } @@ -245,7 +246,7 @@ class Conference extends AbstractConference { { _showPrejoin && } - + { _showLobby && }
@@ -373,12 +374,12 @@ function _mapStateToProps(state) { return { ...abstractMapStateToProps(state), _backgroundAlpha: backgroundAlpha, - _isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen, _isParticipantsPaneVisible: getParticipantsPaneOpen(state), _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)], _mouseMoveCallbackInterval: mouseMoveCallbackInterval, _roomName: getConferenceNameForTitle(state), - _showPrejoin: isPrejoinPageVisible(state) + _showLobby: getIsLobbyVisible(state), + _showPrejoin: isPrejoinPageVisible(state) || isPrejoinPageLoading(state) }; } diff --git a/react/features/invite/components/add-people-dialog/web/InviteButton.js b/react/features/invite/components/add-people-dialog/web/InviteButton.js new file mode 100644 index 000000000..3a3ea8f2c --- /dev/null +++ b/react/features/invite/components/add-people-dialog/web/InviteButton.js @@ -0,0 +1,44 @@ +// @flow + +import { createToolbarEvent, sendAnalytics } from '../../../../analytics'; +import { translate } from '../../../../base/i18n'; +import { IconAddPeople } from '../../../../base/icons'; +import { connect } from '../../../../base/redux'; +import { AbstractButton, type AbstractButtonProps } from '../../../../base/toolbox/components'; +import { beginAddPeople } from '../../../actions.any'; + +/** + * The type of the React {@code Component} props of {@link EmbedMeetingButton}. + */ +type Props = AbstractButtonProps & { + + /** + * The redux {@code dispatch} function. + */ + dispatch: Function +}; + +/** + * Implementation of a button for opening invite people dialog. + */ +class InviteButton extends AbstractButton { + accessibilityLabel = 'toolbar.accessibilityLabel.invite'; + icon = IconAddPeople; + label = 'toolbar.invite'; + tooltip = 'toolbar.invite'; + + /** + * Handles clicking / pressing the button, and opens the appropriate dialog. + * + * @protected + * @returns {void} + */ + _handleClick() { + const { dispatch } = this.props; + + sendAnalytics(createToolbarEvent('invite')); + dispatch(beginAddPeople()); + } +} + +export default translate(connect()(InviteButton)); diff --git a/react/features/invite/components/add-people-dialog/web/index.js b/react/features/invite/components/add-people-dialog/web/index.js index f2e67e631..1aedf86dc 100644 --- a/react/features/invite/components/add-people-dialog/web/index.js +++ b/react/features/invite/components/add-people-dialog/web/index.js @@ -1,3 +1,4 @@ // @flow export { default as AddPeopleDialog } from './AddPeopleDialog'; +export { default as InviteButton } from './InviteButton'; diff --git a/react/features/lobby/actionTypes.js b/react/features/lobby/actionTypes.js index 8d00a4678..b734263ea 100644 --- a/react/features/lobby/actionTypes.js +++ b/react/features/lobby/actionTypes.js @@ -20,6 +20,11 @@ export const SET_LOBBY_MODE_ENABLED = 'SET_LOBBY_MODE_ENABLED'; */ export const SET_KNOCKING_STATE = 'SET_KNOCKING_STATE'; +/** + * Action type to set the lobby visibility. + */ +export const SET_LOBBY_VISIBILITY = 'TOGGLE_LOBBY_VISIBILITY'; + /** * Action type to set the password join failed status. */ diff --git a/react/features/lobby/actions.any.js b/react/features/lobby/actions.any.js index 75f6f5b89..1c5b6ff65 100644 --- a/react/features/lobby/actions.any.js +++ b/react/features/lobby/actions.any.js @@ -6,6 +6,8 @@ import { getCurrentConference } from '../base/conference'; +import { SET_LOBBY_VISIBILITY } from './actionTypes'; + /** * Action to toggle lobby mode on or off. * @@ -23,3 +25,27 @@ export function toggleLobbyMode(enabled: boolean) { } }; } + +/** + * Action to open the lobby screen. + * + * @returns {openDialog} + */ +export function openLobbyScreen() { + return { + type: SET_LOBBY_VISIBILITY, + visible: true + }; +} + +/** + * Action to hide the lobby screen. + * + * @returns {hideDialog} + */ +export function hideLobbyScreen() { + return { + type: SET_LOBBY_VISIBILITY, + visible: false + }; +} diff --git a/react/features/lobby/actions.web.js b/react/features/lobby/actions.web.js index 8f0775afa..48f37a778 100644 --- a/react/features/lobby/actions.web.js +++ b/react/features/lobby/actions.web.js @@ -9,7 +9,6 @@ import { sendLocalParticipant, setPassword } from '../base/conference'; -import { hideDialog, openDialog } from '../base/dialog'; import { getLocalParticipant } from '../base/participants'; export * from './actions.any'; @@ -20,7 +19,6 @@ import { SET_LOBBY_MODE_ENABLED, SET_PASSWORD_JOIN_FAILED } from './actionTypes'; -import { LobbyScreen } from './components'; declare var APP: Object; @@ -44,15 +42,6 @@ export function cancelKnocking() { }; } -/** - * Action to hide the lobby screen. - * - * @returns {hideDialog} - */ -export function hideLobbyScreen() { - return hideDialog(LobbyScreen); -} - /** * Tries to join with a preset password. * @@ -83,15 +72,6 @@ export function knockingParticipantLeft(id: string) { }; } -/** - * Action to open the lobby screen. - * - * @returns {openDialog} - */ -export function openLobbyScreen() { - return openDialog(LobbyScreen, {}, true); -} - /** * Action to be executed when a participant starts knocking or an already knocking participant gets updated. * diff --git a/react/features/lobby/components/AbstractLobbyScreen.js b/react/features/lobby/components/AbstractLobbyScreen.js index bb8fd1a40..f2ced30c0 100644 --- a/react/features/lobby/components/AbstractLobbyScreen.js +++ b/react/features/lobby/components/AbstractLobbyScreen.js @@ -7,6 +7,7 @@ import { getFeatureFlag, INVITE_ENABLED } from '../../base/flags'; import { getLocalParticipant } from '../../base/participants'; import { getFieldValue } from '../../base/react'; import { updateSettings } from '../../base/settings'; +import { isDeviceStatusVisible } from '../../prejoin/functions'; import { cancelKnocking, joinWithPassword, setPasswordJoinFailed, startKnocking } from '../actions'; export const SCREEN_STATES = { @@ -17,6 +18,11 @@ export const SCREEN_STATES = { export type Props = { + /** + * Indicates whether the device status should be visible. + */ + _deviceStatusVisible: boolean, + /** * True if knocking is already happening, so we're waiting for a response. */ @@ -380,8 +386,10 @@ export function _mapStateToProps(state: Object): $Shape { const { knocking, passwordJoinFailed } = state['features/lobby']; const { iAmSipGateway } = state['features/base/config']; const showCopyUrlButton = inviteEnabledFlag || !disableInviteFunctions; + const deviceStatusVisible = isDeviceStatusVisible(state); return { + _deviceStatusVisible: deviceStatusVisible, _knocking: knocking, _meetingName: getConferenceName(state), _participantEmail: localParticipant?.email, diff --git a/react/features/lobby/components/native/LobbyScreen.js b/react/features/lobby/components/native/LobbyScreen.js index b4c5711e2..16bd4d7bd 100644 --- a/react/features/lobby/components/native/LobbyScreen.js +++ b/react/features/lobby/components/native/LobbyScreen.js @@ -27,15 +27,16 @@ class LobbyScreen extends AbstractLobbyScreen { return ( - - { t(this._getScreenTitleKey()) } - - - { _meetingName } - - { this._renderContent() } + onCancel = { this._onCancel }> + + + { t(this._getScreenTitleKey()) } + + + { _meetingName } + + { this._renderContent() } + ); } diff --git a/react/features/lobby/components/web/LobbyScreen.js b/react/features/lobby/components/web/LobbyScreen.js index 7d796f23d..6fbe1fdca 100644 --- a/react/features/lobby/components/web/LobbyScreen.js +++ b/react/features/lobby/components/web/LobbyScreen.js @@ -20,11 +20,13 @@ class LobbyScreen extends AbstractLobbyScreen { * @inheritdoc */ render() { - const { showCopyUrlButton, t } = this.props; + const { _deviceStatusVisible, showCopyUrlButton, t } = this.props; return ( { this._renderContent() } @@ -62,7 +64,7 @@ class LobbyScreen extends AbstractLobbyScreen { */ _renderJoining() { return ( -
+
@@ -113,13 +115,19 @@ class LobbyScreen extends AbstractLobbyScreen { const { _passwordJoinFailed, t } = this.props; return ( - + <> + + + {_passwordJoinFailed &&
{t('lobby.invalidPassword')}
} + ); } @@ -134,11 +142,10 @@ class LobbyScreen extends AbstractLobbyScreen { return ( <> - { t('lobby.passwordJoinButton') } + { t('prejoin.joinMeeting') } { ...state, lobbyEnabled: action.enabled }; + case SET_LOBBY_VISIBILITY: + return { + ...state, + lobbyVisible: action.visible + }; case SET_PASSWORD: return { ...state, diff --git a/react/features/prejoin/actions.js b/react/features/prejoin/actions.js index 0b271448f..f470ce22c 100644 --- a/react/features/prejoin/actions.js +++ b/react/features/prejoin/actions.js @@ -33,6 +33,7 @@ import { SET_PREJOIN_DEVICE_ERRORS, SET_PREJOIN_PAGE_VISIBILITY } from './actionTypes'; +import { type PREJOIN_SCREEN_STATE } from './constants'; import { getFullDialOutNumber, getDialOutConferenceUrl, @@ -480,10 +481,10 @@ export function setPrejoinDeviceErrors(value: Object) { /** * Action used to set the visibility of the prejoin page. * - * @param {boolean} value - The value. + * @param {string} value - The value. * @returns {Object} */ -export function setPrejoinPageVisibility(value: boolean) { +export function setPrejoinPageVisibility(value: PREJOIN_SCREEN_STATE) { return { type: SET_PREJOIN_PAGE_VISIBILITY, value diff --git a/react/features/prejoin/components/Prejoin.js b/react/features/prejoin/components/Prejoin.js index b1bc773da..124642a9b 100644 --- a/react/features/prejoin/components/Prejoin.js +++ b/react/features/prejoin/components/Prejoin.js @@ -4,7 +4,6 @@ import InlineDialog from '@atlaskit/inline-dialog'; import React, { Component } from 'react'; import { getRoomName } from '../../base/conference'; -import { getToolbarButtons } from '../../base/config'; import { translate } from '../../base/i18n'; import { Icon, IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons'; import { isVideoMutedByUser } from '../../base/media'; @@ -12,7 +11,6 @@ import { ActionButton, InputField, PreMeetingScreen, ToggleButton } from '../../ import { connect } from '../../base/redux'; import { getDisplayName, updateSettings } from '../../base/settings'; import { getLocalJitsiVideoTrack } from '../../base/tracks'; -import { isButtonEnabled } from '../../toolbox/functions.web'; import { joinConference as joinConferenceAction, joinConferenceWithoutAudio as joinConferenceWithoutAudioAction, @@ -28,9 +26,6 @@ import { } from '../functions'; import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog'; -import DeviceStatus from './preview/DeviceStatus'; - -declare var interfaceConfig: Object; type Props = { @@ -84,11 +79,6 @@ type Props = { */ setJoinByPhoneDialogVisiblity: Function, - /** - * Indicates whether the avatar should be shown when video is off - */ - showAvatar: boolean, - /** * Flag signaling the visibility of camera preview. */ @@ -99,26 +89,11 @@ type Props = { */ showErrorOnJoin: boolean, - /** - * Flag signaling the visibility of join label, input and buttons - */ - showJoinActions: boolean, - - /** - * Flag signaling the visibility of the conference URL section. - */ - showConferenceInfo: boolean, - /** * If 'JoinByPhoneDialog' is visible or not. */ showDialog: boolean, - /** - * Flag signaling the visibility of the skip prejoin toggle - */ - showSkipPrejoin: boolean, - /** * Used for translation. */ @@ -127,12 +102,7 @@ type Props = { /** * The JitsiLocalTrack to display. */ - videoTrack: ?Object, - - /** - * Array with the buttons which this Toolbox should display. - */ - visibleButtons: Array + videoTrack: ?Object }; type State = { @@ -152,17 +122,6 @@ type State = { * This component is displayed before joining a meeting. */ class Prejoin extends Component { - /** - * Default values for {@code Prejoin} component's properties. - * - * @static - */ - static defaultProps = { - showConferenceInfo: true, - showJoinActions: true, - showSkipPrejoin: true - }; - /** * Initializes a new {@code Prejoin} instance. * @@ -344,18 +303,15 @@ class Prejoin extends Component { */ render() { const { + deviceStatusVisible, hasJoinByPhoneButton, joinConference, joinConferenceWithoutAudio, name, - showAvatar, showCameraPreview, showDialog, - showConferenceInfo, - showJoinActions, t, - videoTrack, - visibleButtons + videoTrack } = this.props; const { _closeDialog, _onDropdownClose, _onJoinButtonClick, _onJoinKeyPress, _showDialogKeyPress, @@ -364,89 +320,78 @@ class Prejoin extends Component { return ( - {showJoinActions && ( -
-
- - + videoTrack = { videoTrack }> +
+ - {showError &&
{t('prejoin.errorMissingName')}
} + {showError &&
{t('prejoin.errorMissingName')}
} -
- -
- - { t('prejoin.joinWithoutAudio') } -
- {hasJoinByPhoneButton &&
- - { t('prejoin.joinAudioByPhone') } -
} -
} - isOpen = { showJoinByPhoneButtons } - onClose = { _onDropdownClose }> - - { t('prejoin.joinMeeting') } - - -
-
+
+ +
+ + { t('prejoin.joinWithoutAudio') } +
+ {hasJoinByPhoneButton &&
+ + { t('prejoin.joinAudioByPhone') } +
} +
} + isOpen = { showJoinByPhoneButtons } + onClose = { _onDropdownClose }> + + { t('prejoin.joinMeeting') } + +
- )} +
{ showDialog && ( { ); } - /** - * Renders the screen footer if any. - * - * @returns {React$Element} - */ - _renderFooter() { - return this.props.deviceStatusVisible && ; - } - /** * Renders the 'skip prejoin' button. * * @returns {React$Element} */ _renderSkipPrejoinButton() { - const { buttonIsToggled, t, showSkipPrejoin } = this.props; - - if (!showSkipPrejoin) { - return null; - } + const { buttonIsToggled, t } = this.props; return (
@@ -493,22 +425,11 @@ class Prejoin extends Component { * Maps (parts of) the redux state to the React {@code Component} props. * * @param {Object} state - The redux state. - * @param {Object} ownProps - The props passed to the component. * @returns {Object} */ -function mapStateToProps(state, ownProps): Object { +function mapStateToProps(state): Object { const name = getDisplayName(state); const showErrorOnJoin = isDisplayNameRequired(state) && !name; - const { showJoinActions } = ownProps; - const isInviteButtonEnabled = isButtonEnabled('invite', state); - - // Hide conference info when interfaceConfig is available and the invite button is disabled. - // In all other cases we want to preserve the behaviour and control the the conference info - // visibility through showJoinActions. - const showConferenceInfo - = typeof isInviteButtonEnabled === 'undefined' || isInviteButtonEnabled === true - ? showJoinActions - : false; return { buttonIsToggled: isPrejoinSkipped(state), @@ -519,9 +440,7 @@ function mapStateToProps(state, ownProps): Object { showErrorOnJoin, hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state), showCameraPreview: !isVideoMutedByUser(state), - showConferenceInfo, - videoTrack: getLocalJitsiVideoTrack(state), - visibleButtons: getToolbarButtons(state) + videoTrack: getLocalJitsiVideoTrack(state) }; } diff --git a/react/features/prejoin/components/PrejoinApp.js b/react/features/prejoin/components/PrejoinApp.js index 579a77199..afdb94573 100644 --- a/react/features/prejoin/components/PrejoinApp.js +++ b/react/features/prejoin/components/PrejoinApp.js @@ -9,27 +9,18 @@ import { getConferenceOptions } from '../../base/conference/functions'; import { setConfig } from '../../base/config'; import { DialogContainer } from '../../base/dialog'; import { createPrejoinTracks } from '../../base/tracks'; +import JitsiThemeProvider from '../../base/ui/components/JitsiThemeProvider'; import { initPrejoin, makePrecallTest } from '../actions'; -import Prejoin from './Prejoin'; +import PrejoinThirdParty from './PrejoinThirdParty'; type Props = { /** - * Indicates whether the avatar should be shown when video is off + * Indicates the style type that needs to be applied. */ - showAvatar: boolean, - - /** - * Flag signaling the visibility of join label, input and buttons - */ - showJoinActions: boolean, - - /** - * Flag signaling the visibility of the skip prejoin toggle - */ - showSkipPrejoin: boolean, -}; + styleType: string +} /** * Wrapper application for prejoin. @@ -50,14 +41,12 @@ export default class PrejoinApp extends BaseApp { this._init.then(async () => { const { store } = this.state; const { dispatch } = store; - const { showAvatar, showJoinActions, showSkipPrejoin } = this.props; + const { styleType } = this.props; super._navigate({ - component: Prejoin, + component: PrejoinThirdParty, props: { - showAvatar, - showJoinActions, - showSkipPrejoin + className: styleType } }); @@ -88,9 +77,11 @@ export default class PrejoinApp extends BaseApp { */ _createMainElement(component, props) { return ( - - { super._createMainElement(component, props) } - + + + { super._createMainElement(component, props) } + + ); } @@ -101,9 +92,11 @@ export default class PrejoinApp extends BaseApp { */ _renderDialogContainer() { return ( - - - + + + + + ); } } diff --git a/react/features/prejoin/components/PrejoinThirdParty.js b/react/features/prejoin/components/PrejoinThirdParty.js new file mode 100644 index 000000000..9e64a506b --- /dev/null +++ b/react/features/prejoin/components/PrejoinThirdParty.js @@ -0,0 +1,87 @@ +// @flow + +import React, { Component } from 'react'; + +import { translate } from '../../base/i18n'; +import { isVideoMutedByUser } from '../../base/media'; +import { PreMeetingScreen } from '../../base/premeeting'; +import { connect } from '../../base/redux'; +import { getLocalJitsiVideoTrack } from '../../base/tracks'; +import { isDeviceStatusVisible } from '../functions'; + +type Props = { + + /** + * Indicates the className that needs to be applied. + */ + className: string, + + /** + * Flag signaling if the device status is visible or not. + */ + deviceStatusVisible: boolean, + + /** + * Flag signaling the visibility of camera preview. + */ + showCameraPreview: boolean, + + /** + * Used for translation. + */ + t: Function, + + /** + * The JitsiLocalTrack to display. + */ + videoTrack: ?Object +}; + +const buttons = [ 'microphone', 'camera', 'select-background' ]; + +/** + * This component is displayed before joining a meeting. + */ +class PrejoinThirdParty extends Component { + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {ReactElement} + */ + render() { + const { + className, + deviceStatusVisible, + showCameraPreview, + videoTrack + } = this.props; + + return ( + + ); + } +} + +/** + * Maps (parts of) the redux state to the React {@code Component} props. + * + * @param {Object} state - The redux state. + * @param {Object} ownProps - The props passed to the component. + * @returns {Object} + */ +function mapStateToProps(state): Object { + return { + deviceStatusVisible: isDeviceStatusVisible(state), + showCameraPreview: !isVideoMutedByUser(state), + videoTrack: getLocalJitsiVideoTrack(state) + }; +} + +export default connect(mapStateToProps)(translate(PrejoinThirdParty)); diff --git a/react/features/prejoin/components/preview/DeviceStatus.js b/react/features/prejoin/components/preview/DeviceStatus.js index 12f5a0c95..522db73b4 100644 --- a/react/features/prejoin/components/preview/DeviceStatus.js +++ b/react/features/prejoin/components/preview/DeviceStatus.js @@ -3,7 +3,7 @@ import React from 'react'; import { translate } from '../../../base/i18n'; -import { Icon, IconCheck, IconExclamation } from '../../../base/icons'; +import { Icon, IconCheckSolid, IconExclamation } from '../../../base/icons'; import { connect } from '../../../base/redux'; import { getDeviceStatusType, @@ -38,11 +38,11 @@ export type Props = { const iconMap = { warning: { src: IconExclamation, - className: 'prejoin-preview-status--warning' + className: 'device-icon--warning' }, ok: { - src: IconCheck, - className: 'prejoin-preview-status--ok' + src: IconCheckSolid, + className: 'device-icon--ok' } }; @@ -57,15 +57,14 @@ function DeviceStatus({ deviceStatusType, deviceStatusText, rawError, t }: Props return (
{t(deviceStatusText)} diff --git a/react/features/prejoin/constants.js b/react/features/prejoin/constants.js new file mode 100644 index 000000000..4200dbdec --- /dev/null +++ b/react/features/prejoin/constants.js @@ -0,0 +1,19 @@ +// @flow + +export type PREJOIN_SCREEN_STATE = "hidden" | "loading" | true; + + +type PREJOIN_SCREEN_STATE_TYPE = { + HIDDEN: PREJOIN_SCREEN_STATE, + LOADING: PREJOIN_SCREEN_STATE, + VISIBLE: PREJOIN_SCREEN_STATE + } + +/** + * Enum of possible prejoin screen states. + */ +export const PREJOIN_SCREEN_STATES: PREJOIN_SCREEN_STATE_TYPE = { + HIDDEN: 'hidden', + LOADING: 'loading', + VISIBLE: true // backwards compatibility with old boolean implementation +}; diff --git a/react/features/prejoin/functions.js b/react/features/prejoin/functions.js index 511429e4c..f6da9dbd3 100644 --- a/react/features/prejoin/functions.js +++ b/react/features/prejoin/functions.js @@ -4,6 +4,8 @@ import { getRoomName } from '../base/conference'; import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions'; import { isAudioMuted, isVideoMutedByUser } from '../base/media'; +import { PREJOIN_SCREEN_STATES } from './constants'; + /** * Selector for the visibility of the 'join by phone' button. * @@ -160,7 +162,17 @@ export function isPrejoinPageEnabled(state: Object): boolean { * @returns {boolean} */ export function isPrejoinPageVisible(state: Object): boolean { - return isPrejoinPageEnabled(state) && state['features/prejoin']?.showPrejoin; + return isPrejoinPageEnabled(state) && state['features/prejoin']?.showPrejoin === PREJOIN_SCREEN_STATES.VISIBLE; +} + +/** + * Returns true if the prejoin page is loading. + * + * @param {Object} state - The state of the app. + * @returns {boolean} + */ +export function isPrejoinPageLoading(state: Object): boolean { + return isPrejoinPageEnabled(state) && state['features/prejoin']?.showPrejoin === PREJOIN_SCREEN_STATES.LOADING; } /** diff --git a/react/features/prejoin/middleware.js b/react/features/prejoin/middleware.js index 0feadb4eb..f6dbc6621 100644 --- a/react/features/prejoin/middleware.js +++ b/react/features/prejoin/middleware.js @@ -1,5 +1,6 @@ // @flow +import { CONFERENCE_JOINED } from '../base/conference'; import { updateConfig } from '../base/config'; import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media'; import { MiddlewareRegistry } from '../base/redux'; @@ -17,6 +18,7 @@ import { setDeviceStatusWarning, setPrejoinPageVisibility } from './actions'; +import { PREJOIN_SCREEN_STATES } from './constants'; import { isPrejoinPageVisible } from './functions'; declare var APP: Object; @@ -56,7 +58,8 @@ MiddlewareRegistry.register(store => next => async action => { const jitsiTracks = localTracks.map(t => t.jitsiTrack); - dispatch(setPrejoinPageVisibility(false)); + dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING)); + APP.conference.prejoinStart(jitsiTracks); break; @@ -103,8 +106,23 @@ MiddlewareRegistry.register(store => next => async action => { } break; } - + case CONFERENCE_JOINED: + return _conferenceJoined(store, next, action); } return next(action); }); + +/** + * Handles cleanup of prejoin state when a conference is joined. + * + * @param {Object} store - The Redux store. + * @param {Function} next - The Redux next function. + * @param {Object} action - The Redux action. + * @returns {Object} + */ +function _conferenceJoined({ dispatch }, next, action) { + dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN)); + + return next(action); +} diff --git a/react/features/settings/actions.js b/react/features/settings/actions.js index c793cf25b..cf0c9e3d8 100644 --- a/react/features/settings/actions.js +++ b/react/features/settings/actions.js @@ -5,6 +5,7 @@ import { openDialog } from '../base/dialog'; import { i18next } from '../base/i18n'; import { updateSettings } from '../base/settings'; import { setPrejoinPageVisibility } from '../prejoin/actions'; +import { PREJOIN_SCREEN_STATES } from '../prejoin/constants'; import { setScreenshareFramerate } from '../screen-share/actions'; import { @@ -84,7 +85,7 @@ export function submitMoreTab(newState: Object): Function { // The 'showPrejoin' flag starts as 'true' on every new session. // This prevents displaying the prejoin page when the user re-enables it. if (showPrejoinPage && getState()['features/prejoin']?.showPrejoin) { - dispatch(setPrejoinPageVisibility(false)); + dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN)); } dispatch(updateSettings({ userSelectedSkipPrejoin: !showPrejoinPage diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js index 36b1602ac..bb1f69217 100644 --- a/react/features/toolbox/components/web/Toolbox.js +++ b/react/features/toolbox/components/web/Toolbox.js @@ -28,6 +28,7 @@ import { DominantSpeakerName } from '../../../display-name'; import { EmbedMeetingButton } from '../../../embed-meeting'; import { SharedDocumentButton } from '../../../etherpad'; import { FeedbackButton } from '../../../feedback'; +import { InviteButton } from '../../../invite/components/add-people-dialog'; import { isVpaasMeeting } from '../../../jaas/functions'; import { KeyboardShortcutsButton } from '../../../keyboard-shortcuts'; import { LocalRecordingButton } from '../../../local-recording'; @@ -66,7 +67,6 @@ import { VideoQualityDialog, VideoQualityButton } from '../../../video-quality/c import { VideoBackgroundButton } from '../../../virtual-background'; import { toggleBackgroundEffect } from '../../../virtual-background/actions'; import { VIRTUAL_BACKGROUND_TYPE } from '../../../virtual-background/constants'; -import { checkBlurSupport } from '../../../virtual-background/functions'; import { setFullScreen, setOverflowMenuVisible, @@ -207,11 +207,6 @@ type Props = { */ _visible: boolean, - /** - * Array with the buttons which this Toolbox should display. - */ - _visibleButtons: Array, - /** * Returns the selected virtual source object. */ @@ -230,7 +225,12 @@ type Props = { /** * Invoked to obtain translated strings. */ - t: Function + t: Function, + + /** + * Explicitly passed array with the buttons which this Toolbox should display. + */ + toolbarButtons: Array, }; declare var APP: Object; @@ -399,9 +399,9 @@ class Toolbox extends Component { * @returns {ReactElement} */ render() { - const { _chatOpen, _visible, _visibleButtons } = this.props; + const { _chatOpen, _visible, _toolbarButtons } = this.props; const rootClassNames = `new-toolbox ${_visible ? 'visible' : ''} ${ - _visibleButtons.length ? '' : 'no-buttons'} ${_chatOpen ? 'shift-right' : ''}`; + _toolbarButtons.length ? '' : 'no-buttons'} ${_chatOpen ? 'shift-right' : ''}`; return (
{ const participants = { key: 'participants-pane', - alias: 'invite', Content: ParticipantsPaneButton, handleClick: this._onToolbarToggleParticipantsPane, group: 2 }; + const invite = { + key: 'invite', + Content: InviteButton, + group: 2 + }; + const tileview = { key: 'tileview', Content: TileViewButton, @@ -691,7 +696,7 @@ class Toolbox extends Component { group: 3 }; - const virtualBackground = !_screenSharing && checkBlurSupport() && { + const virtualBackground = !_screenSharing && { key: 'select-background', Content: VideoBackgroundButton, group: 3 @@ -747,6 +752,7 @@ class Toolbox extends Component { chat, raisehand, participants, + invite, tileview, toggleCamera, videoQuality, @@ -1238,10 +1244,11 @@ class Toolbox extends Component { * props. * * @param {Object} state - The redux store/state. + * @param {Object} ownProps - The props explicitly passed. * @private * @returns {{}} */ -function _mapStateToProps(state) { +function _mapStateToProps(state, ownProps) { const { conference } = state['features/base/conference']; let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled(); const { @@ -1268,6 +1275,15 @@ function _mapStateToProps(state) { } } + let { toolbarButtons } = ownProps; + const stateToolbarButtons = getToolbarButtons(state); + + if (toolbarButtons) { + toolbarButtons = toolbarButtons.filter(name => isToolbarButtonEnabled(name, stateToolbarButtons)); + } else { + toolbarButtons = stateToolbarButtons; + } + return { _chatOpen: state['features/chat'].isOpen, _clientWidth: clientWidth, @@ -1289,9 +1305,8 @@ function _mapStateToProps(state) { _participantsPaneOpen: getParticipantsPaneOpen(state), _raisedHand: localParticipant?.raisedHand, _screenSharing: isScreenVideoShared(state), - _toolbarButtons: getToolbarButtons(state), + _toolbarButtons: toolbarButtons, _visible: isToolboxVisible(state), - _visibleButtons: getToolbarButtons(state), _reactionsEnabled: enableReactions }; } diff --git a/react/features/virtual-background/components/VideoBackgroundButton.js b/react/features/virtual-background/components/VideoBackgroundButton.js index 3afb24917..42d474ca2 100644 --- a/react/features/virtual-background/components/VideoBackgroundButton.js +++ b/react/features/virtual-background/components/VideoBackgroundButton.js @@ -6,6 +6,7 @@ import { IconVirtualBackground } from '../../base/icons'; import { connect } from '../../base/redux'; import { AbstractButton } from '../../base/toolbox/components'; import type { AbstractButtonProps } from '../../base/toolbox/components'; +import { checkBlurSupport } from '../functions'; import { VirtualBackgroundDialog } from './index'; @@ -72,7 +73,8 @@ class VideoBackgroundButton extends AbstractButton { function _mapStateToProps(state): Object { return { - _isBackgroundEnabled: Boolean(state['features/virtual-background'].backgroundEffectEnabled) + _isBackgroundEnabled: Boolean(state['features/virtual-background'].backgroundEffectEnabled), + visible: checkBlurSupport() }; } diff --git a/static/prejoin.html b/static/prejoin.html index 8ce110a52..e0156b2be 100644 --- a/static/prejoin.html +++ b/static/prejoin.html @@ -13,16 +13,12 @@ const url = new URL(window.location.href); const params = new URLSearchParams(url.search); - const showAvatar = params.get('showAvatar') === 'true'; - const showJoinActions = params.get('showJoinActions') === 'true'; - const showSkipPrejoin = params.get('showSkipPrejoin') === 'true'; + const styleType = params.get('styleType'); JitsiMeetJS.app.renderEntryPoint({ Component: JitsiMeetJS.app.entryPoints.PREJOIN, props: { - showAvatar, - showJoinActions, - showSkipPrejoin + styleType } }) })