fix(AddPeopleDialog): Improve contact invite form

- Disables the invite buttons while invites are ongoing
- Adds a keyboard shortcut (Enter) to send out invites
- Closes AddPeopleDialog upon successful invites sent
- Fixes the SecurityDialog closing when trying to set E2EE key via Enter shortcut
- Removes superfluous separator from SecurityDialog
This commit is contained in:
Mihai Uscat 2020-06-24 15:12:23 +03:00 committed by Zoltan Bettenbuk
parent 0494200383
commit 093254d948
9 changed files with 104 additions and 21 deletions

View File

@ -208,6 +208,12 @@
padding: 8px 16px;
background: #0376DA;
}
&.disabled {
& > a {
pointer-events: none;
}
}
}
&.stream {

View File

@ -21,15 +21,11 @@
font-size: 14px;
color: #6FB1EA;
}
&>a+a {
margin-left: 24px;
}
}
}
}
&> :first-child:not(:last-child) {
& > :first-child:not(:last-child) {
margin-right: 24px;
}

View File

@ -109,6 +109,7 @@ class E2EESection extends Component<Props, State> {
disabled = { !editing }
name = 'e2eeKey'
onChange = { this._onKeyChange }
onKeyDown = { this._onKeyDown }
placeholder = { t('dialog.e2eeNoKey') }
ref = { this.fieldRef }
type = 'password'
@ -137,6 +138,20 @@ class E2EESection extends Component<Props, State> {
this.setState({ key: event.target.value.trim() });
}
_onKeyDown: (Object) => void;
/**
* Handler for the keydown event on the form, preventing the closing of the dialog.
*
* @param {Object} event - The DOM event triggered by keydown events.
* @returns {void}
*/
_onKeyDown(event) {
if (event.key === 'Enter') {
event.preventDefault();
}
}
_onSet: () => void;
/**

View File

@ -41,6 +41,11 @@ export const REMOVE_PENDING_INVITE_REQUESTS
*/
export const SET_CALLEE_INFO_VISIBLE = 'SET_CALLEE_INFO_VISIBLE';
/**
* The type of redux action to signal that the {@code AddPeopleDialog} should close.
*/
export const HIDE_ADD_PEOPLE_DIALOG = 'HIDE_ADD_PEOPLE_DIALOG';
/**
* The type of the action which signals an error occurred while requesting dial-
* in numbers.

View File

@ -9,6 +9,7 @@ import { inviteVideoRooms } from '../videosipgw';
import {
ADD_PENDING_INVITE_REQUEST,
BEGIN_ADD_PEOPLE,
HIDE_ADD_PEOPLE_DIALOG,
REMOVE_PENDING_INVITE_REQUESTS,
SET_CALLEE_INFO_VISIBLE,
UPDATE_DIAL_IN_NUMBERS_FAILED,
@ -36,6 +37,20 @@ export function beginAddPeople() {
};
}
/**
* Creates a (redux) action to signal that the {@code AddPeopleDialog}
* should close.
*
* @returns {{
* type: HIDE_ADD_PEOPLE_DIALOG
* }}
*/
export function hideAddPeopleDialog() {
return {
type: HIDE_ADD_PEOPLE_DIALOG
};
}
/**
* Invites (i.e. Sends invites to) an array of invitees (which may be a

View File

@ -10,6 +10,7 @@ import { Icon, IconPhone } from '../../../../base/icons';
import { getLocalParticipant } from '../../../../base/participants';
import { MultiSelectAutocomplete } from '../../../../base/react';
import { connect } from '../../../../base/redux';
import { hideAddPeopleDialog } from '../../../actions';
import AbstractAddPeopleDialog, {
type Props as AbstractProps,
type State,
@ -72,6 +73,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
this._parseQueryResults = this._parseQueryResults.bind(this);
this._setMultiSelectElement = this._setMultiSelectElement.bind(this);
this._renderFooterText = this._renderFooterText.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._resourceClient = {
makeQuery: this._query,
@ -135,7 +137,9 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
}
return (
<div className = 'add-people-form-wrap'>
<div
className = 'add-people-form-wrap'
onKeyDown = { this._onKeyDown }>
{ this._renderErrorMessage() }
<MultiSelectAutocomplete
footer = { footerText }
@ -217,11 +221,30 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
this._multiselect.setSelectedItems(itemsToSelect);
}
} else {
// Do nothing.
this.props.dispatch(hideAddPeopleDialog());
}
});
}
_onKeyDown: (Object) => void;
/**
* Handles 'Enter' key in the form to trigger the invite.
*
* @param {Object} event - The key event.
* @returns {void}
*/
_onKeyDown(event) {
const { inviteItems } = this.state;
if (event.key === 'Enter') {
event.preventDefault();
if (!this._isAddDisabled() && inviteItems.length) {
this._onSubmit();
}
}
}
_parseQueryResults: (?Array<Object>) => Array<Object>;
/**
@ -380,7 +403,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
}
return (
<div className = 'invite-more-dialog invite-buttons'>
<div className = { `invite-more-dialog invite-buttons${this._isAddDisabled() ? ' disabled' : ''}` }>
<a
className = 'invite-more-dialog invite-buttons-cancel'
onClick = { this._onClearItems }>

View File

@ -1,9 +1,9 @@
// @flow
import { openDialog } from '../base/dialog';
import { hideDialog, openDialog } from '../base/dialog';
import { MiddlewareRegistry } from '../base/redux';
import { BEGIN_ADD_PEOPLE } from './actionTypes';
import { BEGIN_ADD_PEOPLE, HIDE_ADD_PEOPLE_DIALOG } from './actionTypes';
import { AddPeopleDialog } from './components';
import './middleware.any';
@ -17,6 +17,8 @@ MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case BEGIN_ADD_PEOPLE:
return _beginAddPeople(store, next, action);
case HIDE_ADD_PEOPLE_DIALOG:
return _hideAddPeopleDialog(store, next, action);
}
return next(action);
@ -42,3 +44,22 @@ function _beginAddPeople({ dispatch }, next, action) {
return result;
}
/**
* Notifies the feature invite that the action {@link HIDE_ADD_PEOPLE_DIALOG} is being
* dispatched within a specific redux {@code store}.
*
* @param {Store} store - The redux store in which the specified {@code action}
* is being dispatched.
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
* specified {@code action} to the specified {@code store}.
* @param {Action} action - The redux action {@code HIDE_ADD_PEOPLE_DIALOG} which is
* being dispatched in the specified {@code store}.
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _hideAddPeopleDialog({ dispatch }, next, action) {
dispatch(hideDialog(AddPeopleDialog));
return next(action);
}

View File

@ -86,17 +86,20 @@ class LobbySection extends PureComponent<Props, State> {
}
return (
<div id = 'lobby-section'>
{ t('lobby.enableDialogText') }
<div className = 'control-row'>
<label>
{ t('lobby.toggleLabel') }
</label>
<Switch
onValueChange = { this._onToggleLobby }
value = { this.state.lobbyEnabled } />
<>
<div id = 'lobby-section'>
{ t('lobby.enableDialogText') }
<div className = 'control-row'>
<label>
{ t('lobby.toggleLabel') }
</label>
<Switch
onValueChange = { this._onToggleLobby }
value = { this.state.lobbyEnabled } />
</div>
</div>
</div>
<div className = 'separator-line' />
</>
);
}

View File

@ -89,7 +89,6 @@ function SecurityDialog({
width = { 'small' }>
<div className = 'security-dialog'>
<LobbySection />
<div className = 'separator-line' />
<PasswordSection
canEditPassword = { _canEditPassword }
conference = { _conference }