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:
parent
0494200383
commit
093254d948
|
@ -208,6 +208,12 @@
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background: #0376DA;
|
background: #0376DA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
& > a {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.stream {
|
&.stream {
|
||||||
|
|
|
@ -21,15 +21,11 @@
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #6FB1EA;
|
color: #6FB1EA;
|
||||||
}
|
}
|
||||||
|
|
||||||
&>a+a {
|
|
||||||
margin-left: 24px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&> :first-child:not(:last-child) {
|
& > :first-child:not(:last-child) {
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,7 @@ class E2EESection extends Component<Props, State> {
|
||||||
disabled = { !editing }
|
disabled = { !editing }
|
||||||
name = 'e2eeKey'
|
name = 'e2eeKey'
|
||||||
onChange = { this._onKeyChange }
|
onChange = { this._onKeyChange }
|
||||||
|
onKeyDown = { this._onKeyDown }
|
||||||
placeholder = { t('dialog.e2eeNoKey') }
|
placeholder = { t('dialog.e2eeNoKey') }
|
||||||
ref = { this.fieldRef }
|
ref = { this.fieldRef }
|
||||||
type = 'password'
|
type = 'password'
|
||||||
|
@ -137,6 +138,20 @@ class E2EESection extends Component<Props, State> {
|
||||||
this.setState({ key: event.target.value.trim() });
|
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;
|
_onSet: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,6 +41,11 @@ export const REMOVE_PENDING_INVITE_REQUESTS
|
||||||
*/
|
*/
|
||||||
export const SET_CALLEE_INFO_VISIBLE = 'SET_CALLEE_INFO_VISIBLE';
|
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-
|
* The type of the action which signals an error occurred while requesting dial-
|
||||||
* in numbers.
|
* in numbers.
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { inviteVideoRooms } from '../videosipgw';
|
||||||
import {
|
import {
|
||||||
ADD_PENDING_INVITE_REQUEST,
|
ADD_PENDING_INVITE_REQUEST,
|
||||||
BEGIN_ADD_PEOPLE,
|
BEGIN_ADD_PEOPLE,
|
||||||
|
HIDE_ADD_PEOPLE_DIALOG,
|
||||||
REMOVE_PENDING_INVITE_REQUESTS,
|
REMOVE_PENDING_INVITE_REQUESTS,
|
||||||
SET_CALLEE_INFO_VISIBLE,
|
SET_CALLEE_INFO_VISIBLE,
|
||||||
UPDATE_DIAL_IN_NUMBERS_FAILED,
|
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
|
* Invites (i.e. Sends invites to) an array of invitees (which may be a
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { Icon, IconPhone } from '../../../../base/icons';
|
||||||
import { getLocalParticipant } from '../../../../base/participants';
|
import { getLocalParticipant } from '../../../../base/participants';
|
||||||
import { MultiSelectAutocomplete } from '../../../../base/react';
|
import { MultiSelectAutocomplete } from '../../../../base/react';
|
||||||
import { connect } from '../../../../base/redux';
|
import { connect } from '../../../../base/redux';
|
||||||
|
import { hideAddPeopleDialog } from '../../../actions';
|
||||||
import AbstractAddPeopleDialog, {
|
import AbstractAddPeopleDialog, {
|
||||||
type Props as AbstractProps,
|
type Props as AbstractProps,
|
||||||
type State,
|
type State,
|
||||||
|
@ -72,6 +73,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||||
this._parseQueryResults = this._parseQueryResults.bind(this);
|
this._parseQueryResults = this._parseQueryResults.bind(this);
|
||||||
this._setMultiSelectElement = this._setMultiSelectElement.bind(this);
|
this._setMultiSelectElement = this._setMultiSelectElement.bind(this);
|
||||||
this._renderFooterText = this._renderFooterText.bind(this);
|
this._renderFooterText = this._renderFooterText.bind(this);
|
||||||
|
this._onKeyDown = this._onKeyDown.bind(this);
|
||||||
|
|
||||||
this._resourceClient = {
|
this._resourceClient = {
|
||||||
makeQuery: this._query,
|
makeQuery: this._query,
|
||||||
|
@ -135,7 +137,9 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className = 'add-people-form-wrap'>
|
<div
|
||||||
|
className = 'add-people-form-wrap'
|
||||||
|
onKeyDown = { this._onKeyDown }>
|
||||||
{ this._renderErrorMessage() }
|
{ this._renderErrorMessage() }
|
||||||
<MultiSelectAutocomplete
|
<MultiSelectAutocomplete
|
||||||
footer = { footerText }
|
footer = { footerText }
|
||||||
|
@ -217,11 +221,30 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||||
this._multiselect.setSelectedItems(itemsToSelect);
|
this._multiselect.setSelectedItems(itemsToSelect);
|
||||||
}
|
}
|
||||||
} else {
|
} 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>;
|
_parseQueryResults: (?Array<Object>) => Array<Object>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -380,7 +403,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className = 'invite-more-dialog invite-buttons'>
|
<div className = { `invite-more-dialog invite-buttons${this._isAddDisabled() ? ' disabled' : ''}` }>
|
||||||
<a
|
<a
|
||||||
className = 'invite-more-dialog invite-buttons-cancel'
|
className = 'invite-more-dialog invite-buttons-cancel'
|
||||||
onClick = { this._onClearItems }>
|
onClick = { this._onClearItems }>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { openDialog } from '../base/dialog';
|
import { hideDialog, openDialog } from '../base/dialog';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
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 { AddPeopleDialog } from './components';
|
||||||
import './middleware.any';
|
import './middleware.any';
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case BEGIN_ADD_PEOPLE:
|
case BEGIN_ADD_PEOPLE:
|
||||||
return _beginAddPeople(store, next, action);
|
return _beginAddPeople(store, next, action);
|
||||||
|
case HIDE_ADD_PEOPLE_DIALOG:
|
||||||
|
return _hideAddPeopleDialog(store, next, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
|
@ -42,3 +44,22 @@ function _beginAddPeople({ dispatch }, next, action) {
|
||||||
|
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -86,17 +86,20 @@ class LobbySection extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id = 'lobby-section'>
|
<>
|
||||||
{ t('lobby.enableDialogText') }
|
<div id = 'lobby-section'>
|
||||||
<div className = 'control-row'>
|
{ t('lobby.enableDialogText') }
|
||||||
<label>
|
<div className = 'control-row'>
|
||||||
{ t('lobby.toggleLabel') }
|
<label>
|
||||||
</label>
|
{ t('lobby.toggleLabel') }
|
||||||
<Switch
|
</label>
|
||||||
onValueChange = { this._onToggleLobby }
|
<Switch
|
||||||
value = { this.state.lobbyEnabled } />
|
onValueChange = { this._onToggleLobby }
|
||||||
|
value = { this.state.lobbyEnabled } />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className = 'separator-line' />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,6 @@ function SecurityDialog({
|
||||||
width = { 'small' }>
|
width = { 'small' }>
|
||||||
<div className = 'security-dialog'>
|
<div className = 'security-dialog'>
|
||||||
<LobbySection />
|
<LobbySection />
|
||||||
<div className = 'separator-line' />
|
|
||||||
<PasswordSection
|
<PasswordSection
|
||||||
canEditPassword = { _canEditPassword }
|
canEditPassword = { _canEditPassword }
|
||||||
conference = { _conference }
|
conference = { _conference }
|
||||||
|
|
Loading…
Reference in New Issue