fix(prejoin) implement ux improvements for mobile (#9939)
This commit is contained in:
parent
32ed2bccec
commit
adbb5f8ead
|
@ -11,7 +11,7 @@
|
|||
background-color: rgba(0, 0, 0, 0.7);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 8px 12px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
.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;
|
||||
|
||||
&-error {
|
||||
align-items: flex-start;
|
||||
background-color: #F8AE1A;
|
||||
border-radius: 6px;
|
||||
color: #040404;
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
|
@ -18,14 +27,8 @@
|
|||
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;
|
||||
|
|
|
@ -3,21 +3,16 @@
|
|||
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;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-top: -8px;
|
||||
font-size: 12px;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
padding: 7px 16px;
|
||||
|
@ -128,10 +129,22 @@
|
|||
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
transition: none;
|
||||
|
||||
.toolbox-content {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
|
@ -163,17 +176,26 @@
|
|||
padding: 16px;
|
||||
width: 100%;
|
||||
|
||||
&-controls {
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
letter-spacing: -0.012;
|
||||
margin-bottom: 24px;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 16px;
|
||||
width: calc(100% - 32px);
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.device-status-error {
|
||||
border-radius: 0;
|
||||
margin: 0 -16px;
|
||||
}
|
||||
|
||||
input.field {
|
||||
|
@ -183,15 +205,9 @@
|
|||
|
||||
.action-btn {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
padding: 11px 16px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
|
@ -227,68 +243,3 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@mixin icon-container($bg, $fill) {
|
||||
.toggle-button-icon-container {
|
||||
background: $bg;
|
||||
|
||||
svg {
|
||||
fill: $fill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
margin: 0 auto;
|
||||
transition: background 0.16s ease-out;
|
||||
|
||||
@include flex-centered();
|
||||
|
||||
svg {
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
|
||||
.toggle-button-icon-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: relative;
|
||||
|
||||
@include flex-centered();
|
||||
}
|
||||
|
||||
&-icon-container {
|
||||
border-radius: 50%;
|
||||
left: -22px;
|
||||
padding: 2px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&--toggled {
|
||||
@include icon-container(white, #1C2025);
|
||||
|
||||
&:hover {
|
||||
.toggle-button-icon-container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-button-icon-container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -696,6 +696,7 @@
|
|||
"errorDialOutFailed": "Could not dial out. Call failed",
|
||||
"errorDialOutStatus": "Error getting dial out status",
|
||||
"errorMissingName": "Please enter your name to join the meeting",
|
||||
"errorNoPermissions": "You need to enable microphone and camera access",
|
||||
"errorStatusCode": "Error dialing out, status code: {{status}}",
|
||||
"errorValidation": "Number validation failed",
|
||||
"iWantToDialIn": "I want to dial in",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.2332 14.3254L9.70838 0.910718C9.63451 0.77902 9.52397 0.670633 9.38965 0.598199C8.99846 0.387243 8.50693 0.52716 8.29177 0.910713L0.766813 14.3254C0.701168 14.4424 0.666748 14.5738 0.666748 14.7073C0.666748 15.1451 1.02867 15.4999 1.47512 15.4999H16.5249C16.6611 15.4999 16.7951 15.4662 16.9145 15.4018C17.3057 15.1909 17.4484 14.7089 17.2332 14.3254ZM2.84224 13.9147L9.00002 2.93733L15.1577 13.9147H2.84224ZM8.19177 11.5371C8.19177 11.0993 8.54663 10.7445 8.98437 10.7445H9.01591C9.45365 10.7445 9.80851 11.0993 9.80851 11.5371C9.80851 11.9748 9.45365 12.3297 9.01591 12.3297H8.98437C8.54663 12.3297 8.19177 11.9748 8.19177 11.5371ZM9.00014 6.7815C8.55369 6.7815 8.19177 7.14341 8.19177 7.58986V9.14351C8.19177 9.58996 8.55369 9.95188 9.00014 9.95188C9.44659 9.95188 9.80851 9.58996 9.80851 9.14351V7.58986C9.80851 7.14341 9.44659 6.7815 9.00014 6.7815Z" fill="#040404"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
|
@ -51,6 +51,7 @@ export { default as IconEmail } from './envelope.svg';
|
|||
export { default as IconEventNote } from './event_note.svg';
|
||||
export { default as IconExclamation } from './exclamation.svg';
|
||||
export { default as IconExclamationSolid } from './exclamation-solid.svg';
|
||||
export { default as IconExclamationTriangle } from './exclamation-triangle.svg';
|
||||
export { default as IconExitFullScreen } from './exit-full-screen.svg';
|
||||
export { default as IconFeedback } from './feedback.svg';
|
||||
export { default as IconFullScreen } from './full-screen.svg';
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { Icon, IconCheck } from '../../../icons';
|
||||
|
||||
const mainClass = 'toggle-button';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Text of the button.
|
||||
*/
|
||||
children: React$Node,
|
||||
|
||||
/**
|
||||
* If the button is toggled or not.
|
||||
*/
|
||||
isToggled?: boolean,
|
||||
|
||||
/**
|
||||
* OnClick button handler.
|
||||
*/
|
||||
onClick: Function
|
||||
}
|
||||
|
||||
/**
|
||||
* Button used as a toggle.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function ToggleButton({ children, isToggled, onClick }: Props) {
|
||||
const className = isToggled ? `${mainClass} ${mainClass}--toggled` : mainClass;
|
||||
|
||||
const onKeyPressHandler = useCallback(e => {
|
||||
if (onClick && (e.key === ' ')) {
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}
|
||||
}, [ onClick ]);
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-checked = { isToggled }
|
||||
className = { className }
|
||||
id = 'toggle-button'
|
||||
onClick = { onClick }
|
||||
onKeyPress = { onKeyPressHandler }
|
||||
role = 'switch'
|
||||
tabIndex = { 0 }>
|
||||
<div className = 'toggle-button-container'>
|
||||
<div className = 'toggle-button-icon-container'>
|
||||
<Icon
|
||||
className = 'toggle-button-icon'
|
||||
size = { 10 }
|
||||
src = { IconCheck } />
|
||||
</div>
|
||||
<label htmlFor = 'toggle-button'>{children}</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToggleButton;
|
|
@ -3,4 +3,3 @@
|
|||
export { default as ActionButton } from './ActionButton';
|
||||
export { default as InputField } from './InputField';
|
||||
export { default as PreMeetingScreen } from './PreMeetingScreen';
|
||||
export { default as ToggleButton } from './ToggleButton';
|
||||
|
|
|
@ -7,33 +7,26 @@ import { getRoomName } from '../../base/conference';
|
|||
import { translate } from '../../base/i18n';
|
||||
import { Icon, IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons';
|
||||
import { isVideoMutedByUser } from '../../base/media';
|
||||
import { ActionButton, InputField, PreMeetingScreen, ToggleButton } from '../../base/premeeting';
|
||||
import { ActionButton, InputField, PreMeetingScreen } from '../../base/premeeting';
|
||||
import { connect } from '../../base/redux';
|
||||
import { getDisplayName, updateSettings } from '../../base/settings';
|
||||
import { getLocalJitsiVideoTrack } from '../../base/tracks';
|
||||
import {
|
||||
joinConference as joinConferenceAction,
|
||||
joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
|
||||
setSkipPrejoin as setSkipPrejoinAction,
|
||||
setJoinByPhoneDialogVisiblity as setJoinByPhoneDialogVisiblityAction
|
||||
} from '../actions';
|
||||
import {
|
||||
isDeviceStatusVisible,
|
||||
isDisplayNameRequired,
|
||||
isJoinByPhoneButtonVisible,
|
||||
isJoinByPhoneDialogVisible,
|
||||
isPrejoinSkipped
|
||||
isJoinByPhoneDialogVisible
|
||||
} from '../functions';
|
||||
|
||||
import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Flag signaling if the 'skip prejoin' button is toggled or not.
|
||||
*/
|
||||
buttonIsToggled: boolean,
|
||||
|
||||
/**
|
||||
* Flag signaling if the device status is visible or not.
|
||||
*/
|
||||
|
@ -69,11 +62,6 @@ type Props = {
|
|||
*/
|
||||
roomName: string,
|
||||
|
||||
/**
|
||||
* Sets visibility of the prejoin page for the next sessions.
|
||||
*/
|
||||
setSkipPrejoin: Function,
|
||||
|
||||
/**
|
||||
* Sets visibility of the 'JoinByPhoneDialog'.
|
||||
*/
|
||||
|
@ -138,7 +126,6 @@ class Prejoin extends Component<Props, State> {
|
|||
this._closeDialog = this._closeDialog.bind(this);
|
||||
this._showDialog = this._showDialog.bind(this);
|
||||
this._onJoinButtonClick = this._onJoinButtonClick.bind(this);
|
||||
this._onToggleButtonClick = this._onToggleButtonClick.bind(this);
|
||||
this._onDropdownClose = this._onDropdownClose.bind(this);
|
||||
this._onOptionsClick = this._onOptionsClick.bind(this);
|
||||
this._setName = this._setName.bind(this);
|
||||
|
@ -183,18 +170,6 @@ class Prejoin extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
_onToggleButtonClick: () => void;
|
||||
|
||||
/**
|
||||
* Handler for the toggle button.
|
||||
*
|
||||
* @param {Object} e - The synthetic event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleButtonClick() {
|
||||
this.props.setSkipPrejoin(!this.props.buttonIsToggled);
|
||||
}
|
||||
|
||||
_onDropdownClose: () => void;
|
||||
|
||||
/**
|
||||
|
@ -321,7 +296,6 @@ class Prejoin extends Component<Props, State> {
|
|||
return (
|
||||
<PreMeetingScreen
|
||||
showDeviceStatus = { deviceStatusVisible }
|
||||
skipPrejoinButton = { this._renderSkipPrejoinButton() }
|
||||
title = { t('prejoin.joinMeeting') }
|
||||
videoMuted = { !showCameraPreview }
|
||||
videoTrack = { videoTrack }>
|
||||
|
@ -400,25 +374,6 @@ class Prejoin extends Component<Props, State> {
|
|||
</PreMeetingScreen>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the 'skip prejoin' button.
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderSkipPrejoinButton() {
|
||||
const { buttonIsToggled, t } = this.props;
|
||||
|
||||
return (
|
||||
<div className = 'prejoin-checkbox-container'>
|
||||
<ToggleButton
|
||||
isToggled = { buttonIsToggled }
|
||||
onClick = { this._onToggleButtonClick }>
|
||||
{t('prejoin.doNotShow')}
|
||||
</ToggleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -432,7 +387,6 @@ function mapStateToProps(state): Object {
|
|||
const showErrorOnJoin = isDisplayNameRequired(state) && !name;
|
||||
|
||||
return {
|
||||
buttonIsToggled: isPrejoinSkipped(state),
|
||||
name,
|
||||
deviceStatusVisible: isDeviceStatusVisible(state),
|
||||
roomName: getRoomName(state),
|
||||
|
@ -448,7 +402,6 @@ const mapDispatchToProps = {
|
|||
joinConferenceWithoutAudio: joinConferenceWithoutAudioAction,
|
||||
joinConference: joinConferenceAction,
|
||||
setJoinByPhoneDialogVisiblity: setJoinByPhoneDialogVisiblityAction,
|
||||
setSkipPrejoin: setSkipPrejoinAction,
|
||||
updateSettings
|
||||
};
|
||||
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconCheckSolid, IconExclamation } from '../../../base/icons';
|
||||
import { Icon, IconCheckSolid, IconExclamationTriangle } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import {
|
||||
getDeviceStatusType,
|
||||
getDeviceStatusText,
|
||||
getRawError
|
||||
getDeviceStatusText
|
||||
} from '../../functions';
|
||||
|
||||
export type Props = {
|
||||
|
@ -24,11 +23,6 @@ export type Props = {
|
|||
*/
|
||||
deviceStatusType: string,
|
||||
|
||||
/**
|
||||
* The error coming from device configuration.
|
||||
*/
|
||||
rawError: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
|
@ -37,7 +31,7 @@ export type Props = {
|
|||
|
||||
const iconMap = {
|
||||
warning: {
|
||||
src: IconExclamation,
|
||||
src: IconExclamationTriangle,
|
||||
className: 'device-icon--warning'
|
||||
},
|
||||
ok: {
|
||||
|
@ -52,25 +46,23 @@ const iconMap = {
|
|||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function DeviceStatus({ deviceStatusType, deviceStatusText, rawError, t }: Props) {
|
||||
function DeviceStatus({ deviceStatusType, deviceStatusText, t }: Props) {
|
||||
const { src, className } = iconMap[deviceStatusType];
|
||||
const hasError = deviceStatusType === 'warning';
|
||||
const containerClassName = `device-status ${hasError ? 'device-status-error' : ''}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = 'device-status'
|
||||
className = { containerClassName }
|
||||
role = 'alert'
|
||||
tabIndex = { -1 }>
|
||||
<Icon
|
||||
className = { `device-icon ${className}` }
|
||||
size = { 16 }
|
||||
src = { src } />
|
||||
<span
|
||||
role = 'heading'>
|
||||
{t(deviceStatusText)}
|
||||
<span role = 'heading'>
|
||||
{hasError ? t('prejoin.errorNoPermissions') : t(deviceStatusText)}
|
||||
</span>
|
||||
{ rawError && <span>
|
||||
{ rawError }
|
||||
</span> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -84,8 +76,7 @@ function DeviceStatus({ deviceStatusType, deviceStatusText, rawError, t }: Props
|
|||
function mapStateToProps(state) {
|
||||
return {
|
||||
deviceStatusText: getDeviceStatusText(state),
|
||||
deviceStatusType: getDeviceStatusType(state),
|
||||
rawError: getRawError(state)
|
||||
deviceStatusType: getDeviceStatusType(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue