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
This commit is contained in:
parent
49a73ac446
commit
1ad9046a38
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -334,7 +334,7 @@
|
|||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 6px 0;
|
||||
padding: 8px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -264,3 +264,9 @@ $chromeExtensionBannerRightInMeeeting: 10px;
|
|||
*/
|
||||
$smallScreen: 700px;
|
||||
$verySmallScreen: 500px;
|
||||
|
||||
/**
|
||||
* Prejoin / premeeting screen
|
||||
*/
|
||||
|
||||
$prejoinDefaultContentWidth: 336px;
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
@import 'connection-status';
|
||||
@import 'device-status';
|
||||
@import 'lobby';
|
||||
@import 'premeeting-screens';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'prejoin-third-party';
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
|
@ -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.",
|
||||
|
|
|
@ -179,7 +179,7 @@
|
|||
"e2eeLabel": "Ŝlosilo",
|
||||
"e2eeTitle": "Tutvoja ĉifrado",
|
||||
"e2eeWarning": "<br /><p><strong>ATENTIGO:</strong> 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.</p>",
|
||||
"enterDisplayName": "Please enter your name here",
|
||||
"enterDisplayName": "Enter your name here",
|
||||
"error": "Eraro",
|
||||
"externalInstallationMsg": "Vi devas instali nian ekranvidadan kromprogramon.",
|
||||
"externalInstallationTitle": "Kromprogramo bezonata",
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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ë.",
|
||||
|
|
|
@ -209,7 +209,6 @@
|
|||
"e2eeLabel": "啟用端對端加密",
|
||||
"e2eeWarning": "警告:看來不是每位此會議的參與者都有啟用端對端加密,如果您啟用了,他們可能無法看/聽到您。",
|
||||
"enterDisplayName": "請在此輸入您自己的名字",
|
||||
"enterDisplayNameToJoin": "請輸入您的名字以加入",
|
||||
"embedMeeting": "嵌入會議",
|
||||
"error": "錯誤",
|
||||
"gracefulShutdown": "我們的服務目前關閉維護中,請稍後再試。",
|
||||
|
|
|
@ -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...",
|
||||
|
|
|
@ -43,9 +43,11 @@ export class App extends AbstractApp {
|
|||
*/
|
||||
_renderDialogContainer() {
|
||||
return (
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666687 9.00002C0.666687 13.6024 4.39765 17.3334 9.00002 17.3334C13.6024 17.3334 17.3334 13.6024 17.3334 9.00002C17.3334 4.39765 13.6024 0.666687 9.00002 0.666687C4.39765 0.666687 0.666687 4.39765 0.666687 9.00002ZM13.7119 5.86983C13.3639 5.56869 12.8376 5.60672 12.5365 5.95477L7.55616 11.711L5.42261 9.57743C5.09717 9.25199 4.56954 9.25199 4.2441 9.57743C3.91866 9.90287 3.91866 10.4305 4.2441 10.7559L7.01102 13.5229C7.35319 13.865 7.91386 13.8448 8.23047 13.4789L13.7969 7.04527C14.098 6.69722 14.06 6.17096 13.7119 5.86983Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 699 B |
|
@ -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';
|
||||
|
|
|
@ -79,37 +79,35 @@ function ConnectionStatus({ connectionDetails, t, connectionType }: Props) {
|
|||
|
||||
return (
|
||||
<div className = 'con-status'>
|
||||
<div className = 'con-status-container'>
|
||||
<div
|
||||
aria-level = { 1 }
|
||||
className = 'con-status-header'
|
||||
role = 'heading'>
|
||||
<div className = { `con-status-circle ${connectionClass}` }>
|
||||
<Icon
|
||||
size = { 16 }
|
||||
src = { icon } />
|
||||
</div>
|
||||
<span
|
||||
aria-hidden = { !showDetails }
|
||||
className = 'con-status-text'
|
||||
id = 'connection-status-description'>{t(connectionText)}</span>
|
||||
<div
|
||||
aria-level = { 1 }
|
||||
className = 'con-status-header'
|
||||
role = 'heading'>
|
||||
<div className = { `con-status-circle ${connectionClass}` }>
|
||||
<Icon
|
||||
ariaDescribedBy = 'connection-status-description'
|
||||
ariaPressed = { showDetails }
|
||||
className = { arrowClassName }
|
||||
onClick = { onToggleDetails }
|
||||
onKeyPress = { onKeyPressToggleDetails }
|
||||
role = 'button'
|
||||
size = { 24 }
|
||||
src = { IconArrowDownSmall }
|
||||
tabIndex = { 0 } />
|
||||
size = { 16 }
|
||||
src = { icon } />
|
||||
</div>
|
||||
<div
|
||||
aria-level = '2'
|
||||
className = { `con-status-details ${detailsClassName}` }
|
||||
role = 'heading'>
|
||||
{detailsText}</div>
|
||||
<span
|
||||
aria-hidden = { !showDetails }
|
||||
className = 'con-status-text'
|
||||
id = 'connection-status-description'>{t(connectionText)}</span>
|
||||
<Icon
|
||||
ariaDescribedBy = 'connection-status-description'
|
||||
ariaPressed = { showDetails }
|
||||
className = { arrowClassName }
|
||||
onClick = { onToggleDetails }
|
||||
onKeyPress = { onKeyPressToggleDetails }
|
||||
role = 'button'
|
||||
size = { 24 }
|
||||
src = { IconArrowDownSmall }
|
||||
tabIndex = { 0 } />
|
||||
</div>
|
||||
<div
|
||||
aria-level = '2'
|
||||
className = { `con-status-details ${detailsClassName}` }
|
||||
role = 'heading'>
|
||||
{detailsText}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<Props> {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<div className = 'copy-meeting'>
|
||||
<CopyMeetingLinkSection url = { this.props.url } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
|
@ -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<string>,
|
||||
|
||||
/**
|
||||
* 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<string>
|
||||
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<Props> {
|
|||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
showAvatar: true,
|
||||
showCopyUrlButton: true,
|
||||
showConferenceInfo: true
|
||||
showToolbox: true
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -93,57 +85,37 @@ export default class PreMeetingScreen extends PureComponent<Props> {
|
|||
*/
|
||||
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 (
|
||||
<div
|
||||
className = 'premeeting-screen'
|
||||
id = 'lobby-screen'>
|
||||
<ConnectionStatus />
|
||||
<div className = { containerClassName }>
|
||||
<div className = 'content'>
|
||||
<ConnectionStatus />
|
||||
|
||||
<div className = 'content-controls'>
|
||||
<h1 className = 'title'>
|
||||
{ title }
|
||||
</h1>
|
||||
{ children }
|
||||
<Toolbox toolbarButtons = { toolbarButtons || buttons } />
|
||||
{ skipPrejoinButton }
|
||||
{ showDeviceStatus && <DeviceStatus /> }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Preview
|
||||
videoMuted = { videoMuted }
|
||||
videoTrack = { videoTrack } />
|
||||
<div className = 'content'>
|
||||
{showAvatar && videoMuted && (
|
||||
<Avatar
|
||||
className = 'premeeting-screen-avatar'
|
||||
displayName = { name }
|
||||
dynamicColor = { false }
|
||||
participantId = 'local'
|
||||
size = { 80 } />
|
||||
)}
|
||||
{showConferenceInfo && (
|
||||
<>
|
||||
<h1 className = 'title'>
|
||||
{ title }
|
||||
</h1>
|
||||
{showSharingButton ? <CopyMeetingUrl /> : null}
|
||||
</>
|
||||
)}
|
||||
{ this.props.children }
|
||||
<div className = 'media-btn-container'>
|
||||
<div className = 'toolbox-content'>
|
||||
<div className = 'toolbox-content-items'>
|
||||
<AudioSettingsButton visible = { true } />
|
||||
<VideoSettingsButton visible = { true } />
|
||||
{ ((visibleButtons && visibleButtons.includes('select-background'))
|
||||
|| (visibleButtons && visibleButtons.includes('videobackgroundblur')))
|
||||
&& <VideoBackgroundButton visible = { checkBlurSupport() } /> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ this.props.skipPrejoinButton }
|
||||
{ this.props.footer }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<div id = 'preview'>
|
||||
<Video
|
||||
className = { className }
|
||||
videoTrack = {{ jitsiTrack: videoTrack }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return (
|
||||
<div id = 'preview'>
|
||||
{!videoMuted && videoTrack
|
||||
? (
|
||||
<Video
|
||||
className = { className }
|
||||
videoTrack = {{ jitsiTrack: videoTrack }} />
|
||||
)
|
||||
: (
|
||||
<Avatar
|
||||
className = 'premeeting-screen-avatar'
|
||||
displayName = { name }
|
||||
dynamicColor = { false }
|
||||
participantId = { _participantId }
|
||||
size = { 180 } />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<Props, *> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _fullscreenEnabled } = this.props;
|
||||
const { _fullscreenEnabled, _showLobby } = this.props;
|
||||
|
||||
if (_showLobby) {
|
||||
return <LobbyScreen />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container style = { styles.conference }>
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<Props, *> {
|
|||
*/
|
||||
render() {
|
||||
const {
|
||||
_isLobbyScreenVisible,
|
||||
_isParticipantsPaneVisible,
|
||||
_layoutClassName,
|
||||
_showLobby,
|
||||
_showPrejoin
|
||||
} = this.props;
|
||||
|
||||
|
@ -237,7 +238,7 @@ class Conference extends AbstractConference<Props, *> {
|
|||
<Filmstrip />
|
||||
</div>
|
||||
|
||||
{ _showPrejoin || _isLobbyScreenVisible || <Toolbox /> }
|
||||
{ _showPrejoin || _showLobby || <Toolbox /> }
|
||||
<Chat />
|
||||
|
||||
{ this.renderNotificationsContainer() }
|
||||
|
@ -245,7 +246,7 @@ class Conference extends AbstractConference<Props, *> {
|
|||
<CalleeInfoContainer />
|
||||
|
||||
{ _showPrejoin && <Prejoin />}
|
||||
|
||||
{ _showLobby && <LobbyScreen />}
|
||||
</div>
|
||||
<ParticipantsPane />
|
||||
</div>
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Props, *> {
|
||||
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));
|
|
@ -1,3 +1,4 @@
|
|||
// @flow
|
||||
|
||||
export { default as AddPeopleDialog } from './AddPeopleDialog';
|
||||
export { default as InviteButton } from './InviteButton';
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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<Props> {
|
|||
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,
|
||||
|
|
|
@ -27,15 +27,16 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
|
||||
return (
|
||||
<CustomDialog
|
||||
onCancel = { this._onCancel }
|
||||
style = { styles.contentWrapper }>
|
||||
<Text style = { styles.dialogTitle }>
|
||||
{ t(this._getScreenTitleKey()) }
|
||||
</Text>
|
||||
<Text style = { styles.secondaryText }>
|
||||
{ _meetingName }
|
||||
</Text>
|
||||
{ this._renderContent() }
|
||||
onCancel = { this._onCancel }>
|
||||
<View style = { styles.contentWrapper }>
|
||||
<Text style = { styles.dialogTitle }>
|
||||
{ t(this._getScreenTitleKey()) }
|
||||
</Text>
|
||||
<Text style = { styles.secondaryText }>
|
||||
{ _meetingName }
|
||||
</Text>
|
||||
{ this._renderContent() }
|
||||
</View>
|
||||
</CustomDialog>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { showCopyUrlButton, t } = this.props;
|
||||
const { _deviceStatusVisible, showCopyUrlButton, t } = this.props;
|
||||
|
||||
return (
|
||||
<PreMeetingScreen
|
||||
className = 'lobby-screen'
|
||||
showCopyUrlButton = { showCopyUrlButton }
|
||||
showDeviceStatus = { _deviceStatusVisible }
|
||||
title = { t(this._getScreenTitleKey()) }>
|
||||
{ this._renderContent() }
|
||||
</PreMeetingScreen>
|
||||
|
@ -62,7 +64,7 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
*/
|
||||
_renderJoining() {
|
||||
return (
|
||||
<div className = 'container'>
|
||||
<div className = 'lobby-screen-content'>
|
||||
<div className = 'spinner'>
|
||||
<LoadingIndicator size = 'large' />
|
||||
</div>
|
||||
|
@ -113,13 +115,19 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
const { _passwordJoinFailed, t } = this.props;
|
||||
|
||||
return (
|
||||
<InputField
|
||||
className = { _passwordJoinFailed ? 'error' : '' }
|
||||
onChange = { this._onChangePassword }
|
||||
placeHolder = { _passwordJoinFailed ? t('lobby.invalidPassword') : t('lobby.passwordField') }
|
||||
testId = 'lobby.password'
|
||||
type = 'password'
|
||||
value = { this.state.password } />
|
||||
<>
|
||||
<InputField
|
||||
className = { _passwordJoinFailed ? 'error' : '' }
|
||||
onChange = { this._onChangePassword }
|
||||
placeHolder = { t('lobby.passwordField') }
|
||||
testId = 'lobby.password'
|
||||
type = 'password'
|
||||
value = { this.state.password } />
|
||||
|
||||
{_passwordJoinFailed && <div
|
||||
className = 'prejoin-error'
|
||||
data-testid = 'lobby.errorMessage'>{t('lobby.invalidPassword')}</div>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,11 +142,10 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
return (
|
||||
<>
|
||||
<ActionButton
|
||||
disabled = { !this.state.password }
|
||||
onClick = { this._onJoinWithPassword }
|
||||
testId = 'lobby.passwordJoinButton'
|
||||
type = 'primary'>
|
||||
{ t('lobby.passwordJoinButton') }
|
||||
{ t('prejoin.joinMeeting') }
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
onClick = { this._onSwitchToKnockMode }
|
||||
|
|
|
@ -20,6 +20,16 @@ export function getKnockingParticipants(state: any) {
|
|||
return state['features/lobby'].knockingParticipants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector to return lobby visibility.
|
||||
*
|
||||
* @param {any} state - State object.
|
||||
* @returns {any}
|
||||
*/
|
||||
export function getIsLobbyVisible(state: any) {
|
||||
return state['features/lobby'].lobbyVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector to return array with knocking participant ids.
|
||||
*
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
KNOCKING_PARTICIPANT_LEFT,
|
||||
SET_KNOCKING_STATE,
|
||||
SET_LOBBY_MODE_ENABLED,
|
||||
SET_LOBBY_VISIBILITY,
|
||||
SET_PASSWORD_JOIN_FAILED
|
||||
} from './actionTypes';
|
||||
|
||||
|
@ -15,6 +16,7 @@ const DEFAULT_STATE = {
|
|||
knocking: false,
|
||||
knockingParticipants: [],
|
||||
lobbyEnabled: false,
|
||||
lobbyVisible: false,
|
||||
passwordJoinFailed: false
|
||||
};
|
||||
|
||||
|
@ -53,6 +55,11 @@ ReducerRegistry.register('features/lobby', (state = DEFAULT_STATE, action) => {
|
|||
...state,
|
||||
lobbyEnabled: action.enabled
|
||||
};
|
||||
case SET_LOBBY_VISIBILITY:
|
||||
return {
|
||||
...state,
|
||||
lobbyVisible: action.visible
|
||||
};
|
||||
case SET_PASSWORD:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<string>
|
||||
videoTrack: ?Object
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -152,17 +122,6 @@ type State = {
|
|||
* This component is displayed before joining a meeting.
|
||||
*/
|
||||
class Prejoin extends Component<Props, State> {
|
||||
/**
|
||||
* 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<Props, State> {
|
|||
*/
|
||||
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<Props, State> {
|
|||
|
||||
return (
|
||||
<PreMeetingScreen
|
||||
footer = { this._renderFooter() }
|
||||
name = { name }
|
||||
showAvatar = { showAvatar }
|
||||
showConferenceInfo = { showConferenceInfo }
|
||||
showDeviceStatus = { deviceStatusVisible }
|
||||
skipPrejoinButton = { this._renderSkipPrejoinButton() }
|
||||
title = { t('prejoin.joinMeeting') }
|
||||
videoMuted = { !showCameraPreview }
|
||||
videoTrack = { videoTrack }
|
||||
visibleButtons = { visibleButtons }>
|
||||
{showJoinActions && (
|
||||
<div className = 'prejoin-input-area-container'>
|
||||
<div className = 'prejoin-input-area'>
|
||||
<label
|
||||
className = 'prejoin-input-area-label'
|
||||
htmlFor = { 'Prejoin-input-field-id' } >
|
||||
{ t('dialog.enterDisplayNameToJoin') }</label>
|
||||
<InputField
|
||||
autoComplete = { 'name' }
|
||||
autoFocus = { true }
|
||||
className = { showError ? 'error' : '' }
|
||||
hasError = { showError }
|
||||
id = { 'Prejoin-input-field-id' }
|
||||
onChange = { _setName }
|
||||
onSubmit = { joinConference }
|
||||
placeHolder = { t('dialog.enterDisplayName') }
|
||||
value = { name } />
|
||||
videoTrack = { videoTrack }>
|
||||
<div
|
||||
className = 'prejoin-input-area'
|
||||
data-testid = 'prejoin.screen'>
|
||||
<InputField
|
||||
autoComplete = { 'name' }
|
||||
autoFocus = { true }
|
||||
className = { showError ? 'error' : '' }
|
||||
hasError = { showError }
|
||||
onChange = { _setName }
|
||||
onSubmit = { joinConference }
|
||||
placeHolder = { t('dialog.enterDisplayName') }
|
||||
value = { name } />
|
||||
|
||||
{showError && <div
|
||||
className = 'prejoin-error'
|
||||
data-testid = 'prejoin.errorMessage'>{t('prejoin.errorMissingName')}</div>}
|
||||
{showError && <div
|
||||
className = 'prejoin-error'
|
||||
data-testid = 'prejoin.errorMessage'>{t('prejoin.errorMissingName')}</div>}
|
||||
|
||||
<div className = 'prejoin-preview-dropdown-container'>
|
||||
<InlineDialog
|
||||
content = { <div className = 'prejoin-preview-dropdown-btns'>
|
||||
<div
|
||||
className = 'prejoin-preview-dropdown-btn'
|
||||
data-testid = 'prejoin.joinWithoutAudio'
|
||||
onClick = { joinConferenceWithoutAudio }
|
||||
onKeyPress = { _onJoinConferenceWithoutAudioKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
<Icon
|
||||
className = 'prejoin-preview-dropdown-icon'
|
||||
size = { 24 }
|
||||
src = { IconVolumeOff } />
|
||||
{ t('prejoin.joinWithoutAudio') }
|
||||
</div>
|
||||
{hasJoinByPhoneButton && <div
|
||||
className = 'prejoin-preview-dropdown-btn'
|
||||
onClick = { _showDialog }
|
||||
onKeyPress = { _showDialogKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
<Icon
|
||||
className = 'prejoin-preview-dropdown-icon'
|
||||
data-testid = 'prejoin.joinByPhone'
|
||||
size = { 24 }
|
||||
src = { IconPhone } />
|
||||
{ t('prejoin.joinAudioByPhone') }
|
||||
</div>}
|
||||
</div> }
|
||||
isOpen = { showJoinByPhoneButtons }
|
||||
onClose = { _onDropdownClose }>
|
||||
<ActionButton
|
||||
OptionsIcon = { showJoinByPhoneButtons ? IconArrowUp : IconArrowDown }
|
||||
ariaDropDownLabel = { t('prejoin.joinWithoutAudio') }
|
||||
ariaLabel = { t('prejoin.joinMeeting') }
|
||||
ariaPressed = { showJoinByPhoneButtons }
|
||||
hasOptions = { true }
|
||||
onClick = { _onJoinButtonClick }
|
||||
onKeyPress = { _onJoinKeyPress }
|
||||
onOptionsClick = { _onOptionsClick }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }
|
||||
testId = 'prejoin.joinMeeting'
|
||||
type = 'primary'>
|
||||
{ t('prejoin.joinMeeting') }
|
||||
</ActionButton>
|
||||
</InlineDialog>
|
||||
</div>
|
||||
</div>
|
||||
<div className = 'prejoin-preview-dropdown-container'>
|
||||
<InlineDialog
|
||||
content = { <div className = 'prejoin-preview-dropdown-btns'>
|
||||
<div
|
||||
className = 'prejoin-preview-dropdown-btn'
|
||||
data-testid = 'prejoin.joinWithoutAudio'
|
||||
onClick = { joinConferenceWithoutAudio }
|
||||
onKeyPress = { _onJoinConferenceWithoutAudioKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
<Icon
|
||||
className = 'prejoin-preview-dropdown-icon'
|
||||
size = { 24 }
|
||||
src = { IconVolumeOff } />
|
||||
{ t('prejoin.joinWithoutAudio') }
|
||||
</div>
|
||||
{hasJoinByPhoneButton && <div
|
||||
className = 'prejoin-preview-dropdown-btn'
|
||||
onClick = { _showDialog }
|
||||
onKeyPress = { _showDialogKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
<Icon
|
||||
className = 'prejoin-preview-dropdown-icon'
|
||||
data-testid = 'prejoin.joinByPhone'
|
||||
size = { 24 }
|
||||
src = { IconPhone } />
|
||||
{ t('prejoin.joinAudioByPhone') }
|
||||
</div>}
|
||||
</div> }
|
||||
isOpen = { showJoinByPhoneButtons }
|
||||
onClose = { _onDropdownClose }>
|
||||
<ActionButton
|
||||
OptionsIcon = { showJoinByPhoneButtons ? IconArrowUp : IconArrowDown }
|
||||
ariaDropDownLabel = { t('prejoin.joinWithoutAudio') }
|
||||
ariaLabel = { t('prejoin.joinMeeting') }
|
||||
ariaPressed = { showJoinByPhoneButtons }
|
||||
hasOptions = { true }
|
||||
onClick = { _onJoinButtonClick }
|
||||
onKeyPress = { _onJoinKeyPress }
|
||||
onOptionsClick = { _onOptionsClick }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }
|
||||
testId = 'prejoin.joinMeeting'
|
||||
type = 'primary'>
|
||||
{ t('prejoin.joinMeeting') }
|
||||
</ActionButton>
|
||||
</InlineDialog>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{ showDialog && (
|
||||
<JoinByPhoneDialog
|
||||
joinConferenceWithoutAudio = { joinConferenceWithoutAudio }
|
||||
|
@ -456,26 +401,13 @@ class Prejoin extends Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the screen footer if any.
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderFooter() {
|
||||
return this.props.deviceStatusVisible && <DeviceStatus />;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<div className = 'prejoin-checkbox-container'>
|
||||
|
@ -493,22 +425,11 @@ class Prejoin extends Component<Props, State> {
|
|||
* 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)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Props> {
|
|||
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<Props> {
|
|||
*/
|
||||
_createMainElement(component, props) {
|
||||
return (
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
{ super._createMainElement(component, props) }
|
||||
</AtlasKitThemeProvider>
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
{ super._createMainElement(component, props) }
|
||||
</AtlasKitThemeProvider>
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -101,9 +92,11 @@ export default class PrejoinApp extends BaseApp<Props> {
|
|||
*/
|
||||
_renderDialogContainer() {
|
||||
return (
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
deviceStatusVisible,
|
||||
showCameraPreview,
|
||||
videoTrack
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<PreMeetingScreen
|
||||
className = { `prejoin-third-party ${className}` }
|
||||
showDeviceStatus = { deviceStatusVisible }
|
||||
skipPrejoinButton = { false }
|
||||
toolbarButtons = { buttons }
|
||||
videoMuted = { !showCameraPreview }
|
||||
videoTrack = { videoTrack } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
|
@ -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 (
|
||||
<div
|
||||
className = { `prejoin-preview-status ${className}` }
|
||||
className = 'device-status'
|
||||
role = 'alert'
|
||||
tabIndex = { -1 }>
|
||||
<Icon
|
||||
className = 'prejoin-preview-icon'
|
||||
className = { `device-icon ${className}` }
|
||||
size = { 16 }
|
||||
src = { src } />
|
||||
<span
|
||||
className = 'prejoin-preview-error-desc'
|
||||
role = 'heading'>
|
||||
{t(deviceStatusText)}
|
||||
</span>
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<string>,
|
||||
|
||||
/**
|
||||
* 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<string>,
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
|
@ -399,9 +399,9 @@ class Toolbox extends Component<Props> {
|
|||
* @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 (
|
||||
<div
|
||||
|
@ -598,12 +598,17 @@ class Toolbox extends Component<Props> {
|
|||
|
||||
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<Props> {
|
|||
group: 3
|
||||
};
|
||||
|
||||
const virtualBackground = !_screenSharing && checkBlurSupport() && {
|
||||
const virtualBackground = !_screenSharing && {
|
||||
key: 'select-background',
|
||||
Content: VideoBackgroundButton,
|
||||
group: 3
|
||||
|
@ -747,6 +752,7 @@ class Toolbox extends Component<Props> {
|
|||
chat,
|
||||
raisehand,
|
||||
participants,
|
||||
invite,
|
||||
tileview,
|
||||
toggleCamera,
|
||||
videoQuality,
|
||||
|
@ -1238,10 +1244,11 @@ class Toolbox extends Component<Props> {
|
|||
* 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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<Props, *> {
|
|||
function _mapStateToProps(state): Object {
|
||||
|
||||
return {
|
||||
_isBackgroundEnabled: Boolean(state['features/virtual-background'].backgroundEffectEnabled)
|
||||
_isBackgroundEnabled: Boolean(state['features/virtual-background'].backgroundEffectEnabled),
|
||||
visible: checkBlurSupport()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue