@ -1,11 +1,14 @@
import React , { Component } from 'react' ;
import { Provider } from 'react-redux' ;
import { compose , createStore } from 'redux' ;
import Thunk from 'redux-thunk' ;
import { RouteRegistry } from '../../base/navigator' ;
import {
localParticipantJoined ,
localParticipantLeft
} from '../../base/participants' ;
import { MiddlewareRegistry , ReducerRegistry } from '../../base/redux' ;
import {
appNavigate ,
@ -45,11 +48,18 @@ export class AbstractApp extends Component {
this . state = {
/ * *
* The Route rendered by this App .
* The Route rendered by this AbstractA pp .
*
* @ type { Route }
* /
route : undefined
route : undefined ,
/ * *
* The Redux store used by this AbstractApp .
*
* @ type { Store }
* /
store : this . _maybeCreateStore ( props )
} ;
}
@ -60,7 +70,7 @@ export class AbstractApp extends Component {
* @ inheritdoc
* /
componentWillMount ( ) {
const dispatch = this . props . store . dispatch ;
const dispatch = this . _getStore ( ) . dispatch ;
dispatch ( appWillMount ( this ) ) ;
@ -69,6 +79,32 @@ export class AbstractApp extends Component {
this . _openURL ( this . _getDefaultURL ( ) ) ;
}
/ * *
* Notifies this mounted React Component that it will receive new props .
* Makes sure that this AbstractApp has a Redux store to use .
*
* @ inheritdoc
* @ param { Object } nextProps - The read - only React Component props that this
* instance will receive .
* @ returns { void }
* /
componentWillReceiveProps ( nextProps ) {
// The consumer of this AbstractApp did not provide a Redux store.
if ( typeof nextProps . store === 'undefined'
// The consumer of this AbstractApp did provide a Redux store
// before. Which means that the consumer changed their mind. In
// such a case this instance should create its own internal
// Redux store. If the consumer did not provide a Redux store
// before, then this instance is using its own internal Redux
// store already.
&& typeof this . props . store !== 'undefined' ) {
this . setState ( {
store : this . _maybeCreateStore ( nextProps )
} ) ;
}
}
/ * *
* Dispose lib - jitsi - meet and remove local participant when component is
* going to be unmounted .
@ -76,7 +112,7 @@ export class AbstractApp extends Component {
* @ inheritdoc
* /
componentWillUnmount ( ) {
const dispatch = this . props . store . dispatch ;
const dispatch = this . _getStore ( ) . dispatch ;
dispatch ( localParticipantLeft ( ) ) ;
@ -94,7 +130,7 @@ export class AbstractApp extends Component {
if ( route ) {
return (
< Provider store = { this . props . store } >
< Provider store = { this . _getStore ( ) } >
{
this . _createElement ( route . component )
}
@ -142,6 +178,36 @@ export class AbstractApp extends Component {
return React . createElement ( component , { ... thisProps , ... props } ) ;
}
/ * *
* Initializes a new Redux store instance suitable for use by
* this AbstractApp .
*
* @ private
* @ returns { Store } - A new Redux store instance suitable for use by
* this AbstractApp .
* /
_createStore ( ) {
// Create combined reducer from all reducers in ReducerRegistry.
const reducer = ReducerRegistry . combineReducers ( ) ;
// Apply all registered middleware from the MiddlewareRegistry and
// additional 3rd party middleware:
// - Thunk - allows us to dispatch async actions easily. For more info
// @see https://github.com/gaearon/redux-thunk.
let middleware = MiddlewareRegistry . applyMiddleware ( Thunk ) ;
// Try to enable Redux DevTools Chrome extension in order to make it
// available for the purposes of facilitating development.
let devToolsExtension ;
if ( typeof window === 'object'
&& ( devToolsExtension = window . devToolsExtension ) ) {
middleware = compose ( middleware , devToolsExtension ( ) ) ;
}
return createStore ( reducer , middleware ) ;
}
/ * *
* Gets the default URL to be opened when this App mounts .
*
@ -189,6 +255,22 @@ export class AbstractApp extends Component {
return 'https://meet.jit.si' ;
}
/ * *
* Gets the Redux store used by this AbstractApp .
*
* @ protected
* @ returns { Store } - The Redux store used by this AbstractApp .
* /
_getStore ( ) {
let store = this . state . store ;
if ( typeof store === 'undefined' ) {
store = this . props . store ;
}
return store ;
}
/ * *
* Gets a Location object from the window with information about the current
* location of the document . Explicitly defined to allow extenders to
@ -204,6 +286,30 @@ export class AbstractApp extends Component {
return undefined ;
}
/ * *
* Creates a Redux store to be used by this AbstractApp if such as store is
* not defined by the consumer of this AbstractApp through its
* read - only React Component props .
*
* @ param { Object } props - The read - only React Component props that will
* eventually be received by this AbstractApp .
* @ private
* @ returns { Store } - The Redux store to be used by this AbstractApp .
* /
_maybeCreateStore ( props ) {
// The application Jitsi Meet is architected with Redux. However, I do
// not want consumers of the App React Component to be forced into
// dealing with Redux. If the consumer did not provide an external Redux
// store, utilize an internal Redux store.
let store = props . store ;
if ( typeof store === 'undefined' ) {
store = this . _createStore ( ) ;
}
return store ;
}
/ * *
* Navigates to a specific Route .
*
@ -269,6 +375,6 @@ export class AbstractApp extends Component {
* @ returns { void }
* /
_openURL ( url ) {
this . props . store . dispatch ( appNavigate ( url ) ) ;
this . _getStore ( ) . dispatch ( appNavigate ( url ) ) ;
}
}