Merge pull request #1379 from jitsi/base-react-dialogs-2
Password required dialog (web&native) and native room lock using basic react dialogs.
This commit is contained in:
commit
3daae94bca
|
@ -385,10 +385,6 @@ class ConferenceConnector {
|
||||||
logger.error('CONFERENCE FAILED:', err, ...params);
|
logger.error('CONFERENCE FAILED:', err, ...params);
|
||||||
APP.UI.hideRingOverLay();
|
APP.UI.hideRingOverLay();
|
||||||
switch (err) {
|
switch (err) {
|
||||||
// room is locked by the password
|
|
||||||
case ConferenceErrors.PASSWORD_REQUIRED:
|
|
||||||
APP.UI.emitEvent(UIEvents.PASSWORD_REQUIRED);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ConferenceErrors.CONNECTION_ERROR:
|
case ConferenceErrors.CONNECTION_ERROR:
|
||||||
{
|
{
|
||||||
|
|
|
@ -47,31 +47,8 @@ class Invite {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.conference.on(ConferenceEvents.CONFERENCE_JOINED, () => {
|
|
||||||
let roomLocker = this.getRoomLocker();
|
|
||||||
roomLocker.hideRequirePasswordDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
APP.UI.addListener( UIEvents.INVITE_CLICKED,
|
APP.UI.addListener( UIEvents.INVITE_CLICKED,
|
||||||
() => { this.openLinkDialog(); });
|
() => { this.openLinkDialog(); });
|
||||||
|
|
||||||
APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
|
|
||||||
() => {
|
|
||||||
let roomLocker = this.getRoomLocker();
|
|
||||||
this.setLockedFromElsewhere(true);
|
|
||||||
roomLocker.requirePassword().then(() => {
|
|
||||||
let pass = roomLocker.password;
|
|
||||||
// we received that password is required, but user is trying
|
|
||||||
// anyway to login without a password, mark room as not
|
|
||||||
// locked in case he succeeds (maybe someone removed the
|
|
||||||
// password meanwhile), if it is still locked another
|
|
||||||
// password required will be received and the room again
|
|
||||||
// will be marked as locked.
|
|
||||||
if (!pass)
|
|
||||||
this.setLockedFromElsewhere(false);
|
|
||||||
this.conference.join(pass);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,162 +0,0 @@
|
||||||
/* global APP */
|
|
||||||
|
|
||||||
import UIUtil from '../util/UIUtil';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show dialog which asks for required conference password.
|
|
||||||
* @returns {Promise<string>} password or nothing if user canceled
|
|
||||||
*/
|
|
||||||
export default class RequirePasswordDialog {
|
|
||||||
constructor() {
|
|
||||||
this.titleKey = 'dialog.passwordRequired';
|
|
||||||
this.labelKey = 'dialog.passwordLabel';
|
|
||||||
this.errorKey = 'dialog.incorrectPassword';
|
|
||||||
this.errorId = 'passwordRequiredError';
|
|
||||||
this.inputId = 'passwordRequiredInput';
|
|
||||||
this.inputErrorClass = 'error';
|
|
||||||
this.isOpened = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registering dialog listeners
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_registerListeners() {
|
|
||||||
let el = document.getElementById(this.inputId);
|
|
||||||
el.addEventListener('keypress', this._hideError.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method returning dialog body
|
|
||||||
* @returns {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_getBodyMessage() {
|
|
||||||
return (
|
|
||||||
`<div class="form-control">
|
|
||||||
<label class="input-control__label"
|
|
||||||
data-i18n="${this.labelKey}"></label>
|
|
||||||
<input class="input-control__input input-control"
|
|
||||||
name="lockKey" type="text"
|
|
||||||
data-i18n="[placeholder]dialog.password"
|
|
||||||
autofocus id="${this.inputId}">
|
|
||||||
<p class="form-control__hint form-control__hint_error hide"
|
|
||||||
id="${this.errorId}"
|
|
||||||
data-i18n="${this.errorKey}"></p>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asking for a password
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
askForPassword() {
|
|
||||||
if (!this.isOpened) {
|
|
||||||
return this.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.resolve = resolve;
|
|
||||||
this.reject = reject;
|
|
||||||
this._showError();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the dialog
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
open() {
|
|
||||||
let { titleKey } = this;
|
|
||||||
let msgString = this._getBodyMessage();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.resolve = resolve;
|
|
||||||
this.reject = reject;
|
|
||||||
let submitFunction = this._submitFunction.bind(this);
|
|
||||||
let closeFunction = this._closeFunction.bind(this);
|
|
||||||
|
|
||||||
this._dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
|
||||||
titleKey,
|
|
||||||
msgString,
|
|
||||||
leftButtonKey: "dialog.Ok",
|
|
||||||
submitFunction,
|
|
||||||
closeFunction,
|
|
||||||
focus: ':input:first'
|
|
||||||
});
|
|
||||||
|
|
||||||
this._registerListeners();
|
|
||||||
this.isOpened = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit dialog callback
|
|
||||||
* @param e - event
|
|
||||||
* @param v - value
|
|
||||||
* @param m - message
|
|
||||||
* @param f - form
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_submitFunction(e, v, m, f) {
|
|
||||||
e.preventDefault();
|
|
||||||
this._processInput(v, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processing input in dialog
|
|
||||||
* @param v - value
|
|
||||||
* @param f - form
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_processInput(v, f) {
|
|
||||||
if (v && f.lockKey) {
|
|
||||||
this.resolve(UIUtil.escapeHtml(f.lockKey));
|
|
||||||
} else {
|
|
||||||
this.reject(APP.UI.messageHandler.CANCEL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close dialog callback
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_closeFunction(e, v, m, f) {
|
|
||||||
this._processInput(v, f);
|
|
||||||
this._hideError();
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method showing error hint
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_showError() {
|
|
||||||
let className = this.inputErrorClass;
|
|
||||||
let input = document.getElementById(this.inputId);
|
|
||||||
document.getElementById(this.errorId).classList.remove('hide');
|
|
||||||
input.classList.add(className);
|
|
||||||
input.select();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method hiding error hint
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_hideError() {
|
|
||||||
let className = this.inputErrorClass;
|
|
||||||
document.getElementById(this.errorId).classList.add('hide');
|
|
||||||
document.getElementById(this.inputId).classList.remove(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the dialog
|
|
||||||
*/
|
|
||||||
close() {
|
|
||||||
if (this._dialog) {
|
|
||||||
this._dialog.close();
|
|
||||||
}
|
|
||||||
this.isOpened = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
/* global APP, JitsiMeetJS */
|
/* global APP, JitsiMeetJS */
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||||
|
|
||||||
import RequirePasswordDialog from './RequirePasswordDialog';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show notification that user cannot set password for the conference
|
* Show notification that user cannot set password for the conference
|
||||||
* because server doesn't support that.
|
* because server doesn't support that.
|
||||||
|
@ -33,7 +31,6 @@ const ConferenceErrors = JitsiMeetJS.errors.conference;
|
||||||
*/
|
*/
|
||||||
export default function createRoomLocker (room) {
|
export default function createRoomLocker (room) {
|
||||||
let password;
|
let password;
|
||||||
let requirePasswordDialog = new RequirePasswordDialog();
|
|
||||||
/**
|
/**
|
||||||
* If the room was locked from someone other than us, we indicate it with
|
* If the room was locked from someone other than us, we indicate it with
|
||||||
* this property in order to have correct roomLocker state of isLocked.
|
* this property in order to have correct roomLocker state of isLocked.
|
||||||
|
@ -102,31 +99,5 @@ export default function createRoomLocker (room) {
|
||||||
password = null;
|
password = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Asks user for required conference password.
|
|
||||||
*/
|
|
||||||
requirePassword () {
|
|
||||||
return requirePasswordDialog.askForPassword().then(
|
|
||||||
newPass => { password = newPass; }
|
|
||||||
).catch(
|
|
||||||
reason => {
|
|
||||||
// user canceled, no pass was entered.
|
|
||||||
// clear, as if we use the same instance several times
|
|
||||||
// pass stays between attempts
|
|
||||||
password = null;
|
|
||||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
|
||||||
logger.error(reason);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides require password dialog
|
|
||||||
*/
|
|
||||||
hideRequirePasswordDialog() {
|
|
||||||
if (requirePasswordDialog.isOpened) {
|
|
||||||
requirePasswordDialog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@atlassian/aui": "6.0.6",
|
"@atlassian/aui": "6.0.6",
|
||||||
"@atlaskit/button": "1.0.3",
|
"@atlaskit/button": "1.0.3",
|
||||||
"@atlaskit/button-group": "1.0.0",
|
"@atlaskit/button-group": "1.0.0",
|
||||||
|
"@atlaskit/field-text": "2.0.3",
|
||||||
"@atlaskit/modal-dialog": "1.2.4",
|
"@atlaskit/modal-dialog": "1.2.4",
|
||||||
"@atlaskit/tabs": "1.2.5",
|
"@atlaskit/tabs": "1.2.5",
|
||||||
"async": "0.9.0",
|
"async": "0.9.0",
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { appInit } from '../actions';
|
import { appInit } from '../actions';
|
||||||
import { AbstractApp } from './AbstractApp';
|
import { AbstractApp } from './AbstractApp';
|
||||||
|
|
||||||
|
import '../../room-lock';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root application component.
|
* Root application component.
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,10 +6,8 @@ import { DialogContainer } from '../../base/dialog';
|
||||||
import { Container } from '../../base/react';
|
import { Container } from '../../base/react';
|
||||||
import { FilmStrip } from '../../film-strip';
|
import { FilmStrip } from '../../film-strip';
|
||||||
import { LargeVideo } from '../../large-video';
|
import { LargeVideo } from '../../large-video';
|
||||||
import { RoomLockPrompt } from '../../room-lock';
|
|
||||||
import { Toolbar } from '../../toolbar';
|
import { Toolbar } from '../../toolbar';
|
||||||
|
|
||||||
import PasswordRequiredPrompt from './PasswordRequiredPrompt';
|
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,23 +28,6 @@ class Conference extends Component {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
|
||||||
* The indicator which determines whether a password is required to join
|
|
||||||
* the conference and has not been provided yet.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @type {JitsiConference}
|
|
||||||
*/
|
|
||||||
_passwordRequired: React.PropTypes.object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether the user has requested to lock
|
|
||||||
* the conference/room.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @type {JitsiConference}
|
|
||||||
*/
|
|
||||||
_roomLockRequested: React.PropTypes.object,
|
|
||||||
dispatch: React.PropTypes.func
|
dispatch: React.PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,9 +109,6 @@ class Conference extends Component {
|
||||||
|
|
||||||
<DialogContainer />
|
<DialogContainer />
|
||||||
|
|
||||||
{
|
|
||||||
this._renderPrompt()
|
|
||||||
}
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -164,56 +142,6 @@ class Conference extends Component {
|
||||||
this._setToolbarTimeout(toolbarVisible);
|
this._setToolbarTimeout(toolbarVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a prompt if a password is required to join the conference.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderPasswordRequiredPrompt() {
|
|
||||||
const required = this.props._passwordRequired;
|
|
||||||
|
|
||||||
if (required) {
|
|
||||||
return (
|
|
||||||
<PasswordRequiredPrompt conference = { required } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a prompt if necessary such as when a password is required to join
|
|
||||||
* the conference or the user has requested to lock the conference/room.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderPrompt() {
|
|
||||||
return (
|
|
||||||
this._renderPasswordRequiredPrompt()
|
|
||||||
|| this._renderRoomLockPrompt()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a prompt if the user has requested to lock the conference/room.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderRoomLockPrompt() {
|
|
||||||
const requested = this.props._roomLockRequested;
|
|
||||||
|
|
||||||
if (requested) {
|
|
||||||
return (
|
|
||||||
<RoomLockPrompt conference = { requested } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the default toolbar timeout.
|
* Triggers the default toolbar timeout.
|
||||||
*
|
*
|
||||||
|
@ -231,35 +159,4 @@ class Conference extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export default reactReduxConnect()(Conference);
|
||||||
* Maps (parts of) the Redux state to the associated Conference's props.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _passwordRequired: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether a password is required to join
|
|
||||||
* the conference and has not been provided yet.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @type {JitsiConference}
|
|
||||||
*/
|
|
||||||
_passwordRequired: state['features/base/conference'].passwordRequired,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether the user has requested to lock
|
|
||||||
* the conference/room.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @type {JitsiConference}
|
|
||||||
*/
|
|
||||||
_roomLockRequested: state['features/room-lock'].requested
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default reactReduxConnect(_mapStateToProps)(Conference);
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { Symbol } from '../base/react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of Redux action which begins a (user) request to lock a specific
|
|
||||||
* JitsiConference.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: BEGIN_ROOM_LOCK_REQUEST,
|
|
||||||
* conference: JitsiConference
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const BEGIN_ROOM_LOCK_REQUEST = Symbol('BEGIN_ROOM_LOCK_REQUEST');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of Redux action which end a (user) request to lock a specific
|
|
||||||
* JitsiConference.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: END_ROOM_LOCK_REQUEST,
|
|
||||||
* conference: JitsiConference,
|
|
||||||
* password: string
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const END_ROOM_LOCK_REQUEST = Symbol('END_ROOM_LOCK_REQUEST');
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { setPassword } from '../base/conference';
|
import { setPassword } from '../base/conference';
|
||||||
|
import { hideDialog, openDialog } from '../base/dialog';
|
||||||
import { BEGIN_ROOM_LOCK_REQUEST, END_ROOM_LOCK_REQUEST } from './actionTypes';
|
import { PasswordRequiredPrompt, RoomLockPrompt } from './components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begins a (user) request to lock a specific conference/room.
|
* Begins a (user) request to lock a specific conference/room.
|
||||||
|
@ -19,10 +19,7 @@ export function beginRoomLockRequest(conference) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conference) {
|
if (conference) {
|
||||||
dispatch({
|
dispatch(openDialog(RoomLockPrompt, { conference }));
|
||||||
type: BEGIN_ROOM_LOCK_REQUEST,
|
|
||||||
conference
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -43,13 +40,25 @@ export function endRoomLockRequest(conference, password) {
|
||||||
? dispatch(setPassword(conference, conference.lock, password))
|
? dispatch(setPassword(conference, conference.lock, password))
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
const endRoomLockRequest_ = () => {
|
const endRoomLockRequest_ = () => {
|
||||||
dispatch({
|
dispatch(hideDialog());
|
||||||
type: END_ROOM_LOCK_REQUEST,
|
|
||||||
conference,
|
|
||||||
password
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setPassword_.then(endRoomLockRequest_, endRoomLockRequest_);
|
setPassword_.then(endRoomLockRequest_, endRoomLockRequest_);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a request to enter password for a specific conference/room.
|
||||||
|
*
|
||||||
|
* @param {JitsiConference} conference - The JitsiConference
|
||||||
|
* requesting password.
|
||||||
|
* @protected
|
||||||
|
* @returns {{
|
||||||
|
* type: OPEN_DIALOG,
|
||||||
|
* component: Component,
|
||||||
|
* props: React.PropTypes
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function _showPasswordDialog(conference) {
|
||||||
|
return openDialog(PasswordRequiredPrompt, { conference });
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Prompt from 'react-native-prompt';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { Dialog } from '../../base/dialog';
|
||||||
|
|
||||||
import { setPassword } from '../../base/conference';
|
import { setPassword } from '../../base/conference';
|
||||||
import { translate } from '../../base/i18n';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React Component which prompts the user when a password is
|
* Implements a React Component which prompts the user when a password is
|
||||||
|
@ -22,15 +21,7 @@ class PasswordRequiredPrompt extends Component {
|
||||||
* @type {JitsiConference}
|
* @type {JitsiConference}
|
||||||
*/
|
*/
|
||||||
conference: React.PropTypes.object,
|
conference: React.PropTypes.object,
|
||||||
dispatch: React.PropTypes.func,
|
dispatch: React.PropTypes.func
|
||||||
|
|
||||||
/**
|
|
||||||
* The function to translate human-readable text.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
t: React.PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,15 +45,13 @@ class PasswordRequiredPrompt extends Component {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Prompt
|
<Dialog
|
||||||
|
bodyKey = 'dialog.passwordLabel'
|
||||||
onCancel = { this._onCancel }
|
onCancel = { this._onCancel }
|
||||||
onSubmit = { this._onSubmit }
|
onSubmit = { this._onSubmit }
|
||||||
placeholder = { t('dialog.passwordLabel') }
|
titleKey = 'dialog.passwordRequired' />
|
||||||
title = { t('dialog.passwordRequired') }
|
|
||||||
visible = { true } />
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,14 +59,14 @@ class PasswordRequiredPrompt extends Component {
|
||||||
* Notifies this prompt that it has been dismissed by cancel.
|
* Notifies this prompt that it has been dismissed by cancel.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {boolean} whether to hide dialog.
|
||||||
*/
|
*/
|
||||||
_onCancel() {
|
_onCancel() {
|
||||||
// XXX The user has canceled this prompt for a password so we are to
|
// XXX The user has canceled this prompt for a password so we are to
|
||||||
// attempt joining the conference without a password. If the conference
|
// attempt joining the conference without a password. If the conference
|
||||||
// still requires a password to join, the user will be prompted again
|
// still requires a password to join, the user will be prompted again
|
||||||
// later.
|
// later.
|
||||||
this._onSubmit(undefined);
|
return this._onSubmit(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,13 +75,15 @@ class PasswordRequiredPrompt extends Component {
|
||||||
*
|
*
|
||||||
* @param {string} value - The submitted value.
|
* @param {string} value - The submitted value.
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {boolean} whether to hide dialog.
|
||||||
*/
|
*/
|
||||||
_onSubmit(value) {
|
_onSubmit(value) {
|
||||||
const conference = this.props.conference;
|
const conference = this.props.conference;
|
||||||
|
|
||||||
this.props.dispatch(setPassword(conference, conference.join, value));
|
this.props.dispatch(setPassword(conference, conference.join, value));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate(connect()(PasswordRequiredPrompt));
|
export default connect()(PasswordRequiredPrompt);
|
|
@ -0,0 +1,127 @@
|
||||||
|
/* global APP */
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import AKFieldText from '@atlaskit/field-text';
|
||||||
|
|
||||||
|
import { setPassword } from '../../base/conference';
|
||||||
|
import { Dialog } from '../../base/dialog';
|
||||||
|
import { translate } from '../../base/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a React Component which prompts the user when a password is
|
||||||
|
* required to join a conference.
|
||||||
|
*/
|
||||||
|
class PasswordRequiredPrompt extends Component {
|
||||||
|
/**
|
||||||
|
* PasswordRequiredPrompt component's property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static propTypes = {
|
||||||
|
/**
|
||||||
|
* The JitsiConference which requires a password.
|
||||||
|
*
|
||||||
|
* @type {JitsiConference}
|
||||||
|
*/
|
||||||
|
conference: React.PropTypes.object,
|
||||||
|
dispatch: React.PropTypes.func,
|
||||||
|
t: React.PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new PasswordRequiredPrompt instance.
|
||||||
|
*
|
||||||
|
* @param {Object} props - The read-only properties with which the new
|
||||||
|
* instance is to be initialized.
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = { password: '' };
|
||||||
|
|
||||||
|
this._onPasswordChanged = this._onPasswordChanged.bind(this);
|
||||||
|
this._onSubmit = this._onSubmit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
isModal = { true }
|
||||||
|
onSubmit = { this._onSubmit }
|
||||||
|
titleKey = 'dialog.passwordRequired'
|
||||||
|
width = 'small'>
|
||||||
|
{ this._renderBody() }
|
||||||
|
</Dialog>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display component in dialog body.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_renderBody() {
|
||||||
|
const { t } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AKFieldText
|
||||||
|
compact = { true }
|
||||||
|
label = { t('dialog.passwordLabel') }
|
||||||
|
name = 'lockKey'
|
||||||
|
onChange = { this._onPasswordChanged }
|
||||||
|
shouldFitContainer = { true }
|
||||||
|
type = 'text'
|
||||||
|
value = { this.state.password } />
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies this dialog that password has changed.
|
||||||
|
*
|
||||||
|
* @param {Object} event - The details of the notification/event.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onPasswordChanged(event) {
|
||||||
|
this.setState({ password: event.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches action to submit value from thus dialog.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onSubmit() {
|
||||||
|
const conference = this.props.conference;
|
||||||
|
|
||||||
|
// we received that password is required, but user is trying
|
||||||
|
// anyway to login without a password, mark room as not
|
||||||
|
// locked in case he succeeds (maybe someone removed the
|
||||||
|
// password meanwhile), if it is still locked another
|
||||||
|
// password required will be received and the room again
|
||||||
|
// will be marked as locked.
|
||||||
|
if (!this.state.password || this.state.password === '') {
|
||||||
|
// XXX temporary solution till we move the whole invite logic
|
||||||
|
// in react
|
||||||
|
APP.conference.invite.setLockedFromElsewhere(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.dispatch(setPassword(
|
||||||
|
conference, conference.join, this.state.password));
|
||||||
|
|
||||||
|
// we have used the password lets clean it
|
||||||
|
this.setState({ password: undefined });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect()(PasswordRequiredPrompt));
|
|
@ -1,8 +1,6 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Prompt from 'react-native-prompt';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { Dialog } from '../../base/dialog';
|
||||||
import { translate } from '../../base/i18n';
|
|
||||||
|
|
||||||
import { endRoomLockRequest } from '../actions';
|
import { endRoomLockRequest } from '../actions';
|
||||||
|
|
||||||
|
@ -23,15 +21,7 @@ class RoomLockPrompt extends Component {
|
||||||
* @type {JitsiConference}
|
* @type {JitsiConference}
|
||||||
*/
|
*/
|
||||||
conference: React.PropTypes.object,
|
conference: React.PropTypes.object,
|
||||||
dispatch: React.PropTypes.func,
|
dispatch: React.PropTypes.func
|
||||||
|
|
||||||
/**
|
|
||||||
* The function to translate human-readable text.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
t: React.PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,15 +45,13 @@ class RoomLockPrompt extends Component {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Prompt
|
<Dialog
|
||||||
|
bodyKey = 'dialog.passwordLabel'
|
||||||
onCancel = { this._onCancel }
|
onCancel = { this._onCancel }
|
||||||
onSubmit = { this._onSubmit }
|
onSubmit = { this._onSubmit }
|
||||||
placeholder = { t('dialog.passwordLabel') }
|
titleKey = 'toolbar.lock' />
|
||||||
title = { t('toolbar.lock') }
|
|
||||||
visible = { true } />
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,12 +59,12 @@ class RoomLockPrompt extends Component {
|
||||||
* Notifies this prompt that it has been dismissed by cancel.
|
* Notifies this prompt that it has been dismissed by cancel.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {boolean} whether to hide the dialog
|
||||||
*/
|
*/
|
||||||
_onCancel() {
|
_onCancel() {
|
||||||
// An undefined password is understood to cancel the request to lock the
|
// An undefined password is understood to cancel the request to lock the
|
||||||
// conference/room.
|
// conference/room.
|
||||||
this._onSubmit(undefined);
|
return this._onSubmit(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,11 +73,16 @@ class RoomLockPrompt extends Component {
|
||||||
*
|
*
|
||||||
* @param {string} value - The submitted value.
|
* @param {string} value - The submitted value.
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {boolean} returns false, we do not want to hide dialog as this
|
||||||
|
* will be handled inside endRoomLockRequest after setting password is
|
||||||
|
* resolved.
|
||||||
*/
|
*/
|
||||||
_onSubmit(value) {
|
_onSubmit(value) {
|
||||||
this.props.dispatch(endRoomLockRequest(this.props.conference, value));
|
this.props.dispatch(endRoomLockRequest(this.props.conference, value));
|
||||||
|
|
||||||
|
// do not hide
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate(connect()(RoomLockPrompt));
|
export default connect()(RoomLockPrompt);
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export { default as RoomLockPrompt } from './RoomLockPrompt';
|
export { default as RoomLockPrompt } from './RoomLockPrompt';
|
||||||
|
export { default as PasswordRequiredPrompt } from './PasswordRequiredPrompt';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
|
||||||
import './reducer';
|
import './middleware';
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* global APP */
|
||||||
|
import JitsiMeetJS from '../base/lib-jitsi-meet';
|
||||||
|
|
||||||
|
import { CONFERENCE_FAILED } from '../base/conference';
|
||||||
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
import { _showPasswordDialog } from './actions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware that captures conference failed and checks for password required
|
||||||
|
* error and requests a dialog for user to enter password.
|
||||||
|
*
|
||||||
|
* @param {Store} store - Redux store.
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case CONFERENCE_FAILED: {
|
||||||
|
const JitsiConferenceErrors = JitsiMeetJS.errors.conference;
|
||||||
|
|
||||||
|
if (action.conference
|
||||||
|
&& JitsiConferenceErrors.PASSWORD_REQUIRED === action.error) {
|
||||||
|
// XXX temporary solution till we move the whole invite
|
||||||
|
// logic in react
|
||||||
|
if (typeof APP !== 'undefined') {
|
||||||
|
APP.conference.invite.setLockedFromElsewhere(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(_showPasswordDialog(action.conference));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
});
|
|
@ -1,33 +0,0 @@
|
||||||
import {
|
|
||||||
CONFERENCE_FAILED,
|
|
||||||
CONFERENCE_JOINED,
|
|
||||||
CONFERENCE_LEFT
|
|
||||||
} from '../base/conference';
|
|
||||||
import { ReducerRegistry, setStateProperty } from '../base/redux';
|
|
||||||
|
|
||||||
import { BEGIN_ROOM_LOCK_REQUEST, END_ROOM_LOCK_REQUEST } from './actionTypes';
|
|
||||||
|
|
||||||
ReducerRegistry.register('features/room-lock', (state = {}, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case BEGIN_ROOM_LOCK_REQUEST:
|
|
||||||
return setStateProperty(state, 'requested', action.conference);
|
|
||||||
|
|
||||||
case CONFERENCE_FAILED:
|
|
||||||
case CONFERENCE_LEFT:
|
|
||||||
case END_ROOM_LOCK_REQUEST: {
|
|
||||||
if (state.requested === action.conference) {
|
|
||||||
return setStateProperty(state, 'requested', undefined);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFERENCE_JOINED: {
|
|
||||||
if (state.requested !== action.conference) {
|
|
||||||
return setStateProperty(state, 'requested', undefined);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
});
|
|
|
@ -150,11 +150,6 @@ export default {
|
||||||
*/
|
*/
|
||||||
DISPLAY_NAME_CHANGED: "UI.display_name_changed",
|
DISPLAY_NAME_CHANGED: "UI.display_name_changed",
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that a password is required for the call.
|
|
||||||
*/
|
|
||||||
PASSWORD_REQUIRED: "UI.password_required",
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show custom popup/tooltip for a specified button.
|
* Show custom popup/tooltip for a specified button.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue