feat(prejoin) Update design (#12844)
This commit is contained in:
parent
992bf47850
commit
0d5dae7ab9
|
@ -41,11 +41,11 @@
|
|||
&-dropdown-btns {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
|
||||
&-dropdown-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
|
||||
/**
|
||||
* Override default InlineDialog behaviour, since it does not play nicely with relative widths
|
||||
*/
|
||||
|
@ -56,5 +56,12 @@
|
|||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.prejoin-input {
|
||||
margin-bottom: 16px;
|
||||
|
||||
& input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
.premeeting-screen {
|
||||
background: #292929;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
font-size: 1.3em;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.premeeting-screen {
|
||||
.action-btn {
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
|
@ -75,129 +65,38 @@
|
|||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
margin: 0 30px;
|
||||
padding: 24px 0 16px;
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
position: relative;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
z-index: $toolbarZ + 2;
|
||||
transition: none;
|
||||
|
||||
&-controls {
|
||||
align-items: center;
|
||||
.toolbox-content {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015;
|
||||
line-height: 36px;
|
||||
margin-bottom: 16px;
|
||||
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;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white;
|
||||
}
|
||||
}
|
||||
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
position: relative;
|
||||
transition: none;
|
||||
|
||||
.toolbox-content {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
@include ltr;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// mobile phone landscape
|
||||
@media (max-height: 420px) {
|
||||
div.content {
|
||||
padding: 16px 16px 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.content {
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
|
||||
&-controls {
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.device-status-error {
|
||||
border-radius: 0;
|
||||
margin: 0 -16px;
|
||||
}
|
||||
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
|
|
|
@ -7,6 +7,7 @@ import { translate } from '../../../i18n/functions';
|
|||
import Icon from '../../../icons/components/Icon';
|
||||
import { IconArrowDown, IconWifi1Bar, IconWifi2Bars, IconWifi3Bars } from '../../../icons/svg';
|
||||
import { connect } from '../../../redux/functions';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
import { PREJOIN_DEFAULT_CONTENT_WIDTH } from '../../../ui/components/variables';
|
||||
import { CONNECTION_TYPE } from '../../constants';
|
||||
import { getConnectionData } from '../../functions';
|
||||
|
@ -27,11 +28,8 @@ interface IProps extends WithTranslation {
|
|||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
connectionStatus: {
|
||||
borderRadius: '6px',
|
||||
color: '#fff',
|
||||
fontSize: '12px',
|
||||
letterSpacing: '0.16px',
|
||||
lineHeight: '16px',
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
|
||||
|
@ -56,14 +54,15 @@ const useStyles = makeStyles()(theme => {
|
|||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
padding: '14px 16px'
|
||||
padding: '12px 16px',
|
||||
borderRadius: theme.shape.borderRadius
|
||||
},
|
||||
|
||||
'& .con-status-circle': {
|
||||
borderRadius: '50%',
|
||||
display: 'inline-block',
|
||||
padding: theme.spacing(1),
|
||||
marginRight: theme.spacing(3)
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
|
||||
'& .con-status--good': {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* eslint-disable lines-around-comment */
|
||||
import clsx from 'clsx';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
|
@ -9,12 +11,12 @@ import { Toolbox } from '../../../../toolbox/components/web';
|
|||
import { getConferenceName } from '../../../conference/functions';
|
||||
import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from '../../../config/constants';
|
||||
import { getToolbarButtons, isToolbarButtonEnabled } from '../../../config/functions.web';
|
||||
import { connect } from '../../../redux/functions';
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
|
||||
import ConnectionStatus from './ConnectionStatus';
|
||||
// @ts-ignore
|
||||
import Preview from './Preview';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
interface IProps {
|
||||
|
||||
|
@ -51,7 +53,7 @@ interface IProps {
|
|||
/**
|
||||
* Indicates whether the copy url button should be shown.
|
||||
*/
|
||||
showCopyUrlButton: boolean;
|
||||
showCopyUrlButton?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the device status should be shown.
|
||||
|
@ -86,7 +88,64 @@ interface IProps {
|
|||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
subtitle: {
|
||||
container: {
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
inset: '0 0 0 0',
|
||||
display: 'flex',
|
||||
backgroundColor: theme.palette.ui01,
|
||||
zIndex: 252,
|
||||
|
||||
'@media (max-width: 720px)': {
|
||||
flexDirection: 'column-reverse'
|
||||
}
|
||||
},
|
||||
content: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
flexShrink: 0,
|
||||
boxSizing: 'border-box',
|
||||
margin: '0 48px',
|
||||
padding: '24px 0 16px',
|
||||
position: 'relative',
|
||||
width: '300px',
|
||||
height: '100%',
|
||||
zIndex: 252,
|
||||
|
||||
'@media (max-width: 720px)': {
|
||||
height: 'auto',
|
||||
margin: '0 auto'
|
||||
},
|
||||
|
||||
// mobile phone landscape
|
||||
'@media (max-width: 420px)': {
|
||||
padding: '16px 16px 0 16px',
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
'@media (max-width: 400px)': {
|
||||
padding: '16px'
|
||||
}
|
||||
},
|
||||
contentControls: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
margin: 'auto',
|
||||
width: '100%'
|
||||
},
|
||||
title: {
|
||||
...withPixelLineHeight(theme.typography.heading4),
|
||||
color: `${theme.palette.text01}!important`,
|
||||
marginBottom: theme.spacing(3),
|
||||
textAlign: 'center',
|
||||
|
||||
'@media (max-width: 400px)': {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
roomName: {
|
||||
...withPixelLineHeight(theme.typography.heading5),
|
||||
color: theme.palette.text01,
|
||||
marginBottom: theme.spacing(4),
|
||||
|
@ -112,7 +171,6 @@ const PreMeetingScreen = ({
|
|||
videoTrack
|
||||
}: IProps) => {
|
||||
const { classes } = useStyles();
|
||||
const containerClassName = `premeeting-screen ${className ? className : ''}`;
|
||||
const style = _premeetingBackground ? {
|
||||
background: _premeetingBackground,
|
||||
backgroundPosition: 'center',
|
||||
|
@ -120,17 +178,17 @@ const PreMeetingScreen = ({
|
|||
} : {};
|
||||
|
||||
return (
|
||||
<div className = { containerClassName }>
|
||||
<div className = { clsx('premeeting-screen', classes.container, className) }>
|
||||
<div style = { style }>
|
||||
<div className = 'content'>
|
||||
<div className = { classes.content }>
|
||||
<ConnectionStatus />
|
||||
|
||||
<div className = 'content-controls'>
|
||||
<h1 className = 'title'>
|
||||
<div className = { classes.contentControls }>
|
||||
<h1 className = { classes.title }>
|
||||
{title}
|
||||
</h1>
|
||||
{_roomName && (
|
||||
<span className = { classes.subtitle }>
|
||||
<span className = { classes.roomName }>
|
||||
{_roomName}
|
||||
</span>
|
||||
)}
|
||||
|
@ -175,7 +233,7 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
|
|||
? premeetingButtons
|
||||
: premeetingButtons.filter(b => isToolbarButtonEnabled(b, toolbarButtons)),
|
||||
_premeetingBackground: premeetingBackground,
|
||||
_roomName: hideConferenceSubject ? undefined : getConferenceName(state)
|
||||
_roomName: (hideConferenceSubject ? undefined : getConferenceName(state)) ?? ''
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ export const colors = {
|
|||
// after we replace them in the components.
|
||||
primary10: '#17A0DB',
|
||||
primary11: '#1081B2',
|
||||
primary12: '#B8C7E0',
|
||||
surface00: '#111111',
|
||||
surface12: '#AAAAAA',
|
||||
surface13: '#495258',
|
||||
|
@ -199,7 +198,6 @@ export const colorMap = {
|
|||
border01: 'surface08',
|
||||
border02: 'surface06',
|
||||
border03: 'surface04',
|
||||
border04: 'primary12',
|
||||
border05: 'surface07',
|
||||
borderError: 'error06',
|
||||
warning03: 'warning07',
|
||||
|
|
|
@ -10,6 +10,7 @@ import { IInputProps } from '../types';
|
|||
|
||||
interface IProps extends IInputProps {
|
||||
accessibilityLabel?: string;
|
||||
autoComplete?: string;
|
||||
autoFocus?: boolean;
|
||||
bottomLabel?: string;
|
||||
className?: string;
|
||||
|
@ -131,6 +132,7 @@ const useStyles = makeStyles()(theme => {
|
|||
|
||||
const Input = React.forwardRef<any, IProps>(({
|
||||
accessibilityLabel,
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
bottomLabel,
|
||||
className,
|
||||
|
@ -175,6 +177,7 @@ const Input = React.forwardRef<any, IProps>(({
|
|||
{textarea ? (
|
||||
<TextareaAutosize
|
||||
aria-label = { accessibilityLabel }
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { cx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
|
||||
|
@ -194,6 +197,7 @@ const Input = React.forwardRef<any, IProps>(({
|
|||
) : (
|
||||
<input
|
||||
aria-label = { accessibilityLabel }
|
||||
autoComplete = { autoComplete }
|
||||
autoFocus = { autoFocus }
|
||||
className = { cx(styles.input, isMobile && 'is-mobile',
|
||||
error && 'error', clearable && styles.clearableInput, icon && 'icon-input') }
|
||||
|
|
|
@ -265,15 +265,7 @@ export const commonStyles = (theme: Theme) => {
|
|||
padding: 6,
|
||||
textAlign: 'center' as const,
|
||||
pointerEvents: 'all' as const,
|
||||
boxShadow: '0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15)',
|
||||
|
||||
'& > div': {
|
||||
marginLeft: 8,
|
||||
|
||||
'&:first-child': {
|
||||
marginLeft: 0
|
||||
}
|
||||
}
|
||||
boxShadow: '0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15)'
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
// @flow
|
||||
|
||||
import InlineDialog from '@atlaskit/inline-dialog';
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { isNameReadOnly } from '../../../base/config';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconArrowDown, IconArrowUp, IconPhoneRinging, IconVolumeOff } from '../../../base/icons';
|
||||
import { isVideoMutedByUser } from '../../../base/media';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { ActionButton, InputField, PreMeetingScreen } from '../../../base/premeeting';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getDisplayName, updateSettings } from '../../../base/settings';
|
||||
import { getLocalJitsiVideoTrack } from '../../../base/tracks';
|
||||
import { isNameReadOnly } from '../../../base/config/functions.web';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconArrowDown, IconArrowUp, IconPhoneRinging, IconVolumeOff } from '../../../base/icons/svg';
|
||||
import { isVideoMutedByUser } from '../../../base/media/functions';
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
import ActionButton from '../../../base/premeeting/components/web/ActionButton';
|
||||
import PreMeetingScreen from '../../../base/premeeting/components/web/PreMeetingScreen';
|
||||
import { connect } from '../../../base/redux/functions';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import { getDisplayName } from '../../../base/settings/functions.web';
|
||||
import { getLocalJitsiVideoTrack } from '../../../base/tracks/functions.web';
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
import Input from '../../../base/ui/components/web/Input';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.any';
|
||||
import {
|
||||
joinConference as joinConferenceAction,
|
||||
joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
|
||||
setJoinByPhoneDialogVisiblity as setJoinByPhoneDialogVisiblityAction
|
||||
} from '../../actions';
|
||||
} from '../../actions.web';
|
||||
import {
|
||||
isDeviceStatusVisible,
|
||||
isDisplayNameRequired,
|
||||
|
@ -28,114 +33,112 @@ import {
|
|||
isPrejoinDisplayNameVisible
|
||||
} from '../../functions';
|
||||
|
||||
// @ts-ignore
|
||||
import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
|
||||
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Indicates whether the display name is editable.
|
||||
*/
|
||||
canEditDisplayName: boolean,
|
||||
canEditDisplayName: boolean;
|
||||
|
||||
/**
|
||||
* Flag signaling if the device status is visible or not.
|
||||
*/
|
||||
deviceStatusVisible: boolean,
|
||||
deviceStatusVisible: boolean;
|
||||
|
||||
/**
|
||||
* If join by phone button should be visible.
|
||||
*/
|
||||
hasJoinByPhoneButton: boolean,
|
||||
hasJoinByPhoneButton: boolean;
|
||||
|
||||
/**
|
||||
* Joins the current meeting.
|
||||
*/
|
||||
joinConference: Function,
|
||||
joinConference: Function;
|
||||
|
||||
/**
|
||||
* Joins the current meeting without audio.
|
||||
*/
|
||||
joinConferenceWithoutAudio: Function,
|
||||
joinConferenceWithoutAudio: Function;
|
||||
|
||||
/**
|
||||
* Whether conference join is in progress.
|
||||
*/
|
||||
joiningInProgress: boolean,
|
||||
joiningInProgress: boolean;
|
||||
|
||||
/**
|
||||
* The name of the user that is about to join.
|
||||
*/
|
||||
name: string,
|
||||
|
||||
/**
|
||||
* Updates settings.
|
||||
*/
|
||||
updateSettings: Function,
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Local participant id.
|
||||
*/
|
||||
participantId: string,
|
||||
participantId: string;
|
||||
|
||||
/**
|
||||
* The prejoin config.
|
||||
*/
|
||||
prejoinConfig?: Object,
|
||||
prejoinConfig?: any;
|
||||
|
||||
/**
|
||||
* Whether the name input should be read only or not.
|
||||
*/
|
||||
readOnlyName: boolean,
|
||||
readOnlyName: boolean;
|
||||
|
||||
/**
|
||||
* Sets visibility of the 'JoinByPhoneDialog'.
|
||||
*/
|
||||
setJoinByPhoneDialogVisiblity: Function,
|
||||
setJoinByPhoneDialogVisiblity: Function;
|
||||
|
||||
/**
|
||||
* Flag signaling the visibility of camera preview.
|
||||
*/
|
||||
showCameraPreview: boolean,
|
||||
|
||||
/**
|
||||
* If should show an error when joining without a name.
|
||||
*/
|
||||
showErrorOnJoin: boolean,
|
||||
showCameraPreview: boolean;
|
||||
|
||||
/**
|
||||
* If 'JoinByPhoneDialog' is visible or not.
|
||||
*/
|
||||
showDialog: boolean,
|
||||
showDialog: boolean;
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
* If should show an error when joining without a name.
|
||||
*/
|
||||
t: Function,
|
||||
showErrorOnJoin: boolean;
|
||||
|
||||
/**
|
||||
* Updates settings.
|
||||
*/
|
||||
updateSettings: Function;
|
||||
|
||||
/**
|
||||
* The JitsiLocalTrack to display.
|
||||
*/
|
||||
videoTrack: ?Object
|
||||
};
|
||||
videoTrack?: Object;
|
||||
}
|
||||
|
||||
type State = {
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* Flag controlling the visibility of the 'join by phone' buttons.
|
||||
*/
|
||||
showJoinByPhoneButtons: boolean
|
||||
showJoinByPhoneButtons: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is displayed before joining a meeting.
|
||||
*/
|
||||
class Prejoin extends Component<Props, State> {
|
||||
class Prejoin extends Component<IProps, IState> {
|
||||
showDisplayNameField: boolean;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Prejoin} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -150,12 +153,11 @@ class Prejoin extends Component<Props, State> {
|
|||
this._setName = this._setName.bind(this);
|
||||
this._onJoinConferenceWithoutAudioKeyPress = this._onJoinConferenceWithoutAudioKeyPress.bind(this);
|
||||
this._showDialogKeyPress = this._showDialogKeyPress.bind(this);
|
||||
this._onJoinKeyPress = this._onJoinKeyPress.bind(this);
|
||||
this._getExtraJoinButtons = this._getExtraJoinButtons.bind(this);
|
||||
this._onInputKeyPress = this._onInputKeyPress.bind(this);
|
||||
|
||||
this.showDisplayNameField = props.canEditDisplayName || props.showErrorOnJoin;
|
||||
}
|
||||
_onJoinButtonClick: () => void;
|
||||
|
||||
/**
|
||||
* Handler for the join button.
|
||||
|
@ -170,24 +172,6 @@ class Prejoin extends Component<Props, State> {
|
|||
this.props.joinConference();
|
||||
}
|
||||
|
||||
_onJoinKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
* @param {Object} e - The key event to handle.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onJoinKeyPress(e) {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this._onJoinButtonClick();
|
||||
}
|
||||
}
|
||||
|
||||
_onDropdownClose: () => void;
|
||||
|
||||
/**
|
||||
* Closes the dropdown.
|
||||
*
|
||||
|
@ -199,38 +183,32 @@ class Prejoin extends Component<Props, State> {
|
|||
});
|
||||
}
|
||||
|
||||
_onOptionsClick: () => void;
|
||||
|
||||
/**
|
||||
* Displays the join by phone buttons dropdown.
|
||||
*
|
||||
* @param {Object} e - The synthetic event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOptionsClick(e) {
|
||||
e.stopPropagation();
|
||||
_onOptionsClick(e?: React.KeyboardEvent | React.MouseEvent | undefined) {
|
||||
e?.stopPropagation();
|
||||
|
||||
this.setState({
|
||||
showJoinByPhoneButtons: !this.state.showJoinByPhoneButtons
|
||||
});
|
||||
}
|
||||
|
||||
_setName: () => void;
|
||||
|
||||
/**
|
||||
* Sets the guest participant name.
|
||||
*
|
||||
* @param {string} displayName - Participant name.
|
||||
* @returns {void}
|
||||
*/
|
||||
_setName(displayName) {
|
||||
_setName(displayName: string) {
|
||||
this.props.updateSettings({
|
||||
displayName
|
||||
});
|
||||
}
|
||||
|
||||
_closeDialog: () => void;
|
||||
|
||||
/**
|
||||
* Closes the join by phone dialog.
|
||||
*
|
||||
|
@ -240,8 +218,6 @@ class Prejoin extends Component<Props, State> {
|
|||
this.props.setJoinByPhoneDialogVisiblity(false);
|
||||
}
|
||||
|
||||
_showDialog: () => void;
|
||||
|
||||
/**
|
||||
* Displays the dialog for joining a meeting by phone.
|
||||
*
|
||||
|
@ -252,8 +228,6 @@ class Prejoin extends Component<Props, State> {
|
|||
this._onDropdownClose();
|
||||
}
|
||||
|
||||
_showDialogKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
|
@ -261,15 +235,13 @@ class Prejoin extends Component<Props, State> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_showDialogKeyPress(e) {
|
||||
_showDialogKeyPress(e: React.KeyboardEvent) {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this._showDialog();
|
||||
}
|
||||
}
|
||||
|
||||
_onJoinConferenceWithoutAudioKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
|
@ -277,7 +249,7 @@ class Prejoin extends Component<Props, State> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onJoinConferenceWithoutAudioKeyPress(e) {
|
||||
_onJoinConferenceWithoutAudioKeyPress(e: React.KeyboardEvent) {
|
||||
if (this.props.joinConferenceWithoutAudio
|
||||
&& (e.key === ' '
|
||||
|| e.key === 'Enter')) {
|
||||
|
@ -286,8 +258,6 @@ class Prejoin extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
_getExtraJoinButtons: () => Object;
|
||||
|
||||
/**
|
||||
* Gets the list of extra join buttons.
|
||||
*
|
||||
|
@ -320,6 +290,20 @@ class Prejoin extends Component<Props, State> {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle keypress on input.
|
||||
*
|
||||
* @param {KeyboardEvent} e - Keyboard event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onInputKeyPress(e: React.KeyboardEvent) {
|
||||
const { joinConference } = this.props;
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
joinConference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
@ -330,7 +314,6 @@ class Prejoin extends Component<Props, State> {
|
|||
const {
|
||||
deviceStatusVisible,
|
||||
hasJoinByPhoneButton,
|
||||
joinConference,
|
||||
joinConferenceWithoutAudio,
|
||||
joiningInProgress,
|
||||
name,
|
||||
|
@ -343,16 +326,16 @@ class Prejoin extends Component<Props, State> {
|
|||
t,
|
||||
videoTrack
|
||||
} = this.props;
|
||||
const { _closeDialog, _onDropdownClose, _onJoinButtonClick, _onJoinKeyPress,
|
||||
_onOptionsClick, _setName } = this;
|
||||
const { _closeDialog, _onDropdownClose, _onJoinButtonClick,
|
||||
_onOptionsClick, _setName, _onInputKeyPress } = this;
|
||||
|
||||
const extraJoinButtons = this._getExtraJoinButtons();
|
||||
let extraButtonsToRender = Object.values(extraJoinButtons).filter((val: Object) =>
|
||||
let extraButtonsToRender = Object.values(extraJoinButtons).filter((val: any) =>
|
||||
!(prejoinConfig?.hideExtraJoinButtons || []).includes(val.key)
|
||||
);
|
||||
|
||||
if (!hasJoinByPhoneButton) {
|
||||
extraButtonsToRender = extraButtonsToRender.filter((btn: Object) => btn.key !== 'by-phone');
|
||||
extraButtonsToRender = extraButtonsToRender.filter((btn: any) => btn.key !== 'by-phone');
|
||||
}
|
||||
const hasExtraJoinButtons = Boolean(extraButtonsToRender.length);
|
||||
const { showJoinByPhoneButtons } = this.state;
|
||||
|
@ -366,14 +349,14 @@ class Prejoin extends Component<Props, State> {
|
|||
<div
|
||||
className = 'prejoin-input-area'
|
||||
data-testid = 'prejoin.screen'>
|
||||
{this.showDisplayNameField ? (<InputField
|
||||
{this.showDisplayNameField ? (<Input
|
||||
autoComplete = { 'name' }
|
||||
autoFocus = { true }
|
||||
className = { showErrorOnJoin ? 'error' : '' }
|
||||
hasError = { showErrorOnJoin }
|
||||
className = 'prejoin-input'
|
||||
error = { showErrorOnJoin }
|
||||
onChange = { _setName }
|
||||
onSubmit = { joinConference }
|
||||
placeHolder = { t('dialog.enterDisplayName') }
|
||||
onKeyPress = { _onInputKeyPress }
|
||||
placeholder = { t('dialog.enterDisplayName') }
|
||||
readOnly = { readOnlyName }
|
||||
value = { name } />
|
||||
) : (
|
||||
|
@ -394,7 +377,7 @@ class Prejoin extends Component<Props, State> {
|
|||
<div className = 'prejoin-preview-dropdown-container'>
|
||||
<InlineDialog
|
||||
content = { hasExtraJoinButtons && <div className = 'prejoin-preview-dropdown-btns'>
|
||||
{extraButtonsToRender.map(({ key, ...rest }: Object) => (
|
||||
{extraButtonsToRender.map(({ key, ...rest }) => (
|
||||
<Button
|
||||
disabled = { joiningInProgress }
|
||||
fullWidth = { true }
|
||||
|
@ -413,7 +396,6 @@ class Prejoin extends Component<Props, State> {
|
|||
disabled = { joiningInProgress }
|
||||
hasOptions = { hasExtraJoinButtons }
|
||||
onClick = { _onJoinButtonClick }
|
||||
onKeyPress = { _onJoinKeyPress }
|
||||
onOptionsClick = { _onOptionsClick }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }
|
||||
|
@ -440,10 +422,10 @@ class Prejoin extends Component<Props, State> {
|
|||
* @param {Object} state - The redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state): Object {
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
const name = getDisplayName(state);
|
||||
const showErrorOnJoin = isDisplayNameRequired(state) && !name;
|
||||
const { id: participantId } = getLocalParticipant(state);
|
||||
const { id: participantId } = getLocalParticipant(state) ?? {};
|
||||
const { joiningInProgress } = state['features/prejoin'];
|
||||
|
||||
return {
|
|
@ -4,9 +4,8 @@ import { makeStyles } from 'tss-react/mui';
|
|||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconCheck, IconExclamationTriangle } from '../../../../base/icons/svg';
|
||||
import { connect } from '../../../../base/redux/functions';
|
||||
import { withPixelLineHeight } from '../../../../base/styles/functions.web';
|
||||
import {
|
||||
getDeviceStatusText,
|
||||
getDeviceStatusType
|
||||
|
@ -29,13 +28,12 @@ export interface IProps extends WithTranslation {
|
|||
const useStyles = makeStyles()(theme => {
|
||||
return {
|
||||
deviceStatus: {
|
||||
alignItems: 'center',
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
padding: '6px',
|
||||
textAlign: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||
color: '#fff',
|
||||
marginTop: theme.spacing(4),
|
||||
|
||||
'& span': {
|
||||
marginLeft: theme.spacing(3)
|
||||
|
@ -47,33 +45,23 @@ const useStyles = makeStyles()(theme => {
|
|||
borderRadius: '6px',
|
||||
color: theme.palette.uiBackground,
|
||||
padding: '12px 16px',
|
||||
textAlign: 'left'
|
||||
textAlign: 'left',
|
||||
marginTop: theme.spacing(2)
|
||||
},
|
||||
'& .device-icon': {
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
display: 'inline-block',
|
||||
height: '16px',
|
||||
width: '16px'
|
||||
},
|
||||
'& .device-icon--ok svg path': {
|
||||
fill: '#189B55'
|
||||
|
||||
'@media (max-width: 720px)': {
|
||||
marginTop: 0
|
||||
}
|
||||
},
|
||||
indicator: {
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '100%',
|
||||
backgroundColor: theme.palette.success01
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const iconMap = {
|
||||
warning: {
|
||||
src: IconExclamationTriangle,
|
||||
className: 'device-icon--warning'
|
||||
},
|
||||
ok: {
|
||||
src: IconCheck,
|
||||
className: 'device-icon--ok'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip showing the current status of the devices.
|
||||
* User is informed if there are missing or malfunctioning devices.
|
||||
|
@ -82,7 +70,6 @@ const iconMap = {
|
|||
*/
|
||||
function DeviceStatus({ deviceStatusType, deviceStatusText, t }: IProps) {
|
||||
const { classes, cx } = useStyles();
|
||||
const { src, className } = iconMap[deviceStatusType as keyof typeof iconMap];
|
||||
const hasError = deviceStatusType === 'warning';
|
||||
const containerClassName = cx(classes.deviceStatus, { 'device-status-error': hasError });
|
||||
|
||||
|
@ -91,10 +78,7 @@ function DeviceStatus({ deviceStatusType, deviceStatusText, t }: IProps) {
|
|||
className = { containerClassName }
|
||||
role = 'alert'
|
||||
tabIndex = { -1 }>
|
||||
<Icon
|
||||
className = { `device-icon ${className}` }
|
||||
size = { 16 }
|
||||
src = { src } />
|
||||
{!hasError && <div className = { classes.indicator } />}
|
||||
<span role = 'heading'>
|
||||
{hasError ? t('prejoin.errorNoPermissions') : t(deviceStatusText ?? '')}
|
||||
</span>
|
||||
|
|
Loading…
Reference in New Issue