From f7504d9951767320845df0a2bc4f820458c5ea2d Mon Sep 17 00:00:00 2001 From: crusader Date: Thu, 20 Jul 2017 18:46:42 +0900 Subject: [PATCH] ing --- package.json | 12 +- public/index.html | 2 +- src/ts/@overflow/app/config/index.ts | 29 ++++ src/ts/@overflow/app/index.tsx | 137 +++++++++++++++--- src/ts/@overflow/app/router/index.tsx | 5 + src/ts/@overflow/commons/context/index.ts | 11 ++ .../commons/context/redux/action/lifecycle.ts | 24 +++ .../commons/context/redux/saga/index.ts | 25 ++++ src/ts/@overflow/commons/platform/index.ts | 16 +- .../commons/websocket/WebSocketRPC.ts | 81 +++++++---- .../probe/react/components/ProbeDetail.tsx | 2 - 11 files changed, 285 insertions(+), 59 deletions(-) create mode 100644 src/ts/@overflow/app/config/index.ts create mode 100644 src/ts/@overflow/commons/context/redux/action/lifecycle.ts create mode 100644 src/ts/@overflow/commons/context/redux/saga/index.ts diff --git a/package.json b/package.json index b13c904..7d2f3b1 100644 --- a/package.json +++ b/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", diff --git a/public/index.html b/public/index.html index 1a631af..e09ea73 100644 --- a/public/index.html +++ b/public/index.html @@ -16,7 +16,7 @@ -
+
\ No newline at end of file diff --git a/src/ts/@overflow/app/config/index.ts b/src/ts/@overflow/app/config/index.ts new file mode 100644 index 0000000..24ced6b --- /dev/null +++ b/src/ts/@overflow/app/config/index.ts @@ -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; diff --git a/src/ts/@overflow/app/index.tsx b/src/ts/@overflow/app/index.tsx index 7bced01..94f49cf 100644 --- a/src/ts/@overflow/app/index.tsx +++ b/src/ts/@overflow/app/index.tsx @@ -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,30 +25,117 @@ 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'; - ReactDOM.render( -
-

Loading...

-
, - appContainer, - ); - sagaMiddleware.run(sagas); +class App { + private config: Config; + private container: HTMLElement; + private context: AppContext; + private rpcClient: WebSocketRPC; + private store: Store; + private sagaMiddleware: SagaMiddleware; + private history: History; + + public constructor() { + this.config = appConfig; + this.sagaMiddleware = sagaMiddleware; + this.store = store; + } + + public async start(): Promise { + 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 { + const appContext = new Promise(resolve => { + const context = AppContext.getContext(); + resolve(context); + }); + + return appContext; + } + + public initRpcClient(): Promise { + const rpcClient = new Promise((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 { + const rpcClient = new Promise(resolve => { + this.sagaMiddleware.run(sagas); + resolve(); + }); + + return rpcClient; + } + + + private displayLoading(): void { + ReactDOM.render( +
+

Loading...

+
, + this.container, + ); + } + + private displayError(e: Error): void { + ReactDOM.render( +
+

{e.message}

+
, + this.container, + ); + } + + + private displayApp(): void { + ReactDOM.render( +
, + this.container, + ); + } - ReactDOM.render( -
, - appContainer, - ); } -sagaMiddleware.run(app); +let app = new App(); +app.start(); diff --git a/src/ts/@overflow/app/router/index.tsx b/src/ts/@overflow/app/router/index.tsx index e9ef123..5cef490 100644 --- a/src/ts/@overflow/app/router/index.tsx +++ b/src/ts/@overflow/app/router/index.tsx @@ -16,6 +16,11 @@ import { PWChange } from '@overflow/member/react/components/PWChange'; import { TableExampleSortable } from '@overflow/temp/react/components/TableExampleSortable'; +interface AppRouter { + +} + + const routes = ( {/* diff --git a/src/ts/@overflow/commons/context/index.ts b/src/ts/@overflow/commons/context/index.ts index 9d4f7d1..fbaa4f8 100644 --- a/src/ts/@overflow/commons/context/index.ts +++ b/src/ts/@overflow/commons/context/index.ts @@ -15,12 +15,23 @@ class AppContext { return null; } + public static put(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; diff --git a/src/ts/@overflow/commons/context/redux/action/lifecycle.ts b/src/ts/@overflow/commons/context/redux/action/lifecycle.ts new file mode 100644 index 0000000..c8f7e66 --- /dev/null +++ b/src/ts/@overflow/commons/context/redux/action/lifecycle.ts @@ -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, + }; +}; diff --git a/src/ts/@overflow/commons/context/redux/saga/index.ts b/src/ts/@overflow/commons/context/redux/saga/index.ts new file mode 100644 index 0000000..11620f1 --- /dev/null +++ b/src/ts/@overflow/commons/context/redux/saga/index.ts @@ -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 { + 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); +} diff --git a/src/ts/@overflow/commons/platform/index.ts b/src/ts/@overflow/commons/platform/index.ts index a77284f..3a85eeb 100644 --- a/src/ts/@overflow/commons/platform/index.ts +++ b/src/ts/@overflow/commons/platform/index.ts @@ -1,7 +1,9 @@ +import AppContext from '../context'; + abstract class Platform { - public static * getAppContainer(containerId: string): IterableIterator> { - const appContainer = yield new Promise(resolve => { + public static getAppContainer(containerId: string): Promise { + const appContainer = new Promise(resolve => { document.addEventListener('DOMContentLoaded', () => { resolve(document.getElementById(containerId)); }); @@ -9,6 +11,16 @@ abstract class Platform { return appContainer; } + + public static * getAppContext(): IterableIterator> { + const appContext = yield new Promise(resolve => { + const context = AppContext.getContext(); + resolve(context); + }); + + return appContext; + } + } export default Platform; diff --git a/src/ts/@overflow/commons/websocket/WebSocketRPC.ts b/src/ts/@overflow/commons/websocket/WebSocketRPC.ts index 7886fc1..db1836c 100644 --- a/src/ts/@overflow/commons/websocket/WebSocketRPC.ts +++ b/src/ts/@overflow/commons/websocket/WebSocketRPC.ts @@ -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; private requestQueue: Map; + /** * 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 { @@ -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 { - this.conn = new WebSocket(this.url); - this.conn.onopen = (e: Event): any => { - this.fireOnConnect(); + public initialize(): Promise { + return new Promise((resolve, reject) => { + try { + this.conn = new WebSocket(this.url); + } 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.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 = (e: CloseEvent): any => { + this.conn.onclose = (ev: CloseEvent): any => { + this.connStatus = WebSocketStatus.NOT_CONNECT; this.fireOnDisconnect(); return null; }; - this.conn.onmessage = (e: MessageEvent): any => { - let response: ProtocolResponse = JSON.parse(e.data); + this.conn.onmessage = (ev: MessageEvent): any => { + let response: ProtocolResponse = 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++) { diff --git a/src/ts/@overflow/probe/react/components/ProbeDetail.tsx b/src/ts/@overflow/probe/react/components/ProbeDetail.tsx index 533eb29..b331d77 100644 --- a/src/ts/@overflow/probe/react/components/ProbeDetail.tsx +++ b/src/ts/@overflow/probe/react/components/ProbeDetail.tsx @@ -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 { } } - export class ProbeBasicInfo extends React.Component { constructor(props: Props, context: State) {