diff --git a/react/features/base/connection/actions.web.js b/react/features/base/connection/actions.web.js index 4e48024d5..da4339781 100644 --- a/react/features/base/connection/actions.web.js +++ b/react/features/base/connection/actions.web.js @@ -2,15 +2,17 @@ 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 { SET_DOMAIN } from './actionTypes'; -import { appNavigate } from '../../app'; -import { setUnsupportedBrowser } from '../../unsupported-browser'; - declare var APP: Object; declare var config: Object; @@ -77,28 +79,19 @@ export function connect() { APP.UI.promptDisplayName(); } }) - .catch(err => { + .catch(error => { APP.UI.hideRingOverLay(); APP.API.notifyConferenceLeft(APP.conference.roomName); - logger.error(err); + logger.error(error); - dispatch(setUnsupportedBrowser(err)); - - // If during the conference initialization was defined that - // browser doesn't support WebRTC then we should define - // which route to render. - dispatch(appNavigate(room)); - - // Force reinitialization of the conference if WebRTC is ready. - 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)); - }); + // TODO The following are in fact Errors raised by + // JitsiMeetJS.init() which should be taken care of in + // features/base/lib-jitsi-meet but we are not there yet on the + // Web at the time of this writing. + switch (error.name) { + case WEBRTC_NOT_READY: + case WEBRTC_NOT_SUPPORTED: + dispatch(libInitError(error)); } }); }; diff --git a/react/features/base/lib-jitsi-meet/actionTypes.js b/react/features/base/lib-jitsi-meet/actionTypes.js index ace4605e1..17ba367ff 100644 --- a/react/features/base/lib-jitsi-meet/actionTypes.js +++ b/react/features/base/lib-jitsi-meet/actionTypes.js @@ -58,3 +58,13 @@ export const LIB_WILL_INIT = Symbol('LIB_WILL_INIT'); * } */ 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'); diff --git a/react/features/base/lib-jitsi-meet/actions.js b/react/features/base/lib-jitsi-meet/actions.js index 17b3c4594..1cc10722a 100644 --- a/react/features/base/lib-jitsi-meet/actions.js +++ b/react/features/base/lib-jitsi-meet/actions.js @@ -7,7 +7,8 @@ import { LIB_INIT_ERROR, LIB_WILL_DISPOSE, LIB_WILL_INIT, - SET_CONFIG + SET_CONFIG, + SET_WEBRTC_READY } from './actionTypes'; declare var APP: Object; @@ -100,3 +101,54 @@ export function setConfig(config: Object) { 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; + } + } + } + }; +} diff --git a/react/features/base/lib-jitsi-meet/constants.js b/react/features/base/lib-jitsi-meet/constants.js new file mode 100644 index 000000000..9b114fbd5 --- /dev/null +++ b/react/features/base/lib-jitsi-meet/constants.js @@ -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'; diff --git a/react/features/base/lib-jitsi-meet/index.js b/react/features/base/lib-jitsi-meet/index.js index 2081b6289..5f5fa3b53 100644 --- a/react/features/base/lib-jitsi-meet/index.js +++ b/react/features/base/lib-jitsi-meet/index.js @@ -15,6 +15,7 @@ export const JitsiTrackEvents = JitsiMeetJS.events.track; export * from './actions'; export * from './actionTypes'; +export * from './constants'; export * from './functions'; import './middleware'; diff --git a/react/features/base/lib-jitsi-meet/middleware.js b/react/features/base/lib-jitsi-meet/middleware.js index 26bca5c8f..2f0401078 100644 --- a/react/features/base/lib-jitsi-meet/middleware.js +++ b/react/features/base/lib-jitsi-meet/middleware.js @@ -1,8 +1,9 @@ import { PARTICIPANT_LEFT } from '../participants'; import { MiddlewareRegistry } from '../redux'; -import { disposeLib, initLib } from './actions'; -import { SET_CONFIG } from './actionTypes'; +import { disposeLib, initLib, setWebRTCReady } from './actions'; +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 @@ -16,6 +17,13 @@ import { SET_CONFIG } from './actionTypes'; */ MiddlewareRegistry.register(store => next => action => { switch (action.type) { + case LIB_DID_INIT: + store.dispatch(setWebRTCReady(true)); + break; + + case LIB_INIT_ERROR: + return _libInitError(store, next, action); + case PARTICIPANT_LEFT: action.participant.local && store.dispatch(disposeLib()); break; @@ -27,6 +35,44 @@ MiddlewareRegistry.register(store => 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 * dispatched within a specific Redux store. diff --git a/react/features/base/lib-jitsi-meet/reducer.js b/react/features/base/lib-jitsi-meet/reducer.js index 2ea127f11..b85c544da 100644 --- a/react/features/base/lib-jitsi-meet/reducer.js +++ b/react/features/base/lib-jitsi-meet/reducer.js @@ -4,7 +4,8 @@ import { LIB_DID_DISPOSE, LIB_DID_INIT, LIB_INIT_ERROR, - SET_CONFIG + SET_CONFIG, + SET_WEBRTC_READY } from './actionTypes'; /** @@ -66,6 +67,12 @@ ReducerRegistry.register( case SET_CONFIG: return _setConfig(state, action); + case SET_WEBRTC_READY: + return { + ...state, + webRTCReady: action.webRTCReady + }; + default: return state; } diff --git a/react/features/base/react/Platform.web.js b/react/features/base/react/Platform.web.js index 78cb6bd9c..7066dfb01 100644 --- a/react/features/base/react/Platform.web.js +++ b/react/features/base/react/Platform.web.js @@ -7,10 +7,10 @@ if (userAgent.match(/Android/i)) { OS = 'android'; } else if (userAgent.match(/iP(ad|hone|od)/i)) { 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'; -} else if (userAgent.match(/mac/i)) { - OS = 'mac'; } /** diff --git a/react/features/base/util/interceptComponent.js b/react/features/base/util/interceptComponent.js index 8ae0b2619..3994dd6c9 100644 --- a/react/features/base/util/interceptComponent.js +++ b/react/features/base/util/interceptComponent.js @@ -44,12 +44,22 @@ const _RULES = [ } }, state => { - switch (state['features/unsupported-browser'].name) { - case 'WEBRTC_NOT_READY': - return PluginRequiredBrowser; + const { webRTCReady } = state['features/base/lib-jitsi-meet']; - case 'WEBRTC_NOT_SUPPORTED': - return UnsupportedDesktopBrowser; + switch (typeof webRTCReady) { + 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; } } ]; diff --git a/react/features/unsupported-browser/actionTypes.js b/react/features/unsupported-browser/actionTypes.js index 55676c6c2..599fa4d94 100644 --- a/react/features/unsupported-browser/actionTypes.js +++ b/react/features/unsupported-browser/actionTypes.js @@ -16,14 +16,3 @@ import { Symbol } from '../base/react'; * } */ 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'); diff --git a/react/features/unsupported-browser/actions.js b/react/features/unsupported-browser/actions.js index f732b1775..c07c8a7d5 100644 --- a/react/features/unsupported-browser/actions.js +++ b/react/features/unsupported-browser/actions.js @@ -1,7 +1,4 @@ -import { - DISMISS_MOBILE_APP_PROMO, - SET_UNSUPPORTED_BROWSER -} from './actionTypes'; +import { DISMISS_MOBILE_APP_PROMO } from './actionTypes'; /** * Returns a Redux action which signals that the UnsupportedMobileBrowser which @@ -22,20 +19,3 @@ export function dismissMobileAppPromo() { 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 - }; -} diff --git a/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js b/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js index dd7820743..2ef84e05c 100644 --- a/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js +++ b/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js @@ -39,8 +39,9 @@ export default class UnsupportedDesktopBrowser extends Component { Firefox or  - { this._showSafariLinkIfRequired() } - { this._showIELinkIfRequired() }. + { + this._renderOSSpecificBrowserDownloadLink() + }

@@ -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 ( - IE - ); - } - - return null; - } - /** * Depending on the platform returns the link to Safari browser. * * @returns {ReactElement|null} * @private */ - _showSafariLinkIfRequired() { - if (Platform.OS === 'mac') { + _renderOSSpecificBrowserDownloadLink() { + 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 ( Safari + href = { link }> + { + text + } + ); } diff --git a/react/features/unsupported-browser/index.js b/react/features/unsupported-browser/index.js index 582e1f9dd..7f0ef0251 100644 --- a/react/features/unsupported-browser/index.js +++ b/react/features/unsupported-browser/index.js @@ -1,4 +1,5 @@ export * from './actions'; export * from './components'; +import './middleware'; import './reducer'; diff --git a/react/features/unsupported-browser/middleware.js b/react/features/unsupported-browser/middleware.js new file mode 100644 index 000000000..011614756 --- /dev/null +++ b/react/features/unsupported-browser/middleware.js @@ -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; +} diff --git a/react/features/unsupported-browser/reducer.js b/react/features/unsupported-browser/reducer.js index 92a442a26..72fc5393a 100644 --- a/react/features/unsupported-browser/reducer.js +++ b/react/features/unsupported-browser/reducer.js @@ -1,9 +1,6 @@ import { ReducerRegistry } from '../base/redux'; -import { - DISMISS_MOBILE_APP_PROMO, - SET_UNSUPPORTED_BROWSER -} from './actionTypes'; +import { DISMISS_MOBILE_APP_PROMO } from './actionTypes'; ReducerRegistry.register( 'features/unsupported-browser', @@ -24,11 +21,6 @@ ReducerRegistry.register( */ mobileAppPromoDismissed: true }; - case SET_UNSUPPORTED_BROWSER: - return { - ...state, - ...action.unsupportedBrowser - }; } return state;