import { LocalStorage } from '@fantaptik/core';
import { MODULE_NAME } from './consts';
import { createReducer, createStoreSelector } from './Reducer';
import Actions from './Actions';
import Session from './Session';
import SocketPlugin from './SocketPlugin';
import GobFactory from './components/GobFactory';
/**
* `Gob` combines {@link Actions}, {@link SocketPlugin}, {@link Reducer} and the {@link Components} into a shared namespace.
*
* Without `Gob` this package could only authenticate to a single server as all instances of `SocketPlugin` would
* be bound to the same Redux state.
*
* With `Gob` this package can authenticate with multiple servers that implement the *Server API* with each server's Redux
* state separated within the store.
*
* **Important** `Gob.dispatcher` needs to be set to the Redux store's dispatch function. This will happen automatically
* if you mount either `Gob.React.Guest` or `Gob.React.Authenticated` within your application. If you do not mount
* either of those components then you need to set `dispatcher` explicitly after creating your Redux store.
*
* @class
*/
class Gob {
/**
* Creates a new `Gob`.
*
* @param {string} id The id common to the elements in the `Gob`.
*/
constructor( id ) {
//
// Privatize some properties.
const actions = new Actions( id );
const session = new Session();
const reducer = createReducer( id, actions, session );
const storage = new LocalStorage( { prefix : `${MODULE_NAME}/Gob/${id}/` } );
this.props = {
id,
actions,
reducer,
session,
storage,
// The `dispatch` function for the Redux store.
dispatcher : null,
// The Redux store selector function.
storeSelector : null,
// Queued actions from `SocketPlugin` instances since they do not have a handle to the
// true dispatch method for the store.
dispatchQueue : [],
// The React components.
components : GobFactory( this ),
};
}
/**
* `storage` is the `LocalStorage` instance attached to this `Gob`.
*
* @type {LocalStorage}
*/
get storage() {
return this.props.storage;
}
/**
* `id` is the `id` value the `Gob` was created with.
*
* @readonly
* @type string
*/
get id() {
return this.props.id;
}
/**
* `dispatcher` is the `dispatch` function tied to the Redux store and must be set for `SocketPlugin` instances
* to communicate with the Redux store.
*
* Mounting `Gob.React.Guest` or `Gob.React.Authenticated` in the application will set this property automatically. If not
* mounting either of those components then you will need to set this property explicitly after creating your Redux
* store.
*
* @type {func}
*/
set dispatcher( dispatcherValue ) {
if( this.props.dispatcher === null ) {
this.props.dispatcher = dispatcherValue;
while( this.props.dispatchQueue.length > 0 ) {
this.props.dispatcher( this.props.dispatchQueue.shift() );
}
}
}
/**
* `Actions` are the Redux actions dispatched by this `Gob`.
*
* @readonly
* @type {Actions}
*/
get Actions() {
return this.props.actions;
}
/**
* `React` returns React components for this `Gob`.
*
* @readonly
* @type {Components}
*/
get React() {
return this.props.components;
}
/**
* `Reducer` is the Redux reducer tied to this `Gob`.
*
* @readonly
* @type {Reducer}
*/
get Reducer() {
return this.props.reducer;
}
/**
* `SocketPlugin` is a constructor to create instances of `SocketPlugin` tied to this `Gob`.
*
* When using this constructor you do not need to pass the `gob`, `session`, or `dispatch` members of
* the `SocketPlugin` options.
*
* @method
*/
SocketPlugin = ( opts ) => {
const { session } = this.props;
// Set options and create instance.
opts = opts || {};
opts = {
...opts,
// Pass a `dispatch` function that queues or immediately dispatches the action depending
// on if the `Gob` has a dispatcher or not.
dispatch : ( action ) => {
const { dispatcher, dispatchQueue } = this.props;
dispatcher === null ? dispatchQueue.push( action ) : dispatcher( action );
},
gob : this,
session,
};
const instance = new SocketPlugin( opts );
return instance;
}
/**
* `storeToProps` is the function used as the first argument to `connect()` when binding a React component
* to Redux.
*
* @method
* @param {Object} state The Redux store state.
*/
storeToProps = ( state ) => {
if( this.props.storeSelector === null ) {
this.props.storeSelector = createStoreSelector( state, item => {
const { __module, __gobId } = item || {};
return __module == MODULE_NAME && __gobId == this.props.id;
} );
}
return this.props.storeSelector( state );
}
}
export default Gob;