jiti-meet/react/features/authentication/middleware.js

176 lines
4.9 KiB
JavaScript
Raw Normal View History

2017-10-04 22:36:09 +00:00
// @flow
2017-09-18 07:09:43 +00:00
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';
2017-09-08 13:36:42 +00:00
import { MiddlewareRegistry } from '../base/redux';
import {
2017-09-18 07:09:43 +00:00
_openLoginDialog,
_openWaitForOwnerDialog,
stopWaitForOwner,
2017-09-08 13:36:42 +00:00
waitForOwner
} from './actions';
import {
CANCEL_LOGIN,
STOP_WAIT_FOR_OWNER,
WAIT_FOR_OWNER
} from './actionTypes';
2017-09-18 07:09:43 +00:00
import { LoginDialog, WaitForOwnerDialog } from './components';
2017-09-08 13:36:42 +00:00
/**
2017-09-18 07:09:43 +00:00
* Middleware that captures connection or conference failed errors and controls
2017-09-08 13:36:42 +00:00
* {@link WaitForOwnerDialog} and {@link LoginDialog}.
*
* FIXME Some of the complexity was introduced by the lack of dialog stacking.
*
* @param {Store} store - Redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
2017-09-18 07:09:43 +00:00
case CANCEL_LOGIN: {
2017-10-06 20:15:51 +00:00
const { dispatch, getState } = store;
const { thenableWithCancel } = getState()['features/authentication'];
2017-09-18 07:09:43 +00:00
2017-09-24 23:41:20 +00:00
thenableWithCancel && thenableWithCancel.cancel();
2017-09-18 07:09:43 +00:00
// The LoginDialog can be opened on top of "wait for owner". The app
// should navigate only if LoginDialog was open without the
// WaitForOwnerDialog.
if (!isDialogOpen(store, WaitForOwnerDialog)) {
if (_isWaitingForOwner(store)) {
// Instead of hiding show the new one.
const result = next(action);
2017-10-06 20:15:51 +00:00
dispatch(_openWaitForOwnerDialog());
2017-09-18 07:09:43 +00:00
return result;
}
// Go back to the app's entry point.
_hideLoginDialog(store);
// FIXME Like cancelWaitForOwner, dispatch conferenceLeft to notify
// the external-api.
2017-10-06 20:15:51 +00:00
dispatch(appNavigate(undefined));
2017-09-08 13:36:42 +00:00
}
break;
}
2017-09-18 07:09:43 +00:00
case CONFERENCE_FAILED: {
const { error } = action;
// XXX The feature authentication affords recovery from
// CONFERENCE_FAILED caused by
// JitsiConferenceErrors.AUTHENTICATION_REQUIRED.
let recoverable;
if (error.name === JitsiConferenceErrors.AUTHENTICATION_REQUIRED) {
if (typeof error.recoverable === 'undefined') {
error.recoverable = true;
}
recoverable = error.recoverable;
}
if (recoverable) {
2017-09-08 13:36:42 +00:00
store.dispatch(waitForOwner());
} else {
2017-09-18 07:09:43 +00:00
store.dispatch(stopWaitForOwner());
2017-09-08 13:36:42 +00:00
}
break;
}
2017-09-18 07:09:43 +00:00
case CONFERENCE_JOINED:
if (_isWaitingForOwner(store)) {
store.dispatch(stopWaitForOwner());
2017-09-08 13:36:42 +00:00
}
2017-09-18 07:09:43 +00:00
_hideLoginDialog(store);
2017-09-08 13:36:42 +00:00
break;
2017-09-18 07:09:43 +00:00
case CONFERENCE_LEFT:
store.dispatch(stopWaitForOwner());
break;
2017-09-08 13:36:42 +00:00
2017-09-18 07:09:43 +00:00
case CONNECTION_ESTABLISHED:
_hideLoginDialog(store);
2017-09-08 13:36:42 +00:00
break;
2017-09-18 07:09:43 +00:00
case CONNECTION_FAILED: {
const { error } = action;
if (error
&& error.name === JitsiConnectionErrors.PASSWORD_REQUIRED
&& typeof error.recoverable === 'undefined') {
error.recoverable = true;
store.dispatch(_openLoginDialog());
}
2017-09-08 13:36:42 +00:00
break;
}
2017-09-08 13:36:42 +00:00
2017-09-18 07:09:43 +00:00
case STOP_WAIT_FOR_OWNER:
_clearExistingWaitForOwnerTimeout(store);
store.dispatch(hideDialog(WaitForOwnerDialog));
break;
2017-09-08 13:36:42 +00:00
2017-09-18 07:09:43 +00:00
case WAIT_FOR_OWNER: {
_clearExistingWaitForOwnerTimeout(store);
2017-09-08 13:36:42 +00:00
2017-09-18 07:09:43 +00:00
const { handler, timeoutMs } = action;
2017-09-08 13:36:42 +00:00
2017-09-18 07:09:43 +00:00
action.waitForOwnerTimeoutID = setTimeout(handler, timeoutMs);
2017-09-08 13:36:42 +00:00
2017-09-18 07:09:43 +00:00
// The WAIT_FOR_OWNER action is cyclic and we don't want to hide the
2017-09-24 23:41:20 +00:00
// login dialog every few seconds.
2017-09-18 07:09:43 +00:00
isDialogOpen(store, LoginDialog)
|| store.dispatch(_openWaitForOwnerDialog());
2017-09-08 13:36:42 +00:00
break;
}
}
return next(action);
});
2017-09-18 07:09:43 +00:00
/**
* Will clear the wait for conference owner timeout handler if any is currently
* set.
*
* @param {Object} store - The redux store.
* @returns {void}
*/
2017-10-04 22:36:09 +00:00
function _clearExistingWaitForOwnerTimeout(
{ getState }: { getState: Function }) {
2017-09-18 07:09:43 +00:00
const { waitForOwnerTimeoutID } = getState()['features/authentication'];
waitForOwnerTimeoutID && clearTimeout(waitForOwnerTimeoutID);
}
/**
* Hides {@link LoginDialog} if it's currently displayed.
*
* @param {Object} store - The redux store.
* @returns {void}
*/
2017-10-04 22:36:09 +00:00
function _hideLoginDialog({ dispatch }: { dispatch: Dispatch<*> }) {
2017-09-18 07:09:43 +00:00
dispatch(hideDialog(LoginDialog));
}
/**
* Checks if the cyclic "wait for conference owner" task is currently scheduled.
*
* @param {Object} store - The redux store.
* @returns {boolean}
*/
2017-10-04 22:36:09 +00:00
function _isWaitingForOwner({ getState }: { getState: Function }) {
2017-09-18 07:09:43 +00:00
return Boolean(getState()['features/authentication'].waitForOwnerTimeoutID);
}