2017-09-27 18:33:10 +09:00

251 lines
6.1 KiB
TypeScript

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
applyMiddleware,
compose,
createStore,
GenericStoreEnhancer,
Middleware,
Store,
} from 'redux';
import {
Provider,
} from 'react-redux';
import {
ConnectedRouter,
routerMiddleware,
} from 'react-router-redux';
import createSagaMiddleware, {
SagaMiddleware,
SagaIterator,
} from 'redux-saga';
import {
fork,
} from 'redux-saga/effects';
import {
createHashHistory,
History,
} from 'history';
import {
AppContainer,
} from 'react-hot-loader';
import * as injectTapEventPlugin from 'react-tap-event-plugin';
import Platform from '@overflow/commons/platform';
import WebSocketRPC from '@overflow/commons/websocket/WebSocketRPC';
import ReducerContext from '@overflow/commons/redux/ReducerContext';
import SagaWatcher from '@overflow/commons/redux/saga/SagaWatcher';
import ServiceInvoker from '@overflow/commons/invoke/ServiceInvoker';
import appConfig, { Config, ReduxState } from './config';
import * as AppView from './views/App';
declare global {
interface Window {
devToolsExtension: () => any;
}
const process: any;
const module: any;
}
injectTapEventPlugin();
class OFApplication {
private static isProduction:boolean = process.env.NODE_ENV === 'production' ? true : false;
private static useReduxDevTools:boolean = window.devToolsExtension && !OFApplication.isProduction ? true : false;
private config: Config;
private container: HTMLElement;
private rpcClient: WebSocketRPC;
private store: Store<any>;
private sagaMiddleware: SagaMiddleware<any>;
private history: History;
public constructor() {
this.config = appConfig;
this.history = createHashHistory();
}
public static main(): void {
let app = new OFApplication();
app.run();
}
public async run(): Promise<void> {
try {
this.container = await Platform.getAppContainer(this.config.container.placeholderID);
this.displayLoading();
this.rpcClient = await this.initRpcClient();
// this.context.PouchFactory.registerPouch(this.rpcClient);
await this.initRedux();
this.displayApp();
} catch (e) {
console.error(e);
this.displayError(e);
}
}
private initRpcClient(): Promise<WebSocketRPC> {
const rpcClient = new Promise<WebSocketRPC>((resolve, reject) => {
let serviceInvoker = new ServiceInvoker(this.config.service);
let client = new WebSocketRPC(this.config.rpc.url);
client.initialize()
.then(() => {
resolve(client);
})
.catch((err: any) => {
reject(err);
});
// resolve(client);
});
return rpcClient;
}
private initRedux(): Promise<void> {
const init = new Promise<void>(resolve => {
// state tree
// reducer
for (let reducerMap of this.config.redux.reducerMaps) {
ReducerContext.putReducers(reducerMap);
}
// middleware
let middlewares: Middleware[] = new Array();
this.sagaMiddleware = createSagaMiddleware();
middlewares.push(this.sagaMiddleware);
let routerReduxMiddleware = routerMiddleware(this.history);
middlewares.push(routerReduxMiddleware);
// store
let middleware: GenericStoreEnhancer = applyMiddleware(...middlewares);
this.store = createStore(
ReducerContext.reducer,
this.config.redux.state,
OFApplication.useReduxDevTools ? compose(middleware, window.devToolsExtension()) : middleware,
);
// saga
this.sagaMiddleware.run(this.initReduxSagaWarchers, this.config, this.rpcClient);
resolve();
});
return init;
}
private * initReduxSagaWarchers(config: Config, rpcClient: WebSocketRPC): SagaIterator {
let sagaWatchers: Class<SagaWatcher>[] = config.redux.sagaWatchers;
for (let type of sagaWatchers) {
let instance = Object.create(type.prototype);
instance.constructor.call(instance);
if (type.prototype.hasOwnProperty('setWebSocketRPC')) {
instance.setWebSocketRPC.call(instance, rpcClient);
}
if (type.prototype.hasOwnProperty('setRestUrl')) {
instance.setRestUrl.call(instance, config.rest.url);
}
yield fork({context: instance, fn: instance.watch});
}
}
private displayLoading(): void {
ReactDOM.render(
<div style={{
width: '100vw',
height: '100vh',
backgroundColor: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<h1>Loading......</h1>
</div>,
this.container,
);
}
private displayError(e: Error): void {
ReactDOM.render(
<div style={{
width: '100vw',
height: '100vh',
backgroundColor: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<h1>{e.message}</h1>
</div>,
this.container,
);
}
private displayApp(): void {
OFApplication.isProduction ? this.displayProductionApp() : this.displayDebugApp();
}
private displayProductionApp(): void {
ReactDOM.render(
<Provider store={this.store}>
<ConnectedRouter history={this.history}>
<AppView.default />
</ConnectedRouter>
</Provider>,
this.container,
);
}
private displayDebugApp(): void {
if (module.hot) {
module.hot.accept('./views/App', async () => {
const NextApp = (await import('./views/App')).default;
ReactDOM.render(
<AppContainer>
<Provider store={this.store}>
<ConnectedRouter history={this.history}>
<NextApp />
</ConnectedRouter>
</Provider>
</AppContainer>
,
this.container,
);
});
}
ReactDOM.render(
<AppContainer>
<Provider store={this.store}>
<ConnectedRouter history={this.history}>
<AppView.default />
</ConnectedRouter>
</Provider>
</AppContainer>
,
this.container,
);
}
}
OFApplication.main();