Compare commits

...

2 Commits

Author SHA1 Message Date
Horatiu Muresan 8ca786ce74 Address review 2023-01-04 12:53:27 +02:00
Horatiu Muresan 2e353eb51d feat(prejoin) Disable join buttons during joining 2023-01-04 11:49:57 +02:00
5 changed files with 34 additions and 120 deletions

View File

@ -50,7 +50,7 @@
* Override default InlineDialog behaviour, since it does not play nicely with relative widths * Override default InlineDialog behaviour, since it does not play nicely with relative widths
*/ */
& > div:nth-child(2) { & > div:nth-child(2) {
background: #fff; background: #E0E0E0;
padding: 0; padding: 0;
position: absolute !important; position: absolute !important;
width: 100%; width: 100%;

View File

@ -34,6 +34,11 @@ export interface IButtonProps {
*/ */
onClick?: (e?: React.MouseEvent<HTMLButtonElement> | GestureResponderEvent) => void; onClick?: (e?: React.MouseEvent<HTMLButtonElement> | GestureResponderEvent) => void;
/**
* Key press callback.
*/
onKeyPress?: (e?: React.KeyboardEvent<HTMLButtonElement>) => void;
/** /**
* The type of button to be displayed. * The type of button to be displayed.
*/ */

View File

@ -187,6 +187,7 @@ const Button = React.forwardRef<any, any>(({
label, label,
labelKey, labelKey,
onClick = () => null, onClick = () => null,
onKeyPress = () => null,
size = 'medium', size = 'medium',
testId, testId,
type = BUTTON_TYPES.PRIMARY type = BUTTON_TYPES.PRIMARY
@ -206,6 +207,7 @@ const Button = React.forwardRef<any, any>(({
disabled = { disabled } disabled = { disabled }
{ ...(id ? { id } : {}) } { ...(id ? { id } : {}) }
onClick = { onClick } onClick = { onClick }
onKeyPress = { onKeyPress }
ref = { ref } ref = { ref }
title = { accessibilityLabel } title = { accessibilityLabel }
type = { isSubmit ? 'submit' : 'button' }> type = { isSubmit ? 'submit' : 'button' }>

View File

@ -1,95 +0,0 @@
import React from 'react';
import { makeStyles } from 'tss-react/mui';
import Icon from '../../../base/icons/components/Icon';
interface IProps {
/**
* Attribute used in automated testing.
*/
dataTestId: string;
/**
* The button's icon.
*/
icon: Function;
/**
* The button's label.
*/
label: string;
/**
* Function to be called when button is clicked.
*/
onButtonClick: (e?: React.MouseEvent) => void;
/**
* Function to be called on key pressed.
*/
onKeyPressed: (e?: React.KeyboardEvent) => void;
}
const useStyles = makeStyles()(theme => {
return {
prejoinPreviewDropdownBtn: {
alignItems: 'center',
color: '#1C2025',
cursor: 'pointer',
display: 'flex',
height: 40,
fontSize: 15,
lineHeight: '24px',
padding: '0 16px', // @ts-ignore
backgroundColor: theme.palette.field02,
'&:hover': { // @ts-ignore
backgroundColor: theme.palette.field02Hover
}
},
prejoinPreviewDropdownIcon: {
display: 'inline-block',
marginRight: 16,
'& > svg': {
fill: '#1C2025'
}
}
};
});
/**
* Buttons used for pre meeting actions.
*
* @returns {ReactElement}
*/
const DropdownButton = ({
dataTestId,
icon,
onButtonClick,
onKeyPressed,
label
}: IProps) => {
const { classes } = useStyles();
return (
<div
className = { classes.prejoinPreviewDropdownBtn }
data-testid = { dataTestId }
onClick = { onButtonClick }
onKeyPress = { onKeyPressed }
role = 'button'
tabIndex = { 0 }>
<Icon
className = { classes.prejoinPreviewDropdownIcon }
color = '#1C2025'
size = { 24 }
src = { icon } />
{label}
</div>
);
};
export default DropdownButton;

View File

@ -13,6 +13,8 @@ import { ActionButton, InputField, PreMeetingScreen } from '../../../base/premee
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { getDisplayName, updateSettings } from '../../../base/settings'; import { getDisplayName, updateSettings } from '../../../base/settings';
import { getLocalJitsiVideoTrack } from '../../../base/tracks'; import { getLocalJitsiVideoTrack } from '../../../base/tracks';
import Button from '../../../base/ui/components/web/Button';
import { BUTTON_TYPES } from '../../../base/ui/constants.any';
import { import {
joinConference as joinConferenceAction, joinConference as joinConferenceAction,
joinConferenceWithoutAudio as joinConferenceWithoutAudioAction, joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
@ -26,7 +28,6 @@ import {
isPrejoinDisplayNameVisible isPrejoinDisplayNameVisible
} from '../../functions'; } from '../../functions';
import DropdownButton from './DropdownButton';
import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog'; import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
type Props = { type Props = {
@ -56,6 +57,11 @@ type Props = {
*/ */
joinConferenceWithoutAudio: Function, joinConferenceWithoutAudio: Function,
/**
* Whether conference join is in progress.
*/
joiningInProgress: boolean,
/** /**
* The name of the user that is about to join. * The name of the user that is about to join.
*/ */
@ -114,11 +120,6 @@ type Props = {
type State = { type State = {
/**
* Flag controlling the visibility of the error label.
*/
showError: boolean,
/** /**
* Flag controlling the visibility of the 'join by phone' buttons. * Flag controlling the visibility of the 'join by phone' buttons.
*/ */
@ -138,7 +139,6 @@ class Prejoin extends Component<Props, State> {
super(props); super(props);
this.state = { this.state = {
showError: false,
showJoinByPhoneButtons: false showJoinByPhoneButtons: false
}; };
@ -165,14 +165,8 @@ class Prejoin extends Component<Props, State> {
*/ */
_onJoinButtonClick() { _onJoinButtonClick() {
if (this.props.showErrorOnJoin) { if (this.props.showErrorOnJoin) {
this.setState({
showError: true
});
return; return;
} }
this.setState({ showError: false });
this.props.joinConference(); this.props.joinConference();
} }
@ -304,20 +298,20 @@ class Prejoin extends Component<Props, State> {
const noAudio = { const noAudio = {
key: 'no-audio', key: 'no-audio',
dataTestId: 'prejoin.joinWithoutAudio', testId: 'prejoin.joinWithoutAudio',
icon: IconVolumeOff, icon: IconVolumeOff,
label: t('prejoin.joinWithoutAudio'), label: t('prejoin.joinWithoutAudio'),
onButtonClick: joinConferenceWithoutAudio, onClick: joinConferenceWithoutAudio,
onKeyPressed: this._onJoinConferenceWithoutAudioKeyPress onKeyPress: this._onJoinConferenceWithoutAudioKeyPress
}; };
const byPhone = { const byPhone = {
key: 'by-phone', key: 'by-phone',
dataTestId: 'prejoin.joinByPhone', testId: 'prejoin.joinByPhone',
icon: IconPhoneRinging, icon: IconPhoneRinging,
label: t('prejoin.joinAudioByPhone'), label: t('prejoin.joinAudioByPhone'),
onButtonClick: this._showDialog, onClick: this._showDialog,
onKeyPressed: this._showDialogKeyPress onKeyPress: this._showDialogKeyPress
}; };
return { return {
@ -338,12 +332,14 @@ class Prejoin extends Component<Props, State> {
hasJoinByPhoneButton, hasJoinByPhoneButton,
joinConference, joinConference,
joinConferenceWithoutAudio, joinConferenceWithoutAudio,
joiningInProgress,
name, name,
participantId, participantId,
prejoinConfig, prejoinConfig,
readOnlyName, readOnlyName,
showCameraPreview, showCameraPreview,
showDialog, showDialog,
showErrorOnJoin,
t, t,
videoTrack videoTrack
} = this.props; } = this.props;
@ -359,7 +355,7 @@ class Prejoin extends Component<Props, State> {
extraButtonsToRender = extraButtonsToRender.filter((btn: Object) => btn.key !== 'by-phone'); extraButtonsToRender = extraButtonsToRender.filter((btn: Object) => btn.key !== 'by-phone');
} }
const hasExtraJoinButtons = Boolean(extraButtonsToRender.length); const hasExtraJoinButtons = Boolean(extraButtonsToRender.length);
const { showJoinByPhoneButtons, showError } = this.state; const { showJoinByPhoneButtons } = this.state;
return ( return (
<PreMeetingScreen <PreMeetingScreen
@ -373,8 +369,8 @@ class Prejoin extends Component<Props, State> {
{this.showDisplayNameField ? (<InputField {this.showDisplayNameField ? (<InputField
autoComplete = { 'name' } autoComplete = { 'name' }
autoFocus = { true } autoFocus = { true }
className = { showError ? 'error' : '' } className = { showErrorOnJoin ? 'error' : '' }
hasError = { showError } hasError = { showErrorOnJoin }
onChange = { _setName } onChange = { _setName }
onSubmit = { joinConference } onSubmit = { joinConference }
placeHolder = { t('dialog.enterDisplayName') } placeHolder = { t('dialog.enterDisplayName') }
@ -391,7 +387,7 @@ class Prejoin extends Component<Props, State> {
</div> </div>
)} )}
{showError && <div {showErrorOnJoin && <div
className = 'prejoin-error' className = 'prejoin-error'
data-testid = 'prejoin.errorMessage'>{t('prejoin.errorMissingName')}</div>} data-testid = 'prejoin.errorMessage'>{t('prejoin.errorMissingName')}</div>}
@ -399,8 +395,11 @@ class Prejoin extends Component<Props, State> {
<InlineDialog <InlineDialog
content = { hasExtraJoinButtons && <div className = 'prejoin-preview-dropdown-btns'> content = { hasExtraJoinButtons && <div className = 'prejoin-preview-dropdown-btns'>
{extraButtonsToRender.map(({ key, ...rest }: Object) => ( {extraButtonsToRender.map(({ key, ...rest }: Object) => (
<DropdownButton <Button
disabled = { joiningInProgress }
fullWidth = { true }
key = { key } key = { key }
type = { BUTTON_TYPES.SECONDARY }
{ ...rest } /> { ...rest } />
))} ))}
</div> } </div> }
@ -411,6 +410,7 @@ class Prejoin extends Component<Props, State> {
ariaDropDownLabel = { t('prejoin.joinWithoutAudio') } ariaDropDownLabel = { t('prejoin.joinWithoutAudio') }
ariaLabel = { t('prejoin.joinMeeting') } ariaLabel = { t('prejoin.joinMeeting') }
ariaPressed = { showJoinByPhoneButtons } ariaPressed = { showJoinByPhoneButtons }
disabled = { joiningInProgress }
hasOptions = { hasExtraJoinButtons } hasOptions = { hasExtraJoinButtons }
onClick = { _onJoinButtonClick } onClick = { _onJoinButtonClick }
onKeyPress = { _onJoinKeyPress } onKeyPress = { _onJoinKeyPress }
@ -444,11 +444,13 @@ function mapStateToProps(state): Object {
const name = getDisplayName(state); const name = getDisplayName(state);
const showErrorOnJoin = isDisplayNameRequired(state) && !name; const showErrorOnJoin = isDisplayNameRequired(state) && !name;
const { id: participantId } = getLocalParticipant(state); const { id: participantId } = getLocalParticipant(state);
const { joiningInProgress } = state['features/prejoin'];
return { return {
canEditDisplayName: isPrejoinDisplayNameVisible(state), canEditDisplayName: isPrejoinDisplayNameVisible(state),
deviceStatusVisible: isDeviceStatusVisible(state), deviceStatusVisible: isDeviceStatusVisible(state),
hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state), hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state),
joiningInProgress,
name, name,
participantId, participantId,
prejoinConfig: state['features/base/config'].prejoinConfig, prejoinConfig: state['features/base/config'].prejoinConfig,