ing
This commit is contained in:
parent
e0f91c03a0
commit
f7504d9951
12
package.json
12
package.json
|
@ -24,12 +24,12 @@
|
|||
"@types/history": "^4.6.0",
|
||||
"@types/jest": "^19.2.4",
|
||||
"@types/prop-types": "^15.5.1",
|
||||
"@types/react": "^15.0.32",
|
||||
"@types/react": "^15.0.38",
|
||||
"@types/react-addons-test-utils": "^0.14.19",
|
||||
"@types/react-dom": "^15.5.1",
|
||||
"@types/react-redux": "^4.4.45",
|
||||
"@types/react-router": "^4.0.12",
|
||||
"@types/react-router-dom": "^4.0.5",
|
||||
"@types/react-redux": "^4.4.46",
|
||||
"@types/react-router": "^4.0.14",
|
||||
"@types/react-router-dom": "^4.0.7",
|
||||
"@types/react-tap-event-plugin": "^0.0.30",
|
||||
"@types/redux": "^3.6.0",
|
||||
"awesome-typescript-loader": "^3.1.3",
|
||||
|
@ -71,8 +71,8 @@
|
|||
"react-dom": "^15.6.1",
|
||||
"react-immutable-proptypes": "^2.1.0",
|
||||
"react-redux": "^5.0.5",
|
||||
"react-router": "^4.1.1",
|
||||
"react-router-dom": "^4.1.1",
|
||||
"react-router": "^4.1.2",
|
||||
"react-router-dom": "^4.1.2",
|
||||
"react-router-redux": "next",
|
||||
"react-tap-event-plugin": "^2.0.1",
|
||||
"redux": "^3.7.1",
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div id="react-placeholder"></div>
|
||||
<div id="appContainer"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
29
src/ts/@overflow/app/config/index.ts
Normal file
29
src/ts/@overflow/app/config/index.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Container Configuration
|
||||
export interface ContainerConfig {
|
||||
placeholderID: string;
|
||||
}
|
||||
const containerConfig: ContainerConfig = {
|
||||
placeholderID: 'appContainer',
|
||||
};
|
||||
|
||||
// RPC Server Configuration
|
||||
export interface RPCConfig {
|
||||
url: string;
|
||||
}
|
||||
const rpcConfig: RPCConfig = {
|
||||
url: 'ws://127.0.0.1:18081/rpc',
|
||||
};
|
||||
|
||||
|
||||
// Configuration
|
||||
export interface Config {
|
||||
container: ContainerConfig;
|
||||
rpc: RPCConfig;
|
||||
}
|
||||
|
||||
const config: Config = {
|
||||
container: containerConfig,
|
||||
rpc: rpcConfig,
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -5,11 +5,19 @@ import { fork } from 'redux-saga/effects';
|
|||
import { Provider } from 'react-redux';
|
||||
import { ConnectedRouter } from 'react-router-redux';
|
||||
import * as injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
import { Store } from 'redux';
|
||||
|
||||
import createSagaMiddleware, { SagaMiddleware } from 'redux-saga';
|
||||
|
||||
import Platform from '@overflow/commons/platform';
|
||||
import AppContext from '@overflow/commons/context';
|
||||
import * as AppContextLifecycleActions from '@overflow/commons/context/redux/action/lifecycle';
|
||||
import WebSocketRPC from '@overflow/commons/websocket/WebSocketRPC';
|
||||
import { History } from 'history';
|
||||
import { store, sagaMiddleware, history } from './redux/store';
|
||||
|
||||
import appConfig, { Config } from './config';
|
||||
|
||||
import sagas from './redux/saga';
|
||||
// import routes from './router';
|
||||
|
||||
|
@ -17,9 +25,76 @@ import {Layout} from './views/layout/Layout';
|
|||
|
||||
injectTapEventPlugin();
|
||||
|
||||
function* app(): any {
|
||||
const appContainer: HTMLElement = yield Platform.getAppContainer('react-placeholder');
|
||||
const rpcURL = 'ws://127.0.0.1:18081/rpc';
|
||||
|
||||
|
||||
class App {
|
||||
private config: Config;
|
||||
private container: HTMLElement;
|
||||
private context: AppContext;
|
||||
private rpcClient: WebSocketRPC;
|
||||
private store: Store<any>;
|
||||
private sagaMiddleware: SagaMiddleware<any>;
|
||||
private history: History;
|
||||
|
||||
public constructor() {
|
||||
this.config = appConfig;
|
||||
this.sagaMiddleware = sagaMiddleware;
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
try {
|
||||
this.container = await Platform.getAppContainer(this.config.container.placeholderID);
|
||||
this.displayLoading();
|
||||
|
||||
this.context = await this.initContext();
|
||||
// this.rpcClient = await this.initRpcClient();
|
||||
await this.initSagaEffect();
|
||||
|
||||
this.store.dispatch(AppContextLifecycleActions.initialized());
|
||||
|
||||
this.displayApp();
|
||||
} catch (e) {
|
||||
this.displayError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public initContext(): Promise<AppContext> {
|
||||
const appContext = new Promise<AppContext>(resolve => {
|
||||
const context = AppContext.getContext();
|
||||
resolve(context);
|
||||
});
|
||||
|
||||
return appContext;
|
||||
}
|
||||
|
||||
public initRpcClient(): Promise<WebSocketRPC> {
|
||||
const rpcClient = new Promise<WebSocketRPC>((resolve, reject) => {
|
||||
let client = new WebSocketRPC(this.config.rpc.url);
|
||||
client.initialize()
|
||||
.then(() => {
|
||||
resolve(client);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
return rpcClient;
|
||||
}
|
||||
|
||||
public initSagaEffect(): Promise<void> {
|
||||
const rpcClient = new Promise<void>(resolve => {
|
||||
this.sagaMiddleware.run(sagas);
|
||||
resolve();
|
||||
});
|
||||
|
||||
return rpcClient;
|
||||
}
|
||||
|
||||
|
||||
private displayLoading(): void {
|
||||
ReactDOM.render(
|
||||
<div style={{
|
||||
width: '100vw',
|
||||
|
@ -31,16 +106,36 @@ function* app(): any {
|
|||
}}>
|
||||
<h1>Loading...</h1>
|
||||
</div>,
|
||||
appContainer,
|
||||
this.container,
|
||||
);
|
||||
}
|
||||
|
||||
sagaMiddleware.run(sagas);
|
||||
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 {
|
||||
ReactDOM.render(
|
||||
<div><Layout /></div>,
|
||||
appContainer,
|
||||
this.container,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sagaMiddleware.run(app);
|
||||
let app = new App();
|
||||
app.start();
|
||||
|
||||
|
|
|
@ -16,6 +16,11 @@ import { PWChange } from '@overflow/member/react/components/PWChange';
|
|||
import { TableExampleSortable } from '@overflow/temp/react/components/TableExampleSortable';
|
||||
|
||||
|
||||
interface AppRouter {
|
||||
|
||||
}
|
||||
|
||||
|
||||
const routes = (
|
||||
<Switch>
|
||||
{/*<Route exact path='/'/>
|
||||
|
|
|
@ -15,12 +15,23 @@ class AppContext {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static put<T>(instance: T): void {
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static getContext(): AppContext {
|
||||
if (null === AppContext.context) {
|
||||
AppContext.context = new AppContext();
|
||||
}
|
||||
return AppContext.context;
|
||||
}
|
||||
|
||||
public static destroy(): void {
|
||||
console.log('AppContext has been destroyed.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AppContext;
|
||||
|
|
24
src/ts/@overflow/commons/context/redux/action/lifecycle.ts
Normal file
24
src/ts/@overflow/commons/context/redux/action/lifecycle.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import Action from '@overflow/commons/redux/Action';
|
||||
|
||||
// Action Type
|
||||
export type INITIALIZED = '@overflow/commons/context/INITIALIZED';
|
||||
export type WILL_DESTROY = '@overflow/commons/context/WILL_DESTROY';
|
||||
|
||||
export const INITIALIZED: INITIALIZED = '@overflow/commons/context/INITIALIZED';
|
||||
export const WILL_DESTROY: WILL_DESTROY = '@overflow/commons/context/WILL_DESTROY';
|
||||
|
||||
// Action Creater
|
||||
export type initialized = () => Action;
|
||||
export type willDestroy = () => Action;
|
||||
|
||||
export const initialized: initialized = (): Action => {
|
||||
return {
|
||||
type: INITIALIZED,
|
||||
};
|
||||
};
|
||||
|
||||
export const willDestroy: willDestroy = (): Action => {
|
||||
return {
|
||||
type: WILL_DESTROY,
|
||||
};
|
||||
};
|
25
src/ts/@overflow/commons/context/redux/saga/index.ts
Normal file
25
src/ts/@overflow/commons/context/redux/saga/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
import { SagaIterator } from 'redux-saga';
|
||||
import { call, fork, take } from 'redux-saga/effects';
|
||||
|
||||
import * as LifecycleActions from '../action/lifecycle';
|
||||
|
||||
import AppContext from '../../';
|
||||
|
||||
|
||||
function _destroy(): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
AppContext.destroy();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function* destroy(): SagaIterator {
|
||||
yield take(LifecycleActions.WILL_DESTROY);
|
||||
yield call(_destroy);
|
||||
}
|
||||
|
||||
|
||||
export default function* root(): SagaIterator {
|
||||
yield fork(destroy);
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import AppContext from '../context';
|
||||
|
||||
abstract class Platform {
|
||||
|
||||
public static * getAppContainer(containerId: string): IterableIterator<Promise<HTMLElement>> {
|
||||
const appContainer = yield new Promise<HTMLElement>(resolve => {
|
||||
public static getAppContainer(containerId: string): Promise<HTMLElement> {
|
||||
const appContainer = new Promise<HTMLElement>(resolve => {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
resolve(document.getElementById(containerId));
|
||||
});
|
||||
|
@ -9,6 +11,16 @@ abstract class Platform {
|
|||
|
||||
return appContainer;
|
||||
}
|
||||
|
||||
public static * getAppContext(): IterableIterator<Promise<AppContext>> {
|
||||
const appContext = yield new Promise<AppContext>(resolve => {
|
||||
const context = AppContext.getContext();
|
||||
resolve(context);
|
||||
});
|
||||
|
||||
return appContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Platform;
|
||||
|
|
|
@ -9,9 +9,7 @@ import {
|
|||
RPCRequest,
|
||||
RPCResponse,
|
||||
} from './protocol/rpc';
|
||||
import injectable from '../context/decorator/injectable';
|
||||
|
||||
export type OnConnectFunc = () => void;
|
||||
export type OnDisconnectFunc = () => void;
|
||||
export type OnResponseFunc = (response: any) => void;
|
||||
|
||||
|
@ -22,28 +20,42 @@ interface RequestQueue {
|
|||
reject: (reason?: any) => void;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
enum WebSocketStatus {
|
||||
NOT_CONNECT = 1,
|
||||
WILL_CONNECT,
|
||||
CONNECTED,
|
||||
WILL_DISCONNECT,
|
||||
}
|
||||
|
||||
enum WebSocketReadyState {
|
||||
CONNECTING = 0,
|
||||
OPEN = 1,
|
||||
CLOSING = 2,
|
||||
CLOSED = 3,
|
||||
}
|
||||
|
||||
|
||||
export default class WebSocketRPC {
|
||||
private url: string;
|
||||
private connStatus: WebSocketStatus;
|
||||
private conn: WebSocket;
|
||||
private isReady: boolean;
|
||||
private requestID: ID_TYPE = 0;
|
||||
|
||||
private onConnectListeners: OnConnectFunc[] = [];
|
||||
private onDisconnectListeners: OnDisconnectFunc[] = [];
|
||||
private onResponseListeners: Map<string, OnResponseFunc[]>;
|
||||
|
||||
private requestQueue: Map<ID_TYPE, RequestQueue>;
|
||||
|
||||
/**
|
||||
* name
|
||||
*/
|
||||
public constructor(url: string) {
|
||||
this.isReady = false;
|
||||
this.connStatus = WebSocketStatus.NOT_CONNECT;
|
||||
this.url = url;
|
||||
this.onResponseListeners = new Map();
|
||||
this.requestQueue = new Map();
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
public Call(method: string, params: any[]): Promise<any> {
|
||||
|
@ -59,33 +71,49 @@ export default class WebSocketRPC {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
public OnConnect(cb: OnConnectFunc): void {
|
||||
if (this.isReady) {
|
||||
cb();
|
||||
}
|
||||
this.onConnectListeners.push(cb);
|
||||
}
|
||||
|
||||
public OnDisconnect(cb: OnDisconnectFunc): void {
|
||||
this.onDisconnectListeners.push(cb);
|
||||
}
|
||||
|
||||
private initialize(): void {
|
||||
public initialize(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
this.conn = new WebSocket(this.url);
|
||||
this.conn.onopen = (e: Event): any => {
|
||||
this.fireOnConnect();
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
this.connStatus = WebSocketStatus.WILL_CONNECT;
|
||||
this.conn.onopen = (ev: Event): any => {
|
||||
this.connStatus = WebSocketStatus.CONNECTED;
|
||||
this.attachCallback();
|
||||
resolve();
|
||||
this.isReady = true;
|
||||
return null;
|
||||
};
|
||||
|
||||
this.conn.onclose = (e: CloseEvent): any => {
|
||||
this.conn.onerror = (ev: Event): any => {
|
||||
this.connStatus = WebSocketStatus.NOT_CONNECT;
|
||||
reject(new Error('Cannot connect to API Server.'));
|
||||
return null;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private attachCallback(): void {
|
||||
this.conn.onopen = (ev: Event): any => {
|
||||
this.connStatus = WebSocketStatus.CONNECTED;
|
||||
this.isReady = true;
|
||||
return null;
|
||||
};
|
||||
|
||||
this.conn.onclose = (ev: CloseEvent): any => {
|
||||
this.connStatus = WebSocketStatus.NOT_CONNECT;
|
||||
this.fireOnDisconnect();
|
||||
return null;
|
||||
};
|
||||
|
||||
this.conn.onmessage = (e: MessageEvent): any => {
|
||||
let response: ProtocolResponse<ID_TYPE> = JSON.parse(e.data);
|
||||
this.conn.onmessage = (ev: MessageEvent): any => {
|
||||
let response: ProtocolResponse<ID_TYPE> = JSON.parse(ev.data);
|
||||
let res: RPCResponse = JSON.parse(response.getBody());
|
||||
const requestID = response.getID();
|
||||
const error = response.getError();
|
||||
|
@ -105,16 +133,15 @@ export default class WebSocketRPC {
|
|||
// perhaps sever side send message
|
||||
}
|
||||
|
||||
this.fireOnMessage(e);
|
||||
this.fireOnMessage(ev);
|
||||
};
|
||||
|
||||
this.conn.onerror = (ev: Event): any => {
|
||||
console.log(ev);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private fireOnConnect(): void {
|
||||
for (let i = 0; i < this.onConnectListeners.length; i++) {
|
||||
this.onConnectListeners[i]();
|
||||
}
|
||||
}
|
||||
|
||||
private fireOnDisconnect(): void {
|
||||
for (let i = 0; i < this.onDisconnectListeners.length; i++) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import { Button,
|
|||
import { TargetTable } from '@overflow/temp/react/components/Targets';
|
||||
import Probe from '@overflow/probe/api/model/Probe';
|
||||
|
||||
|
||||
export interface StateProps {
|
||||
probe: Probe;
|
||||
}
|
||||
|
@ -45,7 +44,6 @@ export class ProbeDetail extends React.Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export class ProbeBasicInfo extends React.Component<Props, State> {
|
||||
|
||||
constructor(props: Props, context: State) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user