[RN] Support XMPP authentication
This commit is contained in:
parent
141acea194
commit
241dc3b147
|
@ -264,7 +264,7 @@
|
||||||
"removeSharedVideoMsg": "Are you sure you would like to remove your shared video?",
|
"removeSharedVideoMsg": "Are you sure you would like to remove your shared video?",
|
||||||
"alreadySharedVideoMsg": "Another member is already sharing video. This conference allows only one shared video at a time.",
|
"alreadySharedVideoMsg": "Another member is already sharing video. This conference allows only one shared video at a time.",
|
||||||
"WaitingForHost": "Waiting for the host ...",
|
"WaitingForHost": "Waiting for the host ...",
|
||||||
"WaitForHostMsg": "The conference '__room__' has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
|
"WaitForHostMsg": "The conference <b>__room__ </b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
|
||||||
"IamHost": "I am the host",
|
"IamHost": "I am the host",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Submit": "Submit",
|
"Submit": "Submit",
|
||||||
|
|
|
@ -140,37 +140,40 @@ function initJWTTokenListener(room) {
|
||||||
* @param {string} [lockPassword] password to use if the conference is locked
|
* @param {string} [lockPassword] password to use if the conference is locked
|
||||||
*/
|
*/
|
||||||
function doXmppAuth(room, lockPassword) {
|
function doXmppAuth(room, lockPassword) {
|
||||||
const loginDialog = LoginDialog.showAuthDialog(function (id, password) {
|
const loginDialog = LoginDialog.showAuthDialog(
|
||||||
const authConnection = room.createAuthenticationConnection();
|
/* successCallback */ (id, password) => {
|
||||||
|
room.authenticateAndUpgradeRole({
|
||||||
authConnection.authenticateAndUpgradeRole({
|
|
||||||
id,
|
id,
|
||||||
password,
|
password,
|
||||||
roomPassword: lockPassword,
|
roomPassword: lockPassword,
|
||||||
onLoginSuccessful: () => { /* Called when XMPP login succeeds */
|
|
||||||
|
/** Called when the XMPP login succeeds. */
|
||||||
|
onLoginSuccessful() {
|
||||||
loginDialog.displayConnectionStatus(
|
loginDialog.displayConnectionStatus(
|
||||||
'connection.FETCH_SESSION_ID');
|
'connection.FETCH_SESSION_ID');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(
|
||||||
|
/* onFulfilled */ () => {
|
||||||
loginDialog.displayConnectionStatus(
|
loginDialog.displayConnectionStatus(
|
||||||
'connection.GOT_SESSION_ID');
|
'connection.GOT_SESSION_ID');
|
||||||
loginDialog.close();
|
loginDialog.close();
|
||||||
})
|
},
|
||||||
.catch(error => {
|
/* onRejected */ error => {
|
||||||
logger.error('authenticateAndUpgradeRole failed', error);
|
logger.error('authenticateAndUpgradeRole failed', error);
|
||||||
if (error.authenticationError) {
|
|
||||||
|
const { authenticationError, connectionError } = error;
|
||||||
|
|
||||||
|
if (authenticationError) {
|
||||||
loginDialog.displayError(
|
loginDialog.displayError(
|
||||||
'connection.GET_SESSION_ID_ERROR', {
|
'connection.GET_SESSION_ID_ERROR',
|
||||||
msg: error.authenticationError
|
{ msg: authenticationError });
|
||||||
});
|
} else if (connectionError) {
|
||||||
} else {
|
loginDialog.displayError(connectionError);
|
||||||
loginDialog.displayError(error.connectionError);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, function () {
|
},
|
||||||
loginDialog.close();
|
/* cancelCallback */ () => loginDialog.close());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* global $, APP, config, JitsiMeetJS */
|
/* global $, APP, config, JitsiMeetJS */
|
||||||
|
|
||||||
import { toJid } from '../../../react/features/base/connection';
|
import { toJid } from '../../../react/features/base/connection';
|
||||||
|
|
||||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||||
|
@ -196,34 +197,38 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show notification that authentication is required
|
* Shows a notification that authentication is required to create the
|
||||||
* to create the conference, so he should authenticate or wait for a host.
|
* conference, so the local participant should authenticate or wait for a
|
||||||
* @param {string} roomName name of the conference
|
* host.
|
||||||
* @param {function} onAuthNow callback to invoke if
|
*
|
||||||
* user want to authenticate.
|
* @param {string} room - The name of the conference.
|
||||||
|
* @param {function} onAuthNow - The callback to invoke if the local
|
||||||
|
* participant wants to authenticate.
|
||||||
* @returns dialog
|
* @returns dialog
|
||||||
*/
|
*/
|
||||||
showAuthRequiredDialog: function (roomName, onAuthNow) {
|
showAuthRequiredDialog(room, onAuthNow) {
|
||||||
var msg = APP.translation.generateTranslationHTML(
|
const msg = APP.translation.generateTranslationHTML(
|
||||||
"dialog.WaitForHostMsg", {room: roomName}
|
'[html]dialog.WaitForHostMsg',
|
||||||
|
{ room }
|
||||||
);
|
);
|
||||||
|
const buttonTxt = APP.translation.generateTranslationHTML(
|
||||||
var buttonTxt = APP.translation.generateTranslationHTML(
|
'dialog.IamHost'
|
||||||
"dialog.IamHost"
|
|
||||||
);
|
);
|
||||||
var buttons = [{title: buttonTxt, value: "authNow"}];
|
const buttons = [{
|
||||||
|
title: buttonTxt,
|
||||||
|
value: 'authNow'
|
||||||
|
}];
|
||||||
|
|
||||||
return APP.UI.messageHandler.openDialog(
|
return APP.UI.messageHandler.openDialog(
|
||||||
"dialog.WaitingForHost",
|
'dialog.WaitingForHost',
|
||||||
msg,
|
msg,
|
||||||
true,
|
true,
|
||||||
buttons,
|
buttons,
|
||||||
function (e, submitValue) {
|
(e, submitValue) => {
|
||||||
|
// Do not close the dialog yet.
|
||||||
// Do not close the dialog yet
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// Open login popup
|
// Open login popup.
|
||||||
if (submitValue === 'authNow') {
|
if (submitValue === 'authNow') {
|
||||||
onAuthNow();
|
onAuthNow();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
||||||
import { Linking } from 'react-native';
|
import { Linking } from 'react-native';
|
||||||
|
|
||||||
import '../../analytics';
|
import '../../analytics';
|
||||||
|
import '../../authentication';
|
||||||
import { Platform } from '../../base/react';
|
import { Platform } from '../../base/react';
|
||||||
import '../../mobile/audio-mode';
|
import '../../mobile/audio-mode';
|
||||||
import '../../mobile/background';
|
import '../../mobile/background';
|
||||||
|
@ -15,7 +16,6 @@ import '../../mobile/proximity';
|
||||||
import '../../mobile/wake-lock';
|
import '../../mobile/wake-lock';
|
||||||
|
|
||||||
import { AbstractApp } from './AbstractApp';
|
import { AbstractApp } from './AbstractApp';
|
||||||
import '../../authentication';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root application component.
|
* Root application component.
|
||||||
|
|
|
@ -28,38 +28,31 @@ export const CANCEL_WAIT_FOR_OWNER = Symbol('CANCEL_WAIT_FOR_OWNER');
|
||||||
*/
|
*/
|
||||||
export const STOP_WAIT_FOR_OWNER = Symbol('STOP_WAIT_FOR_OWNER');
|
export const STOP_WAIT_FOR_OWNER = Symbol('STOP_WAIT_FOR_OWNER');
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of (redux) action which signals that the process of authenticating
|
|
||||||
* and upgrading the current conference user's role has been started.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: UPGRADE_ROLE_STARTED,
|
|
||||||
* authConnection: JitsiAuthConnection
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const UPGRADE_ROLE_STARTED = Symbol('UPGRADE_ROLE_STARTED');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of (redux) action which informs that the authentication and role
|
* The type of (redux) action which informs that the authentication and role
|
||||||
* upgrade process has been completed successfully.
|
* upgrade process has finished either with success or with a specific error.
|
||||||
|
* If <tt>error</tt> is <tt>undefined</tt>, then the process succeeded;
|
||||||
|
* otherwise, it failed. Refer to
|
||||||
|
* {@link JitsiConference#authenticateAndUpgradeRole} in lib-jitsi-meet for the
|
||||||
|
* error details.
|
||||||
*
|
*
|
||||||
* {
|
* {
|
||||||
* type: UPGRADE_ROLE_SUCCESS
|
* type: UPGRADE_ROLE_FINISHED,
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const UPGRADE_ROLE_SUCCESS = Symbol('UPGRADE_ROLE_SUCCESS');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of (redux) action which informs that the authentication and role
|
|
||||||
* upgrade process has failed with an error. Check the docs of
|
|
||||||
* {@link JitsiAuthConnection} for more details about the error structure.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: UPGRADE_ROLE_SUCCESS,
|
|
||||||
* error: Object
|
* error: Object
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const UPGRADE_ROLE_FAILED = Symbol('UPGRADE_ROLE_FAILED');
|
export const UPGRADE_ROLE_FINISHED = Symbol('UPGRADE_ROLE_FINISHED');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of (redux) action which signals that the process of authenticating
|
||||||
|
* and upgrading the local participant's role has been started.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: UPGRADE_ROLE_STARTED,
|
||||||
|
* thenableWithCancel: Object
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const UPGRADE_ROLE_STARTED = Symbol('UPGRADE_ROLE_STARTED');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of (redux) action that sets delayed handler which will check if
|
* The type of (redux) action that sets delayed handler which will check if
|
||||||
|
|
|
@ -1,52 +1,56 @@
|
||||||
import { openDialog } from '../base/dialog/actions';
|
/* @flow */
|
||||||
import { checkIfCanJoin } from '../base/conference/actions';
|
|
||||||
|
import { checkIfCanJoin } from '../base/conference';
|
||||||
|
import { openDialog } from '../base/dialog';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CANCEL_LOGIN,
|
CANCEL_LOGIN,
|
||||||
CANCEL_WAIT_FOR_OWNER,
|
CANCEL_WAIT_FOR_OWNER,
|
||||||
STOP_WAIT_FOR_OWNER,
|
STOP_WAIT_FOR_OWNER,
|
||||||
UPGRADE_ROLE_FAILED,
|
UPGRADE_ROLE_FINISHED,
|
||||||
UPGRADE_ROLE_STARTED,
|
UPGRADE_ROLE_STARTED,
|
||||||
UPGRADE_ROLE_SUCCESS,
|
|
||||||
WAIT_FOR_OWNER
|
WAIT_FOR_OWNER
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import { LoginDialog, WaitForOwnerDialog } from './components';
|
import { LoginDialog, WaitForOwnerDialog } from './components';
|
||||||
|
|
||||||
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates new {@link JitsiAuthConnection} and uses it to authenticate and
|
* Initiates authenticating and upgrading the role of the local participant to
|
||||||
* upgrade role of the current conference user to moderator which will allow to
|
* moderator which will allow to create and join a new conference on an XMPP
|
||||||
* create and join new conference on XMPP password + guest access configuration.
|
* password + guest access configuration. Refer to {@link LoginDialog} for more
|
||||||
* See {@link LoginDialog} description for more info.
|
* info.
|
||||||
*
|
*
|
||||||
* @param {string} id - XMPP user's id eg. user@domain.com.
|
* @param {string} id - The XMPP user's ID (e.g. user@domain.com).
|
||||||
* @param {string} userPassword - The user's password.
|
* @param {string} password - The XMPP user's password.
|
||||||
* @param {JitsiConference} conference - The conference for which user's role
|
* @param {JitsiConference} conference - The conference for which the local
|
||||||
* will be upgraded.
|
* participant's role will be upgraded.
|
||||||
* @returns {function({dispatch: Function, getState: Function})}
|
* @returns {function({ dispatch: Dispatch, getState: Function })}
|
||||||
*/
|
*/
|
||||||
export function authenticateAndUpgradeRole(id, userPassword, conference) {
|
export function authenticateAndUpgradeRole(
|
||||||
return (dispatch, getState) => {
|
id: string,
|
||||||
const authConnection = conference.createAuthenticationConnection();
|
password: string,
|
||||||
|
conference: Object) {
|
||||||
dispatch(_upgradeRoleStarted(authConnection));
|
return (dispatch: Dispatch, getState: Function) => {
|
||||||
|
|
||||||
const { password: roomPassword }
|
const { password: roomPassword }
|
||||||
= getState()['features/base/conference'];
|
= getState()['features/base/conference'];
|
||||||
|
const process
|
||||||
authConnection.authenticateAndUpgradeRole({
|
= conference.authenticateAndUpgradeRole({
|
||||||
id,
|
id,
|
||||||
password: userPassword,
|
password,
|
||||||
roomPassword
|
roomPassword
|
||||||
})
|
});
|
||||||
.then(() => {
|
|
||||||
dispatch(_upgradeRoleSuccess());
|
dispatch(_upgradeRoleStarted(process));
|
||||||
})
|
process.then(
|
||||||
.catch(error => {
|
/* onFulfilled */ () => dispatch(_upgradeRoleFinished()),
|
||||||
// Lack of error means the operation was canceled, so no need to log
|
/* onRejected */ error => {
|
||||||
// that on error level.
|
// The lack of an error signals a cancellation.
|
||||||
if (error.error) {
|
if (error.authenticationError || error.connectionError) {
|
||||||
console.error('upgradeRoleFailed', error);
|
logger.error('authenticateAndUpgradeRole failed', error);
|
||||||
}
|
}
|
||||||
dispatch(_upgradeRoleFailed(error));
|
|
||||||
|
dispatch(_upgradeRoleFinished(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -78,118 +82,77 @@ export function cancelWaitForOwner() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops waiting for conference owner and clears any pending timeout.
|
* Opens {@link LoginDialog} which will ask to enter username and password
|
||||||
|
* for the current conference.
|
||||||
|
*
|
||||||
|
* @protected
|
||||||
|
* @returns {Action}
|
||||||
|
*/
|
||||||
|
export function _openLoginDialog() {
|
||||||
|
return openDialog(LoginDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens {@link WaitForOnwerDialog}.
|
||||||
|
*
|
||||||
|
* @protected
|
||||||
|
* @returns {Action}
|
||||||
|
*/
|
||||||
|
export function _openWaitForOwnerDialog() {
|
||||||
|
return openDialog(WaitForOwnerDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops waiting for the conference owner.
|
||||||
*
|
*
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: STOP_WAIT_FOR_OWNER
|
* type: STOP_WAIT_FOR_OWNER
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function clearWaitForOwnerTimeout() {
|
export function stopWaitForOwner() {
|
||||||
return {
|
return {
|
||||||
type: STOP_WAIT_FOR_OWNER
|
type: STOP_WAIT_FOR_OWNER
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a delayed "wait for owner" handler function.
|
* Signals that the process of authenticating and upgrading the local
|
||||||
*
|
* participant's role has finished either with success or with a specific error.
|
||||||
* @param {Function} handler - The "wait for owner" handler function.
|
|
||||||
* @param {number} waitMs - The delay in milliseconds.
|
|
||||||
*
|
*
|
||||||
|
* @param {Object} error - If <tt>undefined</tt>, then the process of
|
||||||
|
* authenticating and upgrading the local participant's role has succeeded;
|
||||||
|
* otherwise, it has failed with the specified error. Refer to
|
||||||
|
* {@link JitsiConference#authenticateAndUpgradeRole} in lib-jitsi-meet for the
|
||||||
|
* error details.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: WAIT_FOR_OWNER,
|
* type: UPGRADE_ROLE_FINISHED,
|
||||||
* handler: Function,
|
|
||||||
* timeoutMs: number
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _setWaitForOwnerTimeout(handler, waitMs) {
|
|
||||||
return {
|
|
||||||
type: WAIT_FOR_OWNER,
|
|
||||||
handler,
|
|
||||||
timeoutMs: waitMs
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays {@link LoginDialog} which will ask to enter username and password
|
|
||||||
* for the current conference.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @returns {{
|
|
||||||
* type: OPEN_DIALOG,
|
|
||||||
* component: LoginDialog,
|
|
||||||
* props: React.PropTypes
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function _showLoginDialog() {
|
|
||||||
return openDialog(LoginDialog, { });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays {@link WaitForOnwerDialog}.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @returns {{
|
|
||||||
* type: OPEN_DIALOG,
|
|
||||||
* component: WaitForOwnerDialog,
|
|
||||||
* props: React.PropTypes
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function _showWaitForOwnerDialog() {
|
|
||||||
return openDialog(WaitForOwnerDialog, { });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits an error which occurred during {@link authenticateAndUpgradeRole}.
|
|
||||||
*
|
|
||||||
* @param {Object} error - Check the docs of {@link JitsiAuthConnection} in
|
|
||||||
* lib-jitsi-meet for more details about the error's structure.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* type: UPGRADE_ROLE_FAILED,
|
|
||||||
* error: Object
|
* error: Object
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _upgradeRoleFailed(error) {
|
function _upgradeRoleFinished(error) {
|
||||||
return {
|
return {
|
||||||
type: UPGRADE_ROLE_FAILED,
|
type: UPGRADE_ROLE_FINISHED,
|
||||||
error
|
error
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals that the role upgrade process has been started using given
|
* Signals that a process of authenticating and upgrading the local
|
||||||
* {@link JitsiAuthConnection} instance.
|
* participant's role has started.
|
||||||
*
|
|
||||||
* @param {JitsiAuthConnection} authenticationConnection - The authentication
|
|
||||||
* connection instance that can be used to cancel the process.
|
|
||||||
*
|
*
|
||||||
|
* @param {Object} thenableWithCancel - The process of authenticating and
|
||||||
|
* upgrading the local participant's role.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* type: UPGRADE_ROLE_STARTED,
|
* type: UPGRADE_ROLE_STARTED,
|
||||||
* authConnection: JitsiAuthConnection
|
* thenableWithCancel: Object
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _upgradeRoleStarted(authenticationConnection) {
|
function _upgradeRoleStarted(thenableWithCancel) {
|
||||||
return {
|
return {
|
||||||
type: UPGRADE_ROLE_STARTED,
|
type: UPGRADE_ROLE_STARTED,
|
||||||
authConnection: authenticationConnection
|
thenableWithCancel
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals that the role upgrade process has been completed successfully.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* type: UPGRADE_ROLE_SUCCESS
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _upgradeRoleSuccess() {
|
|
||||||
return {
|
|
||||||
type: UPGRADE_ROLE_SUCCESS
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,13 +161,13 @@ function _upgradeRoleSuccess() {
|
||||||
* start the process of "waiting for the owner" by periodically trying to join
|
* start the process of "waiting for the owner" by periodically trying to join
|
||||||
* the room every five seconds.
|
* the room every five seconds.
|
||||||
*
|
*
|
||||||
* @returns {function({ dispatch: Function})}
|
* @returns {function({ dispatch: Dispatch })}
|
||||||
*/
|
*/
|
||||||
export function waitForOwner() {
|
export function waitForOwner() {
|
||||||
return dispatch => {
|
return (dispatch: Dispatch) =>
|
||||||
dispatch(
|
dispatch({
|
||||||
_setWaitForOwnerTimeout(
|
type: WAIT_FOR_OWNER,
|
||||||
() => dispatch(checkIfCanJoin()),
|
handler: () => dispatch(checkIfCanJoin()),
|
||||||
5000));
|
timeoutMs: 5000
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,14 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { Text, TextInput, View } from 'react-native';
|
||||||
import { connect as reduxConnect } from 'react-redux';
|
import { connect as reduxConnect } from 'react-redux';
|
||||||
import {
|
|
||||||
Button,
|
import { connect, toJid } from '../../base/connection';
|
||||||
Modal,
|
import { Dialog } from '../../base/dialog';
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import {
|
|
||||||
authenticateAndUpgradeRole,
|
|
||||||
cancelLogin
|
|
||||||
} from '../actions';
|
|
||||||
import {
|
|
||||||
connect,
|
|
||||||
toJid
|
|
||||||
} from '../../base/connection';
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { JitsiConnectionErrors } from '../../base/lib-jitsi-meet';
|
import { JitsiConnectionErrors } from '../../base/lib-jitsi-meet';
|
||||||
|
|
||||||
|
import { authenticateAndUpgradeRole, cancelLogin } from '../actions';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,15 +28,15 @@ import styles from './styles';
|
||||||
* yet, Jicofo will not allow to start new conference. This will trigger
|
* yet, Jicofo will not allow to start new conference. This will trigger
|
||||||
* 'CONFERENCE_FAILED' action with JitsiConferenceErrors.AUTHENTICATION_REQUIRED
|
* 'CONFERENCE_FAILED' action with JitsiConferenceErrors.AUTHENTICATION_REQUIRED
|
||||||
* error and 'authRequired' value of 'features/base/conference' will hold
|
* error and 'authRequired' value of 'features/base/conference' will hold
|
||||||
* the {@link JitsiConference} instance. If user decides to authenticate a new
|
* the {@link JitsiConference} instance. If user decides to authenticate, a
|
||||||
* {@link JitsiAuthConnection} will be created from which separate XMPP
|
* new/separate XMPP connection is established and authentication is performed.
|
||||||
* connection is established and authentication is performed. In case it
|
* In case it succeeds, Jicofo will assign new session ID which then can be used
|
||||||
* succeeds Jicofo will assign new session ID which then can be used from
|
* from the anonymous domain connection to create and join the room. This part
|
||||||
* the anonymous domain connection to create and join the room. This part is
|
* is done by {@link JitsiConference#authenticateAndUpgradeRole} in
|
||||||
* done by {@link JitsiAuthConnection} from lib-jitsi-meet.
|
* lib-jitsi-meet.
|
||||||
*
|
*
|
||||||
* See https://github.com/jitsi/jicofo#secure-domain for configuration
|
* See {@link https://github.com/jitsi/jicofo#secure-domain} for a description
|
||||||
* parameters description.
|
* of the configuration parameters.
|
||||||
*/
|
*/
|
||||||
class LoginDialog extends Component {
|
class LoginDialog extends Component {
|
||||||
/**
|
/**
|
||||||
|
@ -57,37 +49,37 @@ class LoginDialog extends Component {
|
||||||
* {@link JitsiConference} that needs authentication - will hold a valid
|
* {@link JitsiConference} that needs authentication - will hold a valid
|
||||||
* value in XMPP login + guest access mode.
|
* value in XMPP login + guest access mode.
|
||||||
*/
|
*/
|
||||||
conference: React.PropTypes.object,
|
_conference: PropTypes.object,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
configHosts: React.PropTypes.object,
|
_configHosts: PropTypes.object,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the dialog should display "connecting" status message.
|
* Indicates if the dialog should display "connecting" status message.
|
||||||
*/
|
*/
|
||||||
connecting: React.PropTypes.bool,
|
_connecting: PropTypes.bool,
|
||||||
|
|
||||||
/**
|
|
||||||
* Redux store dispatch method.
|
|
||||||
*/
|
|
||||||
dispatch: React.PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error which occurred during login/authentication.
|
* The error which occurred during login/authentication.
|
||||||
*/
|
*/
|
||||||
error: React.PropTypes.string,
|
_error: PropTypes.string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any extra details about the error provided by lib-jitsi-meet.
|
* Any extra details about the error provided by lib-jitsi-meet.
|
||||||
*/
|
*/
|
||||||
errorDetails: React.PropTypes.string,
|
_errorDetails: PropTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redux store dispatch method.
|
||||||
|
*/
|
||||||
|
dispatch: PropTypes.func,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to obtain translated strings.
|
* Invoked to obtain translated strings.
|
||||||
*/
|
*/
|
||||||
t: React.PropTypes.func
|
t: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,16 +91,16 @@ class LoginDialog extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once for every instance.
|
|
||||||
this._onCancel = this._onCancel.bind(this);
|
|
||||||
this._onLogin = this._onLogin.bind(this);
|
|
||||||
this._onUsernameChange = this._onUsernameChange.bind(this);
|
|
||||||
this._onPasswordChange = this._onPasswordChange.bind(this);
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
|
this._onCancel = this._onCancel.bind(this);
|
||||||
|
this._onLogin = this._onLogin.bind(this);
|
||||||
|
this._onPasswordChange = this._onPasswordChange.bind(this);
|
||||||
|
this._onUsernameChange = this._onUsernameChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,9 +111,9 @@ class LoginDialog extends Component {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
error,
|
_connecting: connecting,
|
||||||
errorDetails,
|
_error: error,
|
||||||
connecting,
|
_errorDetails: errorDetails,
|
||||||
t
|
t
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -132,43 +124,38 @@ class LoginDialog extends Component {
|
||||||
messageKey = 'dialog.incorrectPassword';
|
messageKey = 'dialog.incorrectPassword';
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
messageKey = 'dialog.connectErrorWithMsg';
|
messageKey = 'dialog.connectErrorWithMsg';
|
||||||
|
|
||||||
messageOptions.msg = `${error} ${errorDetails}`;
|
messageOptions.msg = `${error} ${errorDetails}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Dialog
|
||||||
onRequestClose = { this._onCancel }
|
okDisabled = { connecting }
|
||||||
style = { styles.outerArea }
|
onCancel = { this._onCancel }
|
||||||
transparent = { true } >
|
onSubmit = { this._onLogin }
|
||||||
<View style = { styles.dialogBox }>
|
titleKey = 'dialog.passwordRequired'>
|
||||||
<Text>Username:</Text>
|
<View style = { styles.loginDialog }>
|
||||||
<TextInput
|
<TextInput
|
||||||
onChangeText = { this._onUsernameChange }
|
onChangeText = { this._onUsernameChange }
|
||||||
placeholder = { 'user@domain.com' }
|
placeholder = { 'user@domain.com' }
|
||||||
style = { styles.textInput }
|
style = { styles.loginDialogTextInput }
|
||||||
value = { this.state.username } />
|
value = { this.state.username } />
|
||||||
<Text>Password:</Text>
|
|
||||||
<TextInput
|
<TextInput
|
||||||
onChangeText = { this._onPasswordChange }
|
onChangeText = { this._onPasswordChange }
|
||||||
placeholder = { t('dialog.userPassword') }
|
placeholder = { t('dialog.userPassword') }
|
||||||
secureTextEntry = { true }
|
secureTextEntry = { true }
|
||||||
style = { styles.textInput }
|
style = { styles.loginDialogTextInput }
|
||||||
value = { this.state.password } />
|
value = { this.state.password } />
|
||||||
<Text>
|
<Text style = { styles.loginDialogText }>
|
||||||
{error ? t(messageKey, messageOptions) : ''}
|
{
|
||||||
{connecting && !error
|
error
|
||||||
? t('connection.CONNECTING') : ''}
|
? t(messageKey, messageOptions)
|
||||||
|
: connecting
|
||||||
|
? t('connection.CONNECTING')
|
||||||
|
: ''
|
||||||
|
}
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
|
||||||
disabled = { connecting }
|
|
||||||
onPress = { this._onLogin }
|
|
||||||
title = { t('dialog.Ok') } />
|
|
||||||
<Button
|
|
||||||
onPress = { this._onCancel }
|
|
||||||
title = { t('dialog.Cancel') } />
|
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,9 +203,9 @@ class LoginDialog extends Component {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onLogin() {
|
_onLogin() {
|
||||||
const conference = this.props.conference;
|
const { _conference: conference } = this.props;
|
||||||
const { username, password } = this.state;
|
const { username, password } = this.state;
|
||||||
const jid = toJid(username, this.props.configHosts);
|
const jid = toJid(username, this.props._configHosts);
|
||||||
|
|
||||||
// If there's a conference it means that the connection has succeeded,
|
// If there's a conference it means that the connection has succeeded,
|
||||||
// but authentication is required in order to join the room.
|
// but authentication is required in order to join the room.
|
||||||
|
@ -238,42 +225,45 @@ class LoginDialog extends Component {
|
||||||
* @param {Object} state - The Redux state.
|
* @param {Object} state - The Redux state.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* configHosts: Object,
|
* _conference: JitsiConference,
|
||||||
* connecting: boolean,
|
* _configHosts: Object,
|
||||||
* error: string,
|
* _connecting: boolean,
|
||||||
* errorDetails: string,
|
* _error: string,
|
||||||
* conference: JitsiConference
|
* _errorDetails: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
|
const {
|
||||||
|
upgradeRoleError,
|
||||||
|
upgradeRoleInProgress
|
||||||
|
} = state['features/authentication'];
|
||||||
|
const { authRequired } = state['features/base/conference'];
|
||||||
const { hosts: configHosts } = state['features/base/config'];
|
const { hosts: configHosts } = state['features/base/config'];
|
||||||
const {
|
const {
|
||||||
connecting,
|
connecting,
|
||||||
error: connectionError,
|
error: connectionError,
|
||||||
errorMessage: connectionErrorMessage
|
errorMessage: connectionErrorMessage
|
||||||
} = state['features/base/connection'];
|
} = state['features/base/connection'];
|
||||||
const {
|
|
||||||
authRequired
|
|
||||||
} = state['features/base/conference'];
|
|
||||||
const {
|
|
||||||
upgradeRoleError,
|
|
||||||
upgradeRoleInProgress
|
|
||||||
} = state['features/authentication'];
|
|
||||||
|
|
||||||
const error
|
let error;
|
||||||
= connectionError
|
let errorDetails;
|
||||||
|| (upgradeRoleError
|
|
||||||
&& (upgradeRoleError.connectionError
|
if (connectionError) {
|
||||||
|| upgradeRoleError.authenticationError));
|
error = connectionError;
|
||||||
|
errorDetails = connectionErrorMessage;
|
||||||
|
} else if (upgradeRoleError) {
|
||||||
|
error
|
||||||
|
= upgradeRoleError.connectionError
|
||||||
|
|| upgradeRoleError.authenticationError;
|
||||||
|
errorDetails = upgradeRoleError.message;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
configHosts,
|
_conference: authRequired,
|
||||||
connecting: Boolean(connecting) || Boolean(upgradeRoleInProgress),
|
_configHosts: configHosts,
|
||||||
error,
|
_connecting: Boolean(connecting) || Boolean(upgradeRoleInProgress),
|
||||||
errorDetails:
|
_error: error,
|
||||||
(connectionError && connectionErrorMessage)
|
_errorDetails: errorDetails
|
||||||
|| (upgradeRoleError && upgradeRoleError.message),
|
|
||||||
conference: authRequired
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Button, Modal, Text, View } from 'react-native';
|
|
||||||
|
|
||||||
|
import { Dialog } from '../../base/dialog';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { _showLoginDialog, cancelWaitForOwner } from '../actions';
|
|
||||||
|
import { cancelWaitForOwner, _openLoginDialog } from '../actions';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,20 +22,20 @@ class WaitForOwnerDialog extends Component {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
|
||||||
* Redux store dispatch function.
|
|
||||||
*/
|
|
||||||
dispatch: React.PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the conference room (without the domain part).
|
* The name of the conference room (without the domain part).
|
||||||
*/
|
*/
|
||||||
roomName: React.PropTypes.string,
|
_room: PropTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redux store dispatch function.
|
||||||
|
*/
|
||||||
|
dispatch: PropTypes.func,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to obtain translated strings.
|
* Invoked to obtain translated strings.
|
||||||
*/
|
*/
|
||||||
t: React.PropTypes.func
|
t: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,8 +47,9 @@ class WaitForOwnerDialog extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._onLogin = this._onLogin.bind(this);
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._onCancel = this._onCancel.bind(this);
|
this._onCancel = this._onCancel.bind(this);
|
||||||
|
this._onLogin = this._onLogin.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,43 +60,25 @@ class WaitForOwnerDialog extends Component {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
roomName,
|
_room: room,
|
||||||
t
|
t
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Dialog
|
||||||
onRequestClose = { this._onCancel }
|
okTitleKey = { 'dialog.IamHost' }
|
||||||
style = { styles.outerArea }
|
onCancel = { this._onCancel }
|
||||||
transparent = { true } >
|
onSubmit = { this._onLogin }
|
||||||
<View style = { styles.dialogBox } >
|
titleKey = 'dialog.WaitingForHost'>
|
||||||
<Text>
|
<Text style = { styles.waitForOwnerDialog }>
|
||||||
{ t(
|
{
|
||||||
'dialog.WaitForHostMsg',
|
this.renderHTML(t('dialog.WaitForHostMsg', { room }))
|
||||||
{ room: roomName })
|
|
||||||
}
|
}
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
</Dialog>
|
||||||
onPress = { this._onLogin }
|
|
||||||
title = { t('dialog.IamHost') } />
|
|
||||||
<Button
|
|
||||||
onPress = { this._onCancel }
|
|
||||||
title = { t('dialog.Cancel') } />
|
|
||||||
</View>
|
|
||||||
</Modal>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the OK button is clicked.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onLogin() {
|
|
||||||
this.props.dispatch(_showLoginDialog());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the cancel button is clicked.
|
* Called when the cancel button is clicked.
|
||||||
*
|
*
|
||||||
|
@ -102,6 +88,33 @@ class WaitForOwnerDialog extends Component {
|
||||||
_onCancel() {
|
_onCancel() {
|
||||||
this.props.dispatch(cancelWaitForOwner());
|
this.props.dispatch(cancelWaitForOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the OK button is clicked.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onLogin() {
|
||||||
|
this.props.dispatch(_openLoginDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a specific <tt>string</tt> which may contain HTML.
|
||||||
|
*
|
||||||
|
* @param {string} html - The <tt>string</tt> which may contain HTML to
|
||||||
|
* render.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
_renderHTML(html) {
|
||||||
|
if (typeof html === 'string') {
|
||||||
|
// TODO Limited styling may easily be provided by utilizing Text
|
||||||
|
// with style.
|
||||||
|
return html.replace(/<\\?b>/gi, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,7 +124,7 @@ class WaitForOwnerDialog extends Component {
|
||||||
* @param {Object} state - The Redux state.
|
* @param {Object} state - The Redux state.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* roomName: string
|
* _room: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
|
@ -120,7 +133,7 @@ function _mapStateToProps(state) {
|
||||||
} = state['features/base/conference'];
|
} = state['features/base/conference'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
roomName: authRequired && authRequired.getName()
|
_room: authRequired && authRequired.getName()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,55 @@
|
||||||
import {
|
import { BoxModel, createStyleSheet } from '../../base/styles';
|
||||||
ColorPalette,
|
|
||||||
createStyleSheet
|
/**
|
||||||
} from '../../base/styles';
|
* The style common to <tt>LoginDialog</tt> and <tt>WaitForOwnerDialog</tt>.
|
||||||
|
*/
|
||||||
|
const dialog = {
|
||||||
|
marginBottom: BoxModel.margin,
|
||||||
|
marginTop: BoxModel.margin
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The style common to <tt>Text</tt> rendered by <tt>LoginDialog</tt> and
|
||||||
|
* <tt>WaitForOwnerDialog</tt>.
|
||||||
|
*/
|
||||||
|
const text = {
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styles of the authentication feature.
|
* The styles of the authentication feature.
|
||||||
*/
|
*/
|
||||||
export default createStyleSheet({
|
export default createStyleSheet({
|
||||||
outerArea: {
|
/**
|
||||||
flex: 1
|
* The style of <tt>LoginDialog</tt>.
|
||||||
|
*/
|
||||||
|
loginDialog: {
|
||||||
|
...dialog,
|
||||||
|
flex: 0,
|
||||||
|
flexDirection: 'column'
|
||||||
},
|
},
|
||||||
dialogBox: {
|
|
||||||
marginLeft: '10%',
|
/**
|
||||||
marginRight: '10%',
|
* The style of <tt>Text</tt> rendered by <tt>LoginDialog</tt>.
|
||||||
marginTop: '10%',
|
*/
|
||||||
backgroundColor: ColorPalette.white
|
loginDialogText: {
|
||||||
|
...text
|
||||||
},
|
},
|
||||||
textInput: {
|
|
||||||
height: 25,
|
/**
|
||||||
fontSize: 16
|
* The style of <tt>TextInput</tt> rendered by <tt>LoginDialog</tt>.
|
||||||
|
*/
|
||||||
|
loginDialogTextInput: {
|
||||||
|
// XXX Matches react-native-prompt's dialogInput because base/dialog's
|
||||||
|
// Dialog is implemented using react-native-prompt.
|
||||||
|
fontSize: 18,
|
||||||
|
height: 50
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The style of <tt>WaitForOwnerDialog</tt>.
|
||||||
|
*/
|
||||||
|
waitForOwnerDialog: {
|
||||||
|
...dialog,
|
||||||
|
...text
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import {
|
|
||||||
LoginDialog,
|
|
||||||
WaitForOwnerDialog
|
|
||||||
} from './components/index';
|
|
||||||
import { hideDialog } from '../base/dialog/actions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will clear the wait for conference owner timeout handler if any is currently
|
|
||||||
* set.
|
|
||||||
*
|
|
||||||
* @param {Object} store - The Redux store instance.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
export function clearExistingWaitForOwnerTimeout(store) {
|
|
||||||
const { waitForOwnerTimeoutID }
|
|
||||||
= store.getState()['features/authentication'];
|
|
||||||
|
|
||||||
if (waitForOwnerTimeoutID) {
|
|
||||||
clearTimeout(waitForOwnerTimeoutID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if {@link LoginDialog} is currently open.
|
|
||||||
*
|
|
||||||
* @param {Object|Function} getStateOrState - The Redux store instance or
|
|
||||||
* store's get state method.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function isLoginDialogOpened(getStateOrState) {
|
|
||||||
const state
|
|
||||||
= typeof getStateOrState === 'function'
|
|
||||||
? getStateOrState() : getStateOrState;
|
|
||||||
const dialogState = state['features/base/dialog'];
|
|
||||||
|
|
||||||
return dialogState.component && dialogState.component === LoginDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides {@link LoginDialog} if it's currently displayed.
|
|
||||||
*
|
|
||||||
* @param {Object} store - The Redux store instance.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
export function hideLoginDialog({ dispatch, getState }) {
|
|
||||||
if (isLoginDialogOpened(getState)) {
|
|
||||||
dispatch(hideDialog());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if {@link WaitForOwnerDialog} is currently open.
|
|
||||||
*
|
|
||||||
* @param {Object} store - The Redux store instance.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function isWaitForOwnerDialogOpened({ getState }) {
|
|
||||||
const dialogState = getState()['features/base/dialog'];
|
|
||||||
|
|
||||||
return dialogState.component
|
|
||||||
&& dialogState.component === WaitForOwnerDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the cyclic "wait for conference owner" task is currently scheduled.
|
|
||||||
*
|
|
||||||
* @param {Object} store - The Redux store instance.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function isWaitingForOwner({ getState }) {
|
|
||||||
const { waitForOwnerTimeoutID } = getState()['features/authentication'];
|
|
||||||
|
|
||||||
return Boolean(waitForOwnerTimeoutID);
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export * from './actionTypes';
|
export * from './actionTypes';
|
||||||
export * from './functions';
|
export * from './components';
|
||||||
|
|
||||||
import './middleware';
|
import './middleware';
|
||||||
|
|
||||||
import './reducer';
|
import './reducer';
|
||||||
|
|
|
@ -1,40 +1,33 @@
|
||||||
|
import { appNavigate } from '../app';
|
||||||
|
import {
|
||||||
|
CONFERENCE_FAILED,
|
||||||
|
CONFERENCE_JOINED,
|
||||||
|
CONFERENCE_LEFT
|
||||||
|
} from '../base/conference';
|
||||||
|
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../base/connection';
|
||||||
|
import { hideDialog, isDialogOpen } from '../base/dialog';
|
||||||
|
import {
|
||||||
|
JitsiConferenceErrors,
|
||||||
|
JitsiConnectionErrors
|
||||||
|
} from '../base/lib-jitsi-meet';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
clearWaitForOwnerTimeout,
|
_openLoginDialog,
|
||||||
_showLoginDialog,
|
_openWaitForOwnerDialog,
|
||||||
_showWaitForOwnerDialog,
|
stopWaitForOwner,
|
||||||
waitForOwner
|
waitForOwner
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { appNavigate } from '../app/actions';
|
|
||||||
import {
|
import {
|
||||||
CANCEL_LOGIN,
|
CANCEL_LOGIN,
|
||||||
CANCEL_WAIT_FOR_OWNER,
|
CANCEL_WAIT_FOR_OWNER,
|
||||||
STOP_WAIT_FOR_OWNER,
|
STOP_WAIT_FOR_OWNER,
|
||||||
WAIT_FOR_OWNER
|
WAIT_FOR_OWNER
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import {
|
import { LoginDialog, WaitForOwnerDialog } from './components';
|
||||||
CONFERENCE_FAILED,
|
|
||||||
CONFERENCE_JOINED,
|
|
||||||
CONFERENCE_LEFT
|
|
||||||
} from '../base/conference/actionTypes';
|
|
||||||
import { hideDialog } from '../base/dialog/actions';
|
|
||||||
import { CONNECTION_ESTABLISHED } from '../base/connection/actionTypes';
|
|
||||||
import { CONNECTION_FAILED } from '../base/connection';
|
|
||||||
import {
|
|
||||||
clearExistingWaitForOwnerTimeout,
|
|
||||||
hideLoginDialog,
|
|
||||||
isLoginDialogOpened,
|
|
||||||
isWaitForOwnerDialogOpened,
|
|
||||||
isWaitingForOwner
|
|
||||||
} from './functions';
|
|
||||||
import {
|
|
||||||
JitsiConferenceErrors,
|
|
||||||
JitsiConnectionErrors
|
|
||||||
} from '../base/lib-jitsi-meet';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware that captures connection or conference failed errors and controlls
|
* Middleware that captures connection or conference failed errors and controls
|
||||||
* {@link WaitForOwnerDialog} and {@link LoginDialog}.
|
* {@link WaitForOwnerDialog} and {@link LoginDialog}.
|
||||||
*
|
*
|
||||||
* FIXME Some of the complexity was introduced by the lack of dialog stacking.
|
* FIXME Some of the complexity was introduced by the lack of dialog stacking.
|
||||||
|
@ -44,93 +37,121 @@ import {
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case CONNECTION_FAILED: {
|
|
||||||
if (action.error === JitsiConnectionErrors.PASSWORD_REQUIRED) {
|
|
||||||
store.dispatch(_showLoginDialog());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CONNECTION_ESTABLISHED: {
|
|
||||||
hideLoginDialog(store);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CONFERENCE_FAILED: {
|
|
||||||
if (action.error === JitsiConferenceErrors.AUTHENTICATION_REQUIRED) {
|
|
||||||
store.dispatch(waitForOwner());
|
|
||||||
} else {
|
|
||||||
store.dispatch(clearWaitForOwnerTimeout());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CONFERENCE_JOINED: {
|
|
||||||
if (isWaitingForOwner(store)) {
|
|
||||||
store.dispatch(clearWaitForOwnerTimeout());
|
|
||||||
}
|
|
||||||
hideLoginDialog(store);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CONFERENCE_LEFT: {
|
|
||||||
store.dispatch(clearWaitForOwnerTimeout());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WAIT_FOR_OWNER: {
|
|
||||||
clearExistingWaitForOwnerTimeout(store);
|
|
||||||
const { handler, timeoutMs } = action;
|
|
||||||
const newTimeoutId = setTimeout(handler, timeoutMs);
|
|
||||||
|
|
||||||
action.waitForOwnerTimeoutID = newTimeoutId;
|
|
||||||
|
|
||||||
// The WAIT_FOR_OWNER action is cyclic and we don't want to hide
|
|
||||||
// the login dialog every few seconds...
|
|
||||||
if (!isLoginDialogOpened(store.getState())) {
|
|
||||||
store.dispatch(_showWaitForOwnerDialog());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case STOP_WAIT_FOR_OWNER: {
|
|
||||||
clearExistingWaitForOwnerTimeout(store);
|
|
||||||
if (isWaitForOwnerDialogOpened(store)) {
|
|
||||||
store.dispatch(hideDialog());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CANCEL_LOGIN: {
|
case CANCEL_LOGIN: {
|
||||||
const { upgradeRoleInProgress }
|
const { upgradeRoleInProgress }
|
||||||
= store.getState()['features/authentication'];
|
= store.getState()['features/authentication'];
|
||||||
|
|
||||||
if (upgradeRoleInProgress) {
|
upgradeRoleInProgress && upgradeRoleInProgress.cancel();
|
||||||
upgradeRoleInProgress.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
const waitingForOwner = isWaitingForOwner(store);
|
|
||||||
|
|
||||||
// The LoginDialog can be opened on top of "wait for owner". The app
|
// The LoginDialog can be opened on top of "wait for owner". The app
|
||||||
// should navigate only if LoginDialog was open without
|
// should navigate only if LoginDialog was open without the
|
||||||
// the WaitForOwnerDialog.
|
// WaitForOwnerDialog.
|
||||||
if (!isWaitForOwnerDialogOpened(store) && !waitingForOwner) {
|
if (!isDialogOpen(store, WaitForOwnerDialog)) {
|
||||||
// Go back to app entry point
|
if (_isWaitingForOwner(store)) {
|
||||||
hideLoginDialog(store);
|
|
||||||
store.dispatch(appNavigate(undefined));
|
|
||||||
} else if (!isWaitForOwnerDialogOpened(store) && waitingForOwner) {
|
|
||||||
// Instead of hiding show the new one.
|
// Instead of hiding show the new one.
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
store.dispatch(_showWaitForOwnerDialog());
|
store.dispatch(_openWaitForOwnerDialog());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go back to the app's entry point.
|
||||||
|
_hideLoginDialog(store);
|
||||||
|
store.dispatch(appNavigate(undefined));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CANCEL_WAIT_FOR_OWNER: {
|
case CANCEL_WAIT_FOR_OWNER: {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
store.dispatch(clearWaitForOwnerTimeout());
|
store.dispatch(stopWaitForOwner());
|
||||||
store.dispatch(appNavigate(undefined));
|
store.dispatch(appNavigate(undefined));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CONFERENCE_FAILED:
|
||||||
|
if (action.error === JitsiConferenceErrors.AUTHENTICATION_REQUIRED) {
|
||||||
|
store.dispatch(waitForOwner());
|
||||||
|
} else {
|
||||||
|
store.dispatch(stopWaitForOwner());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFERENCE_JOINED:
|
||||||
|
if (_isWaitingForOwner(store)) {
|
||||||
|
store.dispatch(stopWaitForOwner());
|
||||||
|
}
|
||||||
|
_hideLoginDialog(store);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFERENCE_LEFT:
|
||||||
|
store.dispatch(stopWaitForOwner());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECTION_ESTABLISHED:
|
||||||
|
_hideLoginDialog(store);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECTION_FAILED:
|
||||||
|
action.error === JitsiConnectionErrors.PASSWORD_REQUIRED
|
||||||
|
&& store.dispatch(_openLoginDialog());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STOP_WAIT_FOR_OWNER:
|
||||||
|
_clearExistingWaitForOwnerTimeout(store);
|
||||||
|
store.dispatch(hideDialog(WaitForOwnerDialog));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAIT_FOR_OWNER: {
|
||||||
|
_clearExistingWaitForOwnerTimeout(store);
|
||||||
|
|
||||||
|
const { handler, timeoutMs } = action;
|
||||||
|
|
||||||
|
action.waitForOwnerTimeoutID = setTimeout(handler, timeoutMs);
|
||||||
|
|
||||||
|
// The WAIT_FOR_OWNER action is cyclic and we don't want to hide the
|
||||||
|
// login dialog every few seconds...
|
||||||
|
isDialogOpen(store, LoginDialog)
|
||||||
|
|| store.dispatch(_openWaitForOwnerDialog());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will clear the wait for conference owner timeout handler if any is currently
|
||||||
|
* set.
|
||||||
|
*
|
||||||
|
* @param {Object} store - The redux store.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function _clearExistingWaitForOwnerTimeout({ getState }) {
|
||||||
|
const { waitForOwnerTimeoutID } = getState()['features/authentication'];
|
||||||
|
|
||||||
|
waitForOwnerTimeoutID && clearTimeout(waitForOwnerTimeoutID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides {@link LoginDialog} if it's currently displayed.
|
||||||
|
*
|
||||||
|
* @param {Object} store - The redux store.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function _hideLoginDialog({ dispatch }) {
|
||||||
|
dispatch(hideDialog(LoginDialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the cyclic "wait for conference owner" task is currently scheduled.
|
||||||
|
*
|
||||||
|
* @param {Object} store - The redux store.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function _isWaitingForOwner({ getState }) {
|
||||||
|
return Boolean(getState()['features/authentication'].waitForOwnerTimeoutID);
|
||||||
|
}
|
||||||
|
|
|
@ -1,43 +1,39 @@
|
||||||
import { assign } from '../base/redux/functions';
|
/* @flow */
|
||||||
import { ReducerRegistry } from '../base/redux';
|
|
||||||
|
import { assign, ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CANCEL_LOGIN,
|
CANCEL_LOGIN,
|
||||||
STOP_WAIT_FOR_OWNER,
|
STOP_WAIT_FOR_OWNER,
|
||||||
UPGRADE_ROLE_FAILED, UPGRADE_ROLE_STARTED, UPGRADE_ROLE_SUCCESS,
|
UPGRADE_ROLE_FINISHED,
|
||||||
|
UPGRADE_ROLE_STARTED,
|
||||||
WAIT_FOR_OWNER
|
WAIT_FOR_OWNER
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
ReducerRegistry.register('features/authentication', (state = {}, action) => {
|
ReducerRegistry.register('features/authentication', (state = {}, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case WAIT_FOR_OWNER:
|
|
||||||
return assign(state, {
|
|
||||||
waitForOwnerTimeoutID: action.waitForOwnerTimeoutID
|
|
||||||
});
|
|
||||||
case UPGRADE_ROLE_STARTED:
|
|
||||||
return assign(state, {
|
|
||||||
upgradeRoleError: undefined,
|
|
||||||
upgradeRoleInProgress: action.authConnection
|
|
||||||
});
|
|
||||||
case UPGRADE_ROLE_SUCCESS:
|
|
||||||
return assign(state, {
|
|
||||||
upgradeRoleError: undefined,
|
|
||||||
upgradeRoleInProgress: undefined
|
|
||||||
});
|
|
||||||
case UPGRADE_ROLE_FAILED:
|
|
||||||
return assign(state, {
|
|
||||||
upgradeRoleError: action.error,
|
|
||||||
upgradeRoleInProgress: undefined
|
|
||||||
});
|
|
||||||
case CANCEL_LOGIN:
|
case CANCEL_LOGIN:
|
||||||
return assign(state, {
|
return assign(state, {
|
||||||
upgradeRoleError: undefined,
|
upgradeRoleError: undefined,
|
||||||
upgradeRoleInProgress: undefined
|
upgradeRoleInProgress: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
case STOP_WAIT_FOR_OWNER:
|
case STOP_WAIT_FOR_OWNER:
|
||||||
return assign(state, {
|
return assign(state, {
|
||||||
waitForOwnerTimeoutID: undefined,
|
upgradeRoleError: undefined,
|
||||||
upgradeRoleError: undefined
|
waitForOwnerTimeoutID: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
case UPGRADE_ROLE_FINISHED:
|
||||||
|
case UPGRADE_ROLE_STARTED:
|
||||||
|
return assign(state, {
|
||||||
|
upgradeRoleError: action.error,
|
||||||
|
upgradeRoleInProgress: action.thenableWithCancel
|
||||||
|
});
|
||||||
|
|
||||||
|
case WAIT_FOR_OWNER:
|
||||||
|
return assign(state, {
|
||||||
|
waitForOwnerTimeoutID: action.waitForOwnerTimeoutID
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -298,12 +298,10 @@ export function createConference() {
|
||||||
*/
|
*/
|
||||||
export function checkIfCanJoin() {
|
export function checkIfCanJoin() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { password, authRequired }
|
const { authRequired, password }
|
||||||
= getState()['features/base/conference'];
|
= getState()['features/base/conference'];
|
||||||
|
|
||||||
if (authRequired) {
|
authRequired && authRequired.join(password);
|
||||||
authRequired.join(password);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,11 @@ import {
|
||||||
/**
|
/**
|
||||||
* Opens new connection.
|
* Opens new connection.
|
||||||
*
|
*
|
||||||
* @param {string} [username] - The XMPP user id eg. user@server.com.
|
* @param {string} [id] - The XMPP user's ID (e.g. user@server.com).
|
||||||
* @param {string} [password] - The user's password.
|
* @param {string} [password] - The XMPP user's password.
|
||||||
*
|
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
export function connect(username: ?string, password: ?string) {
|
export function connect(id: ?string, password: ?string) {
|
||||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const options = _constructOptions(state);
|
const options = _constructOptions(state);
|
||||||
|
@ -47,7 +46,7 @@ export function connect(username: ?string, password: ?string) {
|
||||||
_onConnectionFailed);
|
_onConnectionFailed);
|
||||||
|
|
||||||
connection.connect({
|
connection.connect({
|
||||||
id: username,
|
id,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -53,24 +53,14 @@ export function getURLWithoutParams(url: URL): URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert provided id to jid if it's not jid yet.
|
* Converts a specific id to jid if it's not jid yet.
|
||||||
*
|
*
|
||||||
* @param {string} id - User id or jid.
|
* @param {string} id - User id or jid.
|
||||||
* @param {Object} configHosts - The 'hosts' part of the config object.
|
* @param {Object} configHosts - The <tt>hosts</tt> part of the <tt>config</tt>
|
||||||
* @returns {string} jid - A string in the form of user@server.com.
|
* object.
|
||||||
|
* @returns {string} A string in the form of a JID (i.e.
|
||||||
|
* <tt>user@server.com</tt>).
|
||||||
*/
|
*/
|
||||||
export function toJid(id: string, configHosts: Object): string {
|
export function toJid(id: string, { authdomain, domain }: Object): string {
|
||||||
if (id.indexOf('@') >= 0) {
|
return id.indexOf('@') >= 0 ? id : `${id}@${authdomain || domain}`;
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
let jid = id.concat('@');
|
|
||||||
|
|
||||||
if (configHosts.authdomain) {
|
|
||||||
jid += configHosts.authdomain;
|
|
||||||
} else {
|
|
||||||
jid += configHosts.domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jid;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue