mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 03:12:29 +00:00
fix(prejoin) improve display name handling relative to configs (#13865)
This commit is contained in:
@@ -16,6 +16,7 @@ import { removeLobbyChatParticipant } from '../../chat/actions.any';
|
||||
import { openDisplayNamePrompt } from '../../display-name/actions';
|
||||
import { showErrorNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
import { hasDisplayName } from '../../prejoin/utils';
|
||||
import { stopLocalVideoRecording } from '../../recording/actions.any';
|
||||
import LocalRecordingManager from '../../recording/components/Recording/LocalRecordingManager';
|
||||
import { iAmVisitor } from '../../visitors/functions';
|
||||
@@ -276,7 +277,9 @@ function _conferenceJoined({ dispatch, getState }: IStore, next: Function, actio
|
||||
if (requireDisplayName
|
||||
&& !getLocalParticipant(getState)?.name
|
||||
&& !conference.isHidden()) {
|
||||
dispatch(openDisplayNamePrompt(undefined));
|
||||
dispatch(openDisplayNamePrompt({
|
||||
validateInput: hasDisplayName
|
||||
}));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -20,6 +20,11 @@ interface IProps extends AbstractProps, WithTranslation {
|
||||
*/
|
||||
descriptionKey?: string;
|
||||
|
||||
/**
|
||||
* Whether to display the cancel button.
|
||||
*/
|
||||
disableCancel?: boolean;
|
||||
|
||||
/**
|
||||
* An optional initial value to initiate the field with.
|
||||
*/
|
||||
@@ -52,6 +57,11 @@ interface IState extends AbstractState {
|
||||
* The current value of the field.
|
||||
*/
|
||||
fieldValue?: string;
|
||||
|
||||
/**
|
||||
* The result of the input validation.
|
||||
*/
|
||||
isValid: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,6 +78,7 @@ class InputDialog extends AbstractDialog<IProps, IState> {
|
||||
|
||||
this.state = {
|
||||
fieldValue: props.initialValue,
|
||||
isValid: props.validateInput ? props.validateInput(props.initialValue) : true,
|
||||
submitting: false
|
||||
};
|
||||
|
||||
@@ -115,10 +126,11 @@ class InputDialog extends AbstractDialog<IProps, IState> {
|
||||
</Dialog.Description>
|
||||
)
|
||||
}
|
||||
<Dialog.Button
|
||||
{!this.props.disableCancel && <Dialog.Button
|
||||
label = { t('dialog.Cancel') }
|
||||
onPress = { this._onCancel } />
|
||||
onPress = { this._onCancel } />}
|
||||
<Dialog.Button
|
||||
disabled = { !this.state.isValid }
|
||||
label = { t('dialog.Ok') }
|
||||
onPress = { this._onSubmitValue } />
|
||||
</Dialog.Container>
|
||||
@@ -132,10 +144,14 @@ class InputDialog extends AbstractDialog<IProps, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeText(fieldValue: string) {
|
||||
if (this.props.validateInput && !this.props.validateInput(fieldValue)) {
|
||||
if (this.props.validateInput) {
|
||||
this.setState({
|
||||
isValid: this.props.validateInput(fieldValue),
|
||||
fieldValue
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
fieldValue
|
||||
});
|
||||
|
||||
@@ -136,6 +136,7 @@ export interface IProps {
|
||||
description?: string;
|
||||
disableBackdropClose?: boolean;
|
||||
disableEnter?: boolean;
|
||||
disableEscape?: boolean;
|
||||
onClose?: () => void;
|
||||
size?: 'large' | 'medium';
|
||||
submit?: () => void;
|
||||
@@ -150,6 +151,7 @@ const BaseDialog = ({
|
||||
description,
|
||||
disableBackdropClose,
|
||||
disableEnter,
|
||||
disableEscape,
|
||||
onClose,
|
||||
size = 'medium',
|
||||
submit,
|
||||
@@ -166,7 +168,7 @@ const BaseDialog = ({
|
||||
}, [ disableBackdropClose, onClose ]);
|
||||
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (e.key === 'Escape' && !disableEscape) {
|
||||
onClose?.();
|
||||
}
|
||||
if (e.key === 'Enter' && !disableEnter) {
|
||||
|
||||
@@ -92,6 +92,7 @@ const Dialog = ({
|
||||
disableBackdropClose,
|
||||
hideCloseButton,
|
||||
disableEnter,
|
||||
disableEscape,
|
||||
ok = { translationKey: 'dialog.Ok' },
|
||||
onCancel,
|
||||
onSubmit,
|
||||
@@ -125,6 +126,7 @@ const Dialog = ({
|
||||
description = { description }
|
||||
disableBackdropClose = { disableBackdropClose }
|
||||
disableEnter = { disableEnter }
|
||||
disableEscape = { disableEscape }
|
||||
onClose = { onClose }
|
||||
size = { size }
|
||||
submit = { submit }
|
||||
@@ -170,7 +172,7 @@ const Dialog = ({
|
||||
id = 'modal-dialog-ok-button'
|
||||
isSubmit = { true }
|
||||
labelKey = { ok.translationKey }
|
||||
onClick = { submit } />}
|
||||
{ ...(!ok.disabled && { onClick: submit }) } />}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
|
||||
@@ -5,12 +5,17 @@ import { DisplayNamePrompt } from './components';
|
||||
/**
|
||||
* Signals to open a dialog with the {@code DisplayNamePrompt} component.
|
||||
*
|
||||
* @param {?Function} onPostSubmit - The function to invoke after a successful
|
||||
* submit of the dialog.
|
||||
* @param {Object} params - Map containing the callbacks to be executed in the prompt:
|
||||
* - onPostSubmit - The function to invoke after a successful submit of the dialog.
|
||||
* - validateInput - The function to invoke after a change in the display name value.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function openDisplayNamePrompt(onPostSubmit?: Function) {
|
||||
export function openDisplayNamePrompt({ onPostSubmit, validateInput }: {
|
||||
onPostSubmit?: Function;
|
||||
validateInput?: Function;
|
||||
}) {
|
||||
return openDialog(DisplayNamePrompt, {
|
||||
onPostSubmit
|
||||
onPostSubmit,
|
||||
validateInput
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,8 +33,10 @@ class DisplayNamePrompt extends Component<IProps> {
|
||||
return (
|
||||
<InputDialog
|
||||
descriptionKey = 'dialog.enterDisplayName'
|
||||
disableCancel = { true }
|
||||
onSubmit = { this._onSetDisplayName }
|
||||
titleKey = 'dialog.displayNameRequired' />
|
||||
titleKey = 'dialog.displayNameRequired'
|
||||
validateInput = { this.props.validateInput } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import Input from '../../../base/ui/components/web/Input';
|
||||
import { onSetDisplayName } from '../../functions';
|
||||
import { IProps } from '../../types';
|
||||
|
||||
const INITIAL_DISPLAY_NAME = '';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link DisplayNamePrompt}.
|
||||
*/
|
||||
@@ -16,6 +18,11 @@ interface IState {
|
||||
* The name to show in the display name text field.
|
||||
*/
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* The result of the input validation.
|
||||
*/
|
||||
isValid: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +44,8 @@ class DisplayNamePrompt extends Component<IProps, IState> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
displayName: ''
|
||||
displayName: INITIAL_DISPLAY_NAME,
|
||||
isValid: this.props.validateInput ? this.props.validateInput(INITIAL_DISPLAY_NAME) : true
|
||||
};
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
@@ -53,10 +61,19 @@ class DisplayNamePrompt extends Component<IProps, IState> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const disableCloseDialog = Boolean(this.props.validateInput);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
cancel = {{ translationKey: 'dialog.Cancel' }}
|
||||
ok = {{ translationKey: 'dialog.Ok' }}
|
||||
cancel = {{ hidden: true }}
|
||||
disableBackdropClose = { disableCloseDialog }
|
||||
disableEnter = { !this.state.isValid }
|
||||
disableEscape = { disableCloseDialog }
|
||||
hideCloseButton = { disableCloseDialog }
|
||||
ok = {{
|
||||
disabled: !this.state.isValid,
|
||||
translationKey: 'dialog.Ok'
|
||||
}}
|
||||
onSubmit = { this._onSubmit }
|
||||
titleKey = 'dialog.displayNameRequired'>
|
||||
<Input
|
||||
@@ -80,6 +97,14 @@ class DisplayNamePrompt extends Component<IProps, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDisplayNameChange(value: string) {
|
||||
if (this.props.validateInput) {
|
||||
this.setState({
|
||||
isValid: this.props.validateInput(value),
|
||||
displayName: value
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
displayName: value
|
||||
});
|
||||
|
||||
@@ -17,4 +17,9 @@ export interface IProps extends WithTranslation {
|
||||
* Function to be invoked after a successful display name change.
|
||||
*/
|
||||
onPostSubmit?: Function;
|
||||
|
||||
/**
|
||||
* Function to be invoked after a display name change.
|
||||
*/
|
||||
validateInput?: Function;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
BackHandler,
|
||||
@@ -16,6 +16,7 @@ import { appNavigate } from '../../../app/actions.native';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { setAudioOnly } from '../../../base/audio-only/actions';
|
||||
import { getConferenceName } from '../../../base/conference/functions';
|
||||
import { isNameReadOnly } from '../../../base/config/functions.any';
|
||||
import { connect } from '../../../base/connection/actions.native';
|
||||
import { PREJOIN_PAGE_HIDE_DISPLAY_NAME } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
@@ -28,6 +29,7 @@ import { updateSettings } from '../../../base/settings/actions';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import { openDisplayNamePrompt } from '../../../display-name/actions';
|
||||
import BrandingImageBackground from '../../../dynamic-branding/components/native/BrandingImageBackground';
|
||||
import LargeVideo from '../../../large-video/components/LargeVideo.native';
|
||||
import HeaderNavigationButton from '../../../mobile/navigation/components/HeaderNavigationButton';
|
||||
@@ -37,6 +39,7 @@ import AudioMuteButton from '../../../toolbox/components/native/AudioMuteButton'
|
||||
import VideoMuteButton from '../../../toolbox/components/native/VideoMuteButton';
|
||||
import { isDisplayNameRequired } from '../../functions';
|
||||
import { IPrejoinProps } from '../../types';
|
||||
import { hasDisplayName } from '../../utils';
|
||||
|
||||
import { preJoinStyles as styles } from './styles';
|
||||
|
||||
@@ -52,10 +55,19 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
|
||||
const isDisplayNameMandatory = useSelector((state: IReduxState) => isDisplayNameRequired(state));
|
||||
const isDisplayNameVisible
|
||||
= useSelector((state: IReduxState) => !getFeatureFlag(state, PREJOIN_PAGE_HIDE_DISPLAY_NAME, false));
|
||||
const isDisplayNameReadonly = !useSelector(isNameReadOnly);
|
||||
const roomName = useSelector((state: IReduxState) => getConferenceName(state));
|
||||
const participantName = localParticipant?.name;
|
||||
const [ displayName, setDisplayName ]
|
||||
= useState(participantName || '');
|
||||
const isDisplayNameMissing = useMemo(
|
||||
() => !displayName && isDisplayNameMandatory, [ displayName, isDisplayNameMandatory ]);
|
||||
const showDisplayNameError = useMemo(
|
||||
() => !isDisplayNameReadonly && isDisplayNameMissing && isDisplayNameVisible,
|
||||
[ isDisplayNameMissing, isDisplayNameReadonly, isDisplayNameVisible ]);
|
||||
const showDisplayNameInput = useMemo(
|
||||
() => isDisplayNameVisible && (displayName || !isDisplayNameReadonly),
|
||||
[ displayName, isDisplayNameReadonly, isDisplayNameVisible ]);
|
||||
const [ isJoining, setIsJoining ]
|
||||
= useState(false);
|
||||
const onChangeDisplayName = useCallback(event => {
|
||||
@@ -73,9 +85,20 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
|
||||
navigateRoot(screen.conference.root);
|
||||
}, [ dispatch ]);
|
||||
|
||||
const maybeJoin = useCallback(() => {
|
||||
if (isDisplayNameMissing) {
|
||||
dispatch(openDisplayNamePrompt({
|
||||
onPostSubmit: onJoin,
|
||||
validateInput: hasDisplayName
|
||||
}));
|
||||
} else {
|
||||
onJoin();
|
||||
}
|
||||
}, [ dispatch, hasDisplayName, isDisplayNameMissing, onJoin ]);
|
||||
|
||||
const onJoinLowBandwidth = useCallback(() => {
|
||||
dispatch(setAudioOnly(true));
|
||||
onJoin();
|
||||
maybeJoin();
|
||||
}, [ dispatch ]);
|
||||
|
||||
const goBack = useCallback(() => {
|
||||
@@ -85,7 +108,6 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
|
||||
}, [ dispatch ]);
|
||||
|
||||
const { PRIMARY, TERTIARY } = BUTTON_TYPES;
|
||||
const joinButtonDisabled = !displayName && isDisplayNameMandatory;
|
||||
|
||||
useEffect(() => {
|
||||
BackHandler.addEventListener('hardwareBackPress', goBack);
|
||||
@@ -158,25 +180,30 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
|
||||
styles = { styles.buttonStylesBorderless } />
|
||||
</View>
|
||||
{
|
||||
isDisplayNameVisible
|
||||
&& <Input
|
||||
showDisplayNameInput && <Input
|
||||
customStyles = {{ input: styles.customInput }}
|
||||
disabled = { isDisplayNameReadonly }
|
||||
error = { showDisplayNameError }
|
||||
onChange = { onChangeDisplayName }
|
||||
placeholder = { t('dialog.enterDisplayName') }
|
||||
value = { displayName } />
|
||||
}
|
||||
{showDisplayNameError && (
|
||||
<View style = { styles.errorContainer as StyleProp<TextStyle> }>
|
||||
<Text style = { styles.error as StyleProp<TextStyle> }>{t('prejoin.errorMissingName')}</Text>
|
||||
</View>)}
|
||||
<Button
|
||||
accessibilityLabel = 'prejoin.joinMeeting'
|
||||
disabled = { joinButtonDisabled }
|
||||
disabled = { showDisplayNameError }
|
||||
labelKey = 'prejoin.joinMeeting'
|
||||
onClick = { isJoining ? undefined : onJoin }
|
||||
onClick = { isJoining ? undefined : maybeJoin }
|
||||
style = { styles.joinButton }
|
||||
type = { PRIMARY } />
|
||||
<Button
|
||||
accessibilityLabel = 'prejoin.joinMeetingInLowBandwidthMode'
|
||||
disabled = { joinButtonDisabled }
|
||||
disabled = { showDisplayNameError }
|
||||
labelKey = 'prejoin.joinMeetingInLowBandwidthMode'
|
||||
onClick = { onJoinLowBandwidth }
|
||||
onClick = { isJoining ? undefined : onJoinLowBandwidth }
|
||||
style = { styles.joinButton }
|
||||
type = { TERTIARY } />
|
||||
</View>
|
||||
|
||||
@@ -83,6 +83,23 @@ export const preJoinStyles = {
|
||||
width: 352
|
||||
},
|
||||
|
||||
errorContainer: {
|
||||
backgroundColor: BaseTheme.palette.actionDanger,
|
||||
borderBottomRightRadius: BaseTheme.shape.borderRadius,
|
||||
borderBottomLeftRadius: BaseTheme.shape.borderRadius,
|
||||
boxSizing: 'border-box',
|
||||
marginTop: -BaseTheme.spacing[2],
|
||||
overflow: 'visible',
|
||||
wordBreak: 'normal',
|
||||
width: 352
|
||||
},
|
||||
|
||||
error: {
|
||||
padding: BaseTheme.spacing[1],
|
||||
color: BaseTheme.palette.text01,
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
preJoinRoomName: {
|
||||
...BaseTheme.typography.heading5,
|
||||
color: BaseTheme.palette.text01,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { connect, useDispatch } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
@@ -21,6 +21,7 @@ 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 isInsecureRoomName from '../../../base/util/isInsecureRoomName';
|
||||
import { openDisplayNamePrompt } from '../../../display-name/actions';
|
||||
import { isUnsafeRoomWarningEnabled } from '../../../prejoin/functions';
|
||||
import {
|
||||
joinConference as joinConferenceAction,
|
||||
@@ -34,16 +35,12 @@ import {
|
||||
isJoinByPhoneDialogVisible,
|
||||
isPrejoinDisplayNameVisible
|
||||
} from '../../functions';
|
||||
import { hasDisplayName } from '../../utils';
|
||||
|
||||
import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Indicates whether the display name is editable.
|
||||
*/
|
||||
canEditDisplayName: boolean;
|
||||
|
||||
/**
|
||||
* Flag signaling if the device status is visible or not.
|
||||
*/
|
||||
@@ -54,6 +51,11 @@ interface IProps {
|
||||
*/
|
||||
hasJoinByPhoneButton: boolean;
|
||||
|
||||
/**
|
||||
* Flag signaling if the display name is visible or not.
|
||||
*/
|
||||
isDisplayNameVisible: boolean;
|
||||
|
||||
/**
|
||||
* Joins the current meeting.
|
||||
*/
|
||||
@@ -193,9 +195,9 @@ const useStyles = makeStyles()(theme => {
|
||||
});
|
||||
|
||||
const Prejoin = ({
|
||||
canEditDisplayName,
|
||||
deviceStatusVisible,
|
||||
hasJoinByPhoneButton,
|
||||
isDisplayNameVisible,
|
||||
joinConference,
|
||||
joinConferenceWithoutAudio,
|
||||
joiningInProgress,
|
||||
@@ -212,10 +214,16 @@ const Prejoin = ({
|
||||
updateSettings: dispatchUpdateSettings,
|
||||
videoTrack
|
||||
}: IProps) => {
|
||||
const showDisplayNameField = useRef(canEditDisplayName || showErrorOnJoin);
|
||||
const showDisplayNameField = useMemo(
|
||||
() => isDisplayNameVisible && !readOnlyName,
|
||||
[ isDisplayNameVisible, readOnlyName ]);
|
||||
const showErrorOnField = useMemo(
|
||||
() => showDisplayNameField && showErrorOnJoin,
|
||||
[ showDisplayNameField, showErrorOnJoin ]);
|
||||
const [ showJoinByPhoneButtons, setShowJoinByPhoneButtons ] = useState(false);
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
/**
|
||||
* Handler for the join button.
|
||||
@@ -225,6 +233,11 @@ const Prejoin = ({
|
||||
*/
|
||||
const onJoinButtonClick = () => {
|
||||
if (showErrorOnJoin) {
|
||||
dispatch(openDisplayNamePrompt({
|
||||
onPostSubmit: joinConference,
|
||||
validateInput: hasDisplayName
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
joinConference();
|
||||
@@ -374,12 +387,12 @@ const Prejoin = ({
|
||||
<div
|
||||
className = { classes.inputContainer }
|
||||
data-testid = 'prejoin.screen'>
|
||||
{showDisplayNameField.current ? (<Input
|
||||
{showDisplayNameField ? (<Input
|
||||
accessibilityLabel = { t('dialog.enterDisplayName') }
|
||||
autoComplete = { 'name' }
|
||||
autoFocus = { true }
|
||||
className = { classes.input }
|
||||
error = { showErrorOnJoin }
|
||||
error = { showErrorOnField }
|
||||
id = 'premeeting-name-input'
|
||||
onChange = { setName }
|
||||
onKeyPress = { showUnsafeRoomWarning && !unsafeRoomConsent ? undefined : onInputKeyPress }
|
||||
@@ -393,11 +406,11 @@ const Prejoin = ({
|
||||
displayName = { name }
|
||||
participantId = { participantId }
|
||||
size = { 72 } />
|
||||
<div className = { classes.avatarName }>{name}</div>
|
||||
{isDisplayNameVisible && <div className = { classes.avatarName }>{name}</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showErrorOnJoin && <div
|
||||
{showErrorOnField && <div
|
||||
className = { classes.error }
|
||||
data-testid = 'prejoin.errorMessage'>{t('prejoin.errorMissingName')}</div>}
|
||||
|
||||
@@ -406,7 +419,7 @@ const Prejoin = ({
|
||||
content = { hasExtraJoinButtons && <div className = { classes.dropdownButtons }>
|
||||
{extraButtonsToRender.map(({ key, ...rest }) => (
|
||||
<Button
|
||||
disabled = { joiningInProgress }
|
||||
disabled = { joiningInProgress || showErrorOnField }
|
||||
fullWidth = { true }
|
||||
key = { key }
|
||||
type = { BUTTON_TYPES.SECONDARY }
|
||||
@@ -422,7 +435,9 @@ const Prejoin = ({
|
||||
ariaDropDownLabel = { t('prejoin.joinWithoutAudio') }
|
||||
ariaLabel = { t('prejoin.joinMeeting') }
|
||||
ariaPressed = { showJoinByPhoneButtons }
|
||||
disabled = { joiningInProgress || (showUnsafeRoomWarning && !unsafeRoomConsent) }
|
||||
disabled = { joiningInProgress
|
||||
|| (showUnsafeRoomWarning && !unsafeRoomConsent)
|
||||
|| showErrorOnField }
|
||||
hasOptions = { hasExtraJoinButtons }
|
||||
onClick = { onJoinButtonClick }
|
||||
onOptionsClick = { onOptionsClick }
|
||||
@@ -460,9 +475,9 @@ function mapStateToProps(state: IReduxState) {
|
||||
const { unsafeRoomConsent } = state['features/base/premeeting'];
|
||||
|
||||
return {
|
||||
canEditDisplayName: isPrejoinDisplayNameVisible(state),
|
||||
deviceStatusVisible: isDeviceStatusVisible(state),
|
||||
hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state),
|
||||
isDisplayNameVisible: isPrejoinDisplayNameVisible(state),
|
||||
joiningInProgress,
|
||||
name,
|
||||
participantId,
|
||||
|
||||
@@ -795,3 +795,11 @@ export function getCountryFromDialCodeText(text: string) {
|
||||
|| null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the display name is present.
|
||||
*
|
||||
* @param {string} value - The display name.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const hasDisplayName = (value: string): boolean => Boolean(value) && value.trim() !== '';
|
||||
|
||||
Reference in New Issue
Block a user