ref(security-dialog) Use new input component for password (#11946)

pull/11965/head jitsi-meet_7626
Robert Pintilii 3 years ago committed by GitHub
parent 873cdbb404
commit 002d0fed42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      css/modals/invite/_info.scss
  2. 10
      css/modals/security/_security.scss
  3. 12
      react/features/base/ui/components/web/Input.tsx
  4. 100
      react/features/security/components/security-dialog/web/PasswordForm.tsx
  5. 46
      react/features/security/components/security-dialog/web/PasswordSection.tsx
  6. 21
      react/features/security/components/security-dialog/web/SecurityDialog.tsx
  7. 0
      react/features/toolbox/constants.ts

@ -38,19 +38,6 @@
color: #fff; color: #fff;
} }
.info-password-input {
width: 100%;
background-color: #0E1624;
border-radius: 3px;
border: 2px solid #202B3D;
color: inherit;
padding-left: 0;
}
.info-password-input:focus ,
.info-password-input:active {
border: 2px solid #B8C7E0;
}
.info-password-local { .info-password-local {
user-select: text; user-select: text;
} }

@ -13,12 +13,14 @@
} }
.password { .password {
align-items: center; align-items: flex-start;
display: flex; display: flex;
justify-content: space-between; justify-content: flex-start;
margin-top: 15px; margin-top: 15px;
flex-direction: column;
&-actions { &-actions {
margin-top: 10px;
a { a {
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
@ -26,8 +28,8 @@
color: #6FB1EA; color: #6FB1EA;
} }
& > :first-child:not(:last-child) { & > :first-child:not(:last-child) {
margin-right: 24px; margin-right: 24px;
} }
} }
} }

@ -10,10 +10,14 @@ import { Theme } from '../../../ui/types';
import { InputProps } from '../types'; import { InputProps } from '../types';
interface IInputProps extends InputProps { interface IInputProps extends InputProps {
accessibilityLabel?: string;
autoFocus?: boolean;
bottomLabel?: string; bottomLabel?: string;
className?: string; className?: string;
id?: string; id?: string;
maxLength?: number;
name?: string; name?: string;
onKeyPress?: (e: React.KeyboardEvent) => void;
type?: 'text' | 'email' | 'number' | 'password'; type?: 'text' | 'email' | 'number' | 'password';
} }
@ -115,6 +119,8 @@ const useStyles = makeStyles((theme: Theme) => {
}); });
const Input = ({ const Input = ({
accessibilityLabel,
autoFocus,
bottomLabel, bottomLabel,
className, className,
clearable = false, clearable = false,
@ -123,8 +129,10 @@ const Input = ({
icon, icon,
id, id,
label, label,
maxLength,
name, name,
onChange, onChange,
onKeyPress,
placeholder, placeholder,
type = 'text', type = 'text',
value value
@ -145,12 +153,16 @@ const Input = ({
size = { 20 } size = { 20 }
src = { icon } />} src = { icon } />}
<input <input
aria-label = { accessibilityLabel }
autoFocus = { autoFocus }
className = { clsx(styles.input, isMobile && 'is-mobile', className = { clsx(styles.input, isMobile && 'is-mobile',
error && 'error', clearable && styles.clearableInput, icon && styles.iconInput) } error && 'error', clearable && styles.clearableInput, icon && styles.iconInput) }
disabled = { disabled } disabled = { disabled }
{ ...(id ? { id } : {}) } { ...(id ? { id } : {}) }
maxLength = { maxLength }
name = { name } name = { name }
onChange = { handleChange } onChange = { handleChange }
onKeyPress = { onKeyPress }
placeholder = { placeholder } placeholder = { placeholder }
type = { type } type = { type }
value = { value } /> value = { value } />

@ -1,14 +1,16 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { translate } from '../../../../base/i18n'; import { translate } from '../../../../base/i18n/functions';
import Input from '../../../../base/ui/components/web/Input';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import { LOCKED_LOCALLY } from '../../../../room-lock'; import { LOCKED_LOCALLY } from '../../../../room-lock';
/** /**
* The type of the React {@code Component} props of {@link PasswordForm}. * The type of the React {@code Component} props of {@link PasswordForm}.
*/ */
type Props = { interface Props extends WithTranslation {
/** /**
* Whether or not to show the password editing field. * Whether or not to show the password editing field.
@ -35,13 +37,8 @@ type Props = {
/** /**
* The number of digits to be used in the password. * The number of digits to be used in the password.
*/ */
passwordNumberOfDigits: boolean, passwordNumberOfDigits?: number
}
/**
* Invoked to obtain translated strings.
*/
t: Function
};
/** /**
* The type of the React {@code Component} state of {@link PasswordForm}. * The type of the React {@code Component} state of {@link PasswordForm}.
@ -65,7 +62,7 @@ class PasswordForm extends Component<Props, State> {
* *
* @inheritdoc * @inheritdoc
*/ */
static getDerivedStateFromProps(props, state) { static getDerivedStateFromProps(props: Props, state: State) {
return { return {
enteredPassword: props.editEnabled ? state.enteredPassword : '' enteredPassword: props.editEnabled ? state.enteredPassword : ''
}; };
@ -96,21 +93,42 @@ class PasswordForm extends Component<Props, State> {
* @returns {ReactElement} * @returns {ReactElement}
*/ */
render() { render() {
const { t } = this.props;
return ( return (
<div className = 'info-password'> <div className = 'info-password'>
<span className = 'info-label'> {this._renderPassword()}
{ t('info.password') } {this._renderPasswordField()}
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-password-field info-value'>
{ this._renderPasswordField() }
</span>
</div> </div>
); );
} }
/** .........
* Renders the password if there is any.
*
* @returns {ReactElement}
*/
_renderPassword() {
const { locked, t } = this.props;
return locked && <>
<span className = 'info-label'>
{t('info.password')}
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-password-field info-value'>
{locked === LOCKED_LOCALLY ? (
<div className = 'info-password-local'>
{this.props.password}
</div>
) : (
<div className = 'info-password-remote'>
{this.props.t('passwordSetRemotely')}
</div>
) }
{this._renderPasswordField()}
</span>
</>;
}
/** /**
* Returns a ReactElement for showing the current state of the password or * Returns a ReactElement for showing the current state of the password or
* for editing the current password. * for editing the current password.
@ -120,7 +138,7 @@ class PasswordForm extends Component<Props, State> {
*/ */
_renderPasswordField() { _renderPasswordField() {
if (this.props.editEnabled) { if (this.props.editEnabled) {
let placeHolderText; let placeHolderText = this.props.t('dialog.password');
if (this.props.passwordNumberOfDigits) { if (this.props.passwordNumberOfDigits) {
placeHolderText = this.props.t('passwordDigitsOnly', { placeHolderText = this.props.t('passwordDigitsOnly', {
@ -130,55 +148,31 @@ class PasswordForm extends Component<Props, State> {
return ( return (
<div <div
className = 'info-password-form'> className = 'info-password-form'>
<input <Input
aria-label = { this.props.t('info.addPassword') } accessibilityLabel = { this.props.t('info.addPassword') }
autoFocus = { true } autoFocus = { true }
className = 'info-password-input' id = 'info-password-input'
maxLength = { this.props.passwordNumberOfDigits } maxLength = { this.props.passwordNumberOfDigits }
onChange = { this._onEnteredPasswordChange } onChange = { this._onEnteredPasswordChange }
onKeyPress = { this._onKeyPress } onKeyPress = { this._onKeyPress }
placeholder = { placeHolderText } placeholder = { placeHolderText }
spellCheck = { 'false' }
type = 'text'
value = { this.state.enteredPassword } /> value = { this.state.enteredPassword } />
</div> </div>
); );
} else if (this.props.locked === LOCKED_LOCALLY) {
return (
<div className = 'info-password-local'>
{ this.props.password }
</div>
);
} else if (this.props.locked) {
return (
<div className = 'info-password-remote'>
{ this.props.t('passwordSetRemotely') }
</div>
);
} }
return (
<div className = 'info-password-none'>
{ this.props.t('info.noPassword') }
</div>
);
} }
_onEnteredPasswordChange: (Object) => void;
/** /**
* Updates the internal state of entered password. * Updates the internal state of entered password.
* *
* @param {Object} event - DOM Event for value change. * @param {string} value - DOM Event for value change.
* @private * @private
* @returns {void} * @returns {void}
*/ */
_onEnteredPasswordChange(event) { _onEnteredPasswordChange(value: string) {
this.setState({ enteredPassword: event.target.value }); this.setState({ enteredPassword: value });
} }
_onKeyPress: (Object) => void;
/** /**
* Stops the the EnterKey for propagation in order to prevent the dialog * Stops the the EnterKey for propagation in order to prevent the dialog
* to close. * to close.
@ -187,7 +181,7 @@ class PasswordForm extends Component<Props, State> {
* @private * @private
* @returns {void} * @returns {void}
*/ */
_onKeyPress(event) { _onKeyPress(event: React.KeyboardEvent) {
if (event.key === 'Enter') { if (event.key === 'Enter') {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();

@ -1,24 +1,24 @@
// @flow /* eslint-disable react/jsx-no-bind */
/* eslint-disable react/no-multi-comp, react/jsx-no-bind */
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { WithTranslation } from 'react-i18next';
import { translate } from '../../../../base/i18n'; import { translate } from '../../../../base/i18n/functions';
import { copyText } from '../../../../base/util'; import { copyText } from '../../../../base/util/helpers';
import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants'; import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
// @ts-ignore
import PasswordForm from './PasswordForm'; import PasswordForm from './PasswordForm';
import { NotifyClick } from './SecurityDialog';
const DIGITS_ONLY = /^\d+$/; const DIGITS_ONLY = /^\d+$/;
const KEY = 'add-passcode'; const KEY = 'add-passcode';
type Props = { interface Props extends WithTranslation {
/** /**
* Toolbar buttons which have their click exposed through the API. * Toolbar buttons which have their click exposed through the API.
*/ */
buttonsWithNotifyClick: Array<string | Object>, buttonsWithNotifyClick: Array<string | NotifyClick>,
/** /**
* Whether or not the current user can modify the current password. * Whether or not the current user can modify the current password.
@ -29,7 +29,7 @@ type Props = {
* The JitsiConference for which to display a lock state and change the * The JitsiConference for which to display a lock state and change the
* password. * password.
*/ */
conference: Object, conference: any,
/** /**
* The value for how the conference is locked (or undefined if not locked) * The value for how the conference is locked (or undefined if not locked)
@ -50,7 +50,7 @@ type Props = {
/** /**
* The number of digits to be used in the password. * The number of digits to be used in the password.
*/ */
passwordNumberOfDigits: ?number, passwordNumberOfDigits?: number,
/** /**
* Action that sets the conference password. * Action that sets the conference password.
@ -60,15 +60,10 @@ type Props = {
/** /**
* Method that sets whether the password editing is enabled or not. * Method that sets whether the password editing is enabled or not.
*/ */
setPasswordEditEnabled: Function, setPasswordEditEnabled: Function
}
/**
* Invoked to obtain translated strings.
*/
t: Function
};
declare var APP: Object; declare let APP: any;
/** /**
* Component that handles the password manipulation from the invite dialog. * Component that handles the password manipulation from the invite dialog.
@ -87,7 +82,7 @@ function PasswordSection({
setPasswordEditEnabled, setPasswordEditEnabled,
t }: Props) { t }: Props) {
const formRef: Object = useRef(null); const formRef = useRef<HTMLDivElement>(null);
/** /**
* Callback invoked to set a password on the current JitsiConference. * Callback invoked to set a password on the current JitsiConference.
@ -97,7 +92,7 @@ function PasswordSection({
* @private * @private
* @returns {void} * @returns {void}
*/ */
function onPasswordSubmit(enteredPassword) { function onPasswordSubmit(enteredPassword: string) {
if (enteredPassword && passwordNumberOfDigits && !DIGITS_ONLY.test(enteredPassword)) { if (enteredPassword && passwordNumberOfDigits && !DIGITS_ONLY.test(enteredPassword)) {
// Don't set the password. // Don't set the password.
return; return;
@ -121,7 +116,7 @@ function PasswordSection({
let notifyMode; let notifyMode;
const notify = buttonsWithNotifyClick.find( const notify = buttonsWithNotifyClick.find(
(btn: string | Object) => (btn: string | NotifyClick) =>
(typeof btn === 'string' && btn === KEY) (typeof btn === 'string' && btn === KEY)
|| (typeof btn === 'object' && btn.key === KEY) || (typeof btn === 'object' && btn.key === KEY)
); );
@ -147,6 +142,7 @@ function PasswordSection({
*/ */
function onPasswordSave() { function onPasswordSave() {
if (formRef.current) { if (formRef.current) {
// @ts-ignore
const { value } = formRef.current.querySelector('div > input'); const { value } = formRef.current.querySelector('div > input');
if (value) { if (value) {
@ -182,7 +178,7 @@ function PasswordSection({
* @private * @private
* @returns {void} * @returns {void}
*/ */
function onTogglePasswordEditStateKeyPressHandler(e) { function onTogglePasswordEditStateKeyPressHandler(e: React.KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
onTogglePasswordEditState(); onTogglePasswordEditState();
@ -197,7 +193,7 @@ function PasswordSection({
* @private * @private
* @returns {void} * @returns {void}
*/ */
function onPasswordSaveKeyPressHandler(e) { function onPasswordSaveKeyPressHandler(e: React.KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
onPasswordSave(); onPasswordSave();
@ -212,7 +208,7 @@ function PasswordSection({
* @private * @private
* @returns {void} * @returns {void}
*/ */
function onPasswordRemoveKeyPressHandler(e) { function onPasswordRemoveKeyPressHandler(e: React.KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
onPasswordRemove(); onPasswordRemove();
@ -227,7 +223,7 @@ function PasswordSection({
* @private * @private
* @returns {void} * @returns {void}
*/ */
function onPasswordCopyKeyPressHandler(e) { function onPasswordCopyKeyPressHandler(e: React.KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
onPasswordCopy(); onPasswordCopy();

@ -1,22 +1,31 @@
// @flow /* eslint-disable lines-around-comment */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
// @ts-ignore
import { setPassword as setPass } from '../../../../base/conference'; import { setPassword as setPass } from '../../../../base/conference';
// @ts-ignore
import { Dialog } from '../../../../base/dialog'; import { Dialog } from '../../../../base/dialog';
// @ts-ignore
import { isLocalParticipantModerator } from '../../../../base/participants'; import { isLocalParticipantModerator } from '../../../../base/participants';
import { connect } from '../../../../base/redux'; // @ts-ignore
import { E2EESection } from '../../../../e2ee/components'; import { E2EESection } from '../../../../e2ee/components';
// @ts-ignore
import { LobbySection } from '../../../../lobby'; import { LobbySection } from '../../../../lobby';
import PasswordSection from './PasswordSection'; import PasswordSection from './PasswordSection';
export interface NotifyClick {
key: string;
preventExecution: boolean;
}
type Props = { type Props = {
/** /**
* Toolbar buttons which have their click exposed through the API. * Toolbar buttons which have their click exposed through the API.
*/ */
_buttonsWithNotifyClick: Array<string | Object>, _buttonsWithNotifyClick: Array<string | NotifyClick>,
/** /**
* Whether or not the current user can modify the current password. * Whether or not the current user can modify the current password.
@ -43,7 +52,7 @@ type Props = {
/** /**
* The number of digits to be used in the password. * The number of digits to be used in the password.
*/ */
_passwordNumberOfDigits: ?number, _passwordNumberOfDigits?: number,
/** /**
* Indicates whether e2ee will be displayed or not. * Indicates whether e2ee will be displayed or not.
@ -117,7 +126,7 @@ function SecurityDialog({
* @private * @private
* @returns {Props} * @returns {Props}
*/ */
function mapStateToProps(state) { function mapStateToProps(state: any) {
const { const {
conference, conference,
e2eeSupported, e2eeSupported,
Loading…
Cancel
Save