Replace features/unsupported-browser SET_UNSUPPORTED_BROWSER with features/base/lib-jitsi-meet SET_WEBRTC_READY

The error raised by JitsiMeetJS.init() is already in the state of
features/base/lib-jitsi-meet so it's not a good design to store the same
error in the state of features/unsupported-browser.
This commit is contained in:
Lyubo Marinov 2017-02-27 21:22:32 -06:00
parent a8877d82b6
commit 0ed85b9d25
15 changed files with 246 additions and 99 deletions

View File

@ -2,15 +2,17 @@
import type { Dispatch } from 'redux'; import type { Dispatch } from 'redux';
import { JitsiConferenceEvents } from '../lib-jitsi-meet'; import {
JitsiConferenceEvents,
libInitError,
WEBRTC_NOT_READY,
WEBRTC_NOT_SUPPORTED
} from '../lib-jitsi-meet';
import UIEvents from '../../../../service/UI/UIEvents'; import UIEvents from '../../../../service/UI/UIEvents';
import { SET_DOMAIN } from './actionTypes'; import { SET_DOMAIN } from './actionTypes';
import { appNavigate } from '../../app';
import { setUnsupportedBrowser } from '../../unsupported-browser';
declare var APP: Object; declare var APP: Object;
declare var config: Object; declare var config: Object;
@ -77,28 +79,19 @@ export function connect() {
APP.UI.promptDisplayName(); APP.UI.promptDisplayName();
} }
}) })
.catch(err => { .catch(error => {
APP.UI.hideRingOverLay(); APP.UI.hideRingOverLay();
APP.API.notifyConferenceLeft(APP.conference.roomName); APP.API.notifyConferenceLeft(APP.conference.roomName);
logger.error(err); logger.error(error);
dispatch(setUnsupportedBrowser(err)); // TODO The following are in fact Errors raised by
// JitsiMeetJS.init() which should be taken care of in
// If during the conference initialization was defined that // features/base/lib-jitsi-meet but we are not there yet on the
// browser doesn't support WebRTC then we should define // Web at the time of this writing.
// which route to render. switch (error.name) {
dispatch(appNavigate(room)); case WEBRTC_NOT_READY:
case WEBRTC_NOT_SUPPORTED:
// Force reinitialization of the conference if WebRTC is ready. dispatch(libInitError(error));
if (err.webRTCReadyPromise) {
err.webRTCReadyPromise.then(() => {
// Setting plugin required flag to false because
// it's already been installed.
dispatch(setUnsupportedBrowser({
name: 'OK'
}));
dispatch(appNavigate(room));
});
} }
}); });
}; };

View File

@ -58,3 +58,13 @@ export const LIB_WILL_INIT = Symbol('LIB_WILL_INIT');
* } * }
*/ */
export const SET_CONFIG = Symbol('SET_CONFIG'); export const SET_CONFIG = Symbol('SET_CONFIG');
/**
* The type of Redux action which indicates whether WebRTC is ready.
*
* {
* type: SET_WEBRTC_READY,
* webRTCReady: boolean | Promise
* }
*/
export const SET_WEBRTC_READY = Symbol('SET_WEBRTC_READY');

View File

@ -7,7 +7,8 @@ import {
LIB_INIT_ERROR, LIB_INIT_ERROR,
LIB_WILL_DISPOSE, LIB_WILL_DISPOSE,
LIB_WILL_INIT, LIB_WILL_INIT,
SET_CONFIG SET_CONFIG,
SET_WEBRTC_READY
} from './actionTypes'; } from './actionTypes';
declare var APP: Object; declare var APP: Object;
@ -100,3 +101,54 @@ export function setConfig(config: Object) {
config config
}; };
} }
/**
* Sets the indicator which determines whether WebRTC is ready. In execution
* environments in which WebRTC is supported via a known plugin such
* as Temasys WebRTC may start not ready and then become ready. Of course, there
* are execution enviroments such as old Mozilla Firefox versions or
* certains Microsoft Edge versions in which WebRTC is not supported at all.
*
* @param {boolean|Promise} webRTCReady - The indicator which determines
* whether WebRTC is ready. If a Promise is specified, its resolution will be
* awaited.
* @returns {Function}
*/
export function setWebRTCReady(webRTCReady: boolean | Promise<*>) {
return (dispatch: Dispatch<*>, getState: Function) => {
if (getState()['features/base/lib-jitsi-meet'].webRTCReady
!== webRTCReady) {
dispatch({
type: SET_WEBRTC_READY,
webRTCReady
});
// If the specified webRTCReady is a thenable (i.e. a Promise), then
// await its resolution.
switch (typeof webRTCReady) {
case 'function':
case 'object': {
const { then } = webRTCReady;
if (typeof then === 'function') {
const onFulfilled = value => {
// Is the app still interested in the specified
// webRTCReady?
if (getState()['features/base/lib-jitsi-meet']
.webRTCReady
=== webRTCReady) {
dispatch(setWebRTCReady(value));
}
};
then.call(
webRTCReady,
/* onFulfilled */ () => onFulfilled(true),
/* onRejected*/ () => onFulfilled(false));
}
break;
}
}
}
};
}

View File

@ -0,0 +1,13 @@
/**
* The name of the Error thrown by {@link JitsiMeetJS.init()} which indicates
* that WebRTC is not ready and its readiness may be tracked via the
* webRTCReadyPromise property value of the Error.
*/
export const WEBRTC_NOT_READY = 'WEBRTC_NOT_READY';
/**
* The name of the Error thrown by {@link JitsiMeetJS.init()} which indicates
* that WebRTC is not supported by the execution environment either natively or
* via a known plugin such as Temasys.
*/
export const WEBRTC_NOT_SUPPORTED = 'WEBRTC_NOT_SUPPORTED';

View File

@ -15,6 +15,7 @@ export const JitsiTrackEvents = JitsiMeetJS.events.track;
export * from './actions'; export * from './actions';
export * from './actionTypes'; export * from './actionTypes';
export * from './constants';
export * from './functions'; export * from './functions';
import './middleware'; import './middleware';

View File

@ -1,8 +1,9 @@
import { PARTICIPANT_LEFT } from '../participants'; import { PARTICIPANT_LEFT } from '../participants';
import { MiddlewareRegistry } from '../redux'; import { MiddlewareRegistry } from '../redux';
import { disposeLib, initLib } from './actions'; import { disposeLib, initLib, setWebRTCReady } from './actions';
import { SET_CONFIG } from './actionTypes'; import { LIB_DID_INIT, LIB_INIT_ERROR, SET_CONFIG } from './actionTypes';
import { WEBRTC_NOT_READY, WEBRTC_NOT_SUPPORTED } from './constants';
/** /**
* Middleware that captures PARTICIPANT_LEFT action for a local participant * Middleware that captures PARTICIPANT_LEFT action for a local participant
@ -16,6 +17,13 @@ import { SET_CONFIG } from './actionTypes';
*/ */
MiddlewareRegistry.register(store => next => action => { MiddlewareRegistry.register(store => next => action => {
switch (action.type) { switch (action.type) {
case LIB_DID_INIT:
store.dispatch(setWebRTCReady(true));
break;
case LIB_INIT_ERROR:
return _libInitError(store, next, action);
case PARTICIPANT_LEFT: case PARTICIPANT_LEFT:
action.participant.local && store.dispatch(disposeLib()); action.participant.local && store.dispatch(disposeLib());
break; break;
@ -27,6 +35,44 @@ MiddlewareRegistry.register(store => next => action => {
return next(action); return next(action);
}); });
/**
* Notifies the feature base/lib-jitsi-meet that the action LIB_INIT_ERROR is
* being dispatched within a specific Redux store.
*
* @param {Store} store - The Redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The Redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The Redux action LIB_INIT_ERROR which is being
* dispatched in the specified store.
* @returns {Object} The new state that is the result of the reduction of the
* specified action.
* @private
*/
function _libInitError(store, next, action) {
const nextState = next(action);
const error = action.error;
if (error) {
let webRTCReady;
switch (error.name) {
case WEBRTC_NOT_READY:
webRTCReady = error.webRTCReadyPromise;
break;
case WEBRTC_NOT_SUPPORTED:
webRTCReady = false;
break;
}
typeof webRTCReady === 'undefined'
|| store.dispatch(setWebRTCReady(webRTCReady));
}
return nextState;
}
/** /**
* Notifies the feature base/lib-jitsi-meet that the action SET_CONFIG is being * Notifies the feature base/lib-jitsi-meet that the action SET_CONFIG is being
* dispatched within a specific Redux store. * dispatched within a specific Redux store.

View File

@ -4,7 +4,8 @@ import {
LIB_DID_DISPOSE, LIB_DID_DISPOSE,
LIB_DID_INIT, LIB_DID_INIT,
LIB_INIT_ERROR, LIB_INIT_ERROR,
SET_CONFIG SET_CONFIG,
SET_WEBRTC_READY
} from './actionTypes'; } from './actionTypes';
/** /**
@ -66,6 +67,12 @@ ReducerRegistry.register(
case SET_CONFIG: case SET_CONFIG:
return _setConfig(state, action); return _setConfig(state, action);
case SET_WEBRTC_READY:
return {
...state,
webRTCReady: action.webRTCReady
};
default: default:
return state; return state;
} }

View File

@ -7,10 +7,10 @@ if (userAgent.match(/Android/i)) {
OS = 'android'; OS = 'android';
} else if (userAgent.match(/iP(ad|hone|od)/i)) { } else if (userAgent.match(/iP(ad|hone|od)/i)) {
OS = 'ios'; OS = 'ios';
} else if (userAgent.match(/windows/i)) { } else if (userAgent.match(/Mac(intosh| OS X)/i)) {
OS = 'macos';
} else if (userAgent.match(/Windows/i)) {
OS = 'windows'; OS = 'windows';
} else if (userAgent.match(/mac/i)) {
OS = 'mac';
} }
/** /**

View File

@ -44,12 +44,22 @@ const _RULES = [
} }
}, },
state => { state => {
switch (state['features/unsupported-browser'].name) { const { webRTCReady } = state['features/base/lib-jitsi-meet'];
case 'WEBRTC_NOT_READY':
return PluginRequiredBrowser;
case 'WEBRTC_NOT_SUPPORTED': switch (typeof webRTCReady) {
return UnsupportedDesktopBrowser; case 'boolean':
if (webRTCReady === false) {
return UnsupportedDesktopBrowser;
}
break;
case 'undefined':
// If webRTCReady is not set, then we cannot use it to take a
// decision.
break;
default:
return PluginRequiredBrowser;
} }
} }
]; ];

View File

@ -16,14 +16,3 @@ import { Symbol } from '../base/react';
* } * }
*/ */
export const DISMISS_MOBILE_APP_PROMO = Symbol('DISMISS_MOBILE_APP_PROMO'); export const DISMISS_MOBILE_APP_PROMO = Symbol('DISMISS_MOBILE_APP_PROMO');
/**
* The type of Redux action which signals to change information about
* unsupported browser in Redux store.
*
* {
* type: SET_UNSUPPORTED_BROWSER,
* unsupportedBrowser: Object
* }
*/
export const SET_UNSUPPORTED_BROWSER = Symbol('SET_UNSUPPORTED_BROWSER');

View File

@ -1,7 +1,4 @@
import { import { DISMISS_MOBILE_APP_PROMO } from './actionTypes';
DISMISS_MOBILE_APP_PROMO,
SET_UNSUPPORTED_BROWSER
} from './actionTypes';
/** /**
* Returns a Redux action which signals that the UnsupportedMobileBrowser which * Returns a Redux action which signals that the UnsupportedMobileBrowser which
@ -22,20 +19,3 @@ export function dismissMobileAppPromo() {
type: DISMISS_MOBILE_APP_PROMO type: DISMISS_MOBILE_APP_PROMO
}; };
} }
/**
* Sets unsupported browser object.
*
* @param {Object} unsupportedBrowser - Object describing the unsupported
* browser.
* @returns {{
* type: SET_UNSUPPORTED_BROWSER,
* unsupportedBrowser: Object
* }}
*/
export function setUnsupportedBrowser(unsupportedBrowser) {
return {
type: SET_UNSUPPORTED_BROWSER,
unsupportedBrowser
};
}

View File

@ -39,8 +39,9 @@ export default class UnsupportedDesktopBrowser extends Component {
<a <a
className = { `${NS}__link` } className = { `${NS}__link` }
href = { FIREFOX }>Firefox</a> or&nbsp; href = { FIREFOX }>Firefox</a> or&nbsp;
{ this._showSafariLinkIfRequired() } {
{ this._showIELinkIfRequired() }. this._renderOSSpecificBrowserDownloadLink()
}
</p> </p>
<HideNotificationBarStyle /> <HideNotificationBarStyle />
@ -48,36 +49,36 @@ export default class UnsupportedDesktopBrowser extends Component {
); );
} }
/**
* Depending on the platform returns the link to IE browser.
*
* @returns {ReactElement|null}
* @private
*/
_showIELinkIfRequired() {
if (Platform.OS === 'windows') {
return (
<a
className = { `${NS}__link` }
href = { IE }>IE</a>
);
}
return null;
}
/** /**
* Depending on the platform returns the link to Safari browser. * Depending on the platform returns the link to Safari browser.
* *
* @returns {ReactElement|null} * @returns {ReactElement|null}
* @private * @private
*/ */
_showSafariLinkIfRequired() { _renderOSSpecificBrowserDownloadLink() {
if (Platform.OS === 'mac') { let link;
let text;
switch (Platform.OS) {
case 'macos':
link = SAFARI;
text = 'Safari';
break;
case 'windows':
link = IE;
text = 'Internet Explorer';
break;
}
if (typeof link !== 'undefined') {
return ( return (
<a <a
className = { `${NS}__link` } className = { `${NS}__link` }
href = { SAFARI }>Safari</a> href = { link }>
{
text
}
</a>
); );
} }

View File

@ -1,4 +1,5 @@
export * from './actions'; export * from './actions';
export * from './components'; export * from './components';
import './middleware';
import './reducer'; import './reducer';

View File

@ -0,0 +1,52 @@
import { appNavigate } from '../app';
import { SET_WEBRTC_READY } from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux';
/**
* Middleware that dispatches appNavigate when WebRTC readiness changes.
*
* @param {Store} store - The Redux store.
* @returns {Function}
* @private
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case SET_WEBRTC_READY:
return _setWebRTCReady(store, next, action);
}
return next(action);
});
/**
* Notifies the feature unsupported-browser that the action SET_WEBRTC_READY is
* being dispatched within a specific Redux store.
*
* @param {Store} store - The Redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The Redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The Redux action SET_WEBRTC_READY which is being
* dispatched in the specified store.
* @returns {Object} The new state that is the result of the reduction of the
* specified action.
* @private
*/
function _setWebRTCReady(store, next, action) {
const nextState = next(action);
// FIXME The feature unsupported-browser needs to notify the app that it may
// need to render a different Component at its current location because the
// execution enviroment has changed. The current location is not necessarily
// available through window.location (e.g. on mobile) but the following
// works at the time of this writing.
const windowLocation = window.location;
if (windowLocation) {
const href = windowLocation.href;
href && store.dispatch(appNavigate(href));
}
return nextState;
}

View File

@ -1,9 +1,6 @@
import { ReducerRegistry } from '../base/redux'; import { ReducerRegistry } from '../base/redux';
import { import { DISMISS_MOBILE_APP_PROMO } from './actionTypes';
DISMISS_MOBILE_APP_PROMO,
SET_UNSUPPORTED_BROWSER
} from './actionTypes';
ReducerRegistry.register( ReducerRegistry.register(
'features/unsupported-browser', 'features/unsupported-browser',
@ -24,11 +21,6 @@ ReducerRegistry.register(
*/ */
mobileAppPromoDismissed: true mobileAppPromoDismissed: true
}; };
case SET_UNSUPPORTED_BROWSER:
return {
...state,
...action.unsupportedBrowser
};
} }
return state; return state;