Add: add mobx for state management

This commit is contained in:
jas0ncn 2018-09-11 00:54:22 +08:00
parent 8f97504649
commit b47b6e0e5b
13 changed files with 283 additions and 14 deletions

14
src/lib/createStore.ts Normal file
View File

@ -0,0 +1,14 @@
import { createHashHistory } from 'history'
import { configure } from 'mobx'
import { RouterStore, ConfigStore } from '@stores'
// prepare MobX stores
configure({ enforceActions: 'observed' })
const history = createHashHistory()
export const rootStores = {
router: new RouterStore(history),
config: new ConfigStore()
}
export const storeKeys = Object.keys(rootStores)

24
src/lib/helper.ts Normal file
View File

@ -0,0 +1,24 @@
export function getLocalStorageItem (key: string) {
return window.localStorage.getItem(key)
}
export function setLocalStorageItem (key: string, value: string) {
return window.localStorage.setItem(key, value)
}
export function removeLocalStorageItem (key: string) {
return window.localStorage.removeItem(key)
}
/**
* to return Promise<[T, Error]>
* @param {Promise<T>} promise
*/
export async function to <T, E = Error> (promise: any): Promise<[T, E]> {
try {
const ret = await promise
return [ret, null as E]
} catch (e) {
return [null as T, e]
}
}

17
src/models/BaseProps.ts Normal file
View File

@ -0,0 +1,17 @@
import { RouteComponentProps } from 'react-router'
import { RouterStore, ConfigStore } from '@stores'
/**
* expose base router component props
* and mobx store to props
*/
export interface BaseRouterProps extends RouteComponentProps<any>, BaseProps {}
/**
* use when component is inject by mobx
*/
export interface BaseProps {
styles?: any
router?: RouterStore
config?: ConfigStore
}

35
src/models/Config.ts Normal file
View File

@ -0,0 +1,35 @@
import { Proxy, ProxyGroup } from './Proxy'
import { Rule } from './Rule'
/**
* clash config
* @see https://github.com/Dreamacro/clash#config
*/
export interface Config {
general?: {
/**
* http proxy port
*/
port?: number
/**
* socks proxy port
*/
socksPort?: number
/**
* controller port
*/
externalController: number
}
proxy?: Proxy[]
proxyGroup?: ProxyGroup[]
rules?: Rule[]
}

93
src/models/Proxy.ts Normal file
View File

@ -0,0 +1,93 @@
/**
* proxy config interface
*/
export interface Proxy {
/**
* proxy name
*/
name?: string
/**
* configs of proxy server
* now support shadowsocks, v2ray and socks5
*/
config?: ShadowsocksProxy | VmessProxy | Socks5Proxy
}
export interface ShadowsocksProxy {
type?: 'ss'
server?: string
port?: number
cipter?: string
password?: string
}
export interface VmessProxy {
type?: 'vmess'
server?: string
port?: number
uuid?: string
alterid?: number
cipter?: string
tls?: boolean
}
export interface Socks5Proxy {
type?: 'socks5'
server?: string
port?: number
}
export interface ProxyGroup {
/**
* proxy group name
*/
name?: string
/**
* configs of proxy server
* now support select and url-test
*/
config?: SelectProxyGroup | UrlTestProxyGroup
}
export interface SelectProxyGroup {
type?: 'select'
proxies?: Proxy[]
}
export interface UrlTestProxyGroup {
type?: 'url-test'
proxies?: Proxy[]
url?: string
interval?: number // second
}

11
src/models/Rule.ts Normal file
View File

@ -0,0 +1,11 @@
import { Proxy } from './Proxy'
export interface Rule {
type?: 'DOMAIN' | 'DOMAIN-SUFFIX' | 'DOMAIN-KEYWORD' | 'DOMAIN-SUFFIX' | 'GEOIP' | 'FINAL'
value?: string
use?: Proxy
}

4
src/models/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from './BaseProps'
export * from './Config'
export * from './Proxy'
export * from './Rule'

View File

@ -1,14 +1,14 @@
import * as React from 'react'
import { render } from 'react-dom'
import { HashRouter, BrowserRouter } from 'react-router-dom'
import { Provider } from 'mobx-react'
import { HashRouter } from 'react-router-dom'
import { I18nextProvider } from 'react-i18next'
import { AppContainer } from 'react-hot-loader'
import { isClashX } from '@lib/jsBridge'
import { rootStores } from '@lib/createStore'
import App from '@views/App'
import i18n from '@i18n'
const rootEl = document.getElementById('root')
const Router = isClashX() ? HashRouter : BrowserRouter
// Hot Module Replacement API
declare let module: { hot: any }
@ -16,11 +16,13 @@ declare let module: { hot: any }
export default function renderApp () {
render(
<AppContainer>
<Router>
<I18nextProvider i18n={ i18n }>
<App />
</I18nextProvider>
</Router>
<Provider {...rootStores}>
<HashRouter>
<I18nextProvider i18n={ i18n }>
<App />
</I18nextProvider>
</HashRouter>
</Provider>
</AppContainer>,
rootEl
)
@ -30,11 +32,13 @@ export default function renderApp () {
const NewApp = require('./views/App').default
render(
<AppContainer>
<Router>
<I18nextProvider i18n={ i18n }>
<NewApp />
</I18nextProvider>
</Router>
<Provider {...rootStores}>
<HashRouter>
<I18nextProvider i18n={ i18n }>
<NewApp />
</I18nextProvider>
</HashRouter>
</Provider>
</AppContainer>,
rootEl
)

38
src/stores/ConfigStore.ts Normal file
View File

@ -0,0 +1,38 @@
import { observable, action, runInAction } from 'mobx'
import { parse } from 'ini'
import { Config } from '@models'
import { jsBridge } from '@lib/jsBridge'
export class ConfigStore {
@observable
config: Config = {}
@observable
public state: 'pending' | 'ok' | 'error' = 'pending'
@action
async fetchAndParseConfig () {
this.state = 'pending'
const rawConfig = await jsBridge.readConfigString()
runInAction(() => {
// emit error when config is empty
// because read config might be error
if (!rawConfig) {
this.state = 'error'
return
}
// otherwise parse ini
const config = parse(rawConfig)
console.log(config)
this.config = config
this.state = 'ok'
})
}
}

14
src/stores/RouterStore.ts Normal file
View File

@ -0,0 +1,14 @@
import { History } from 'history'
import { RouterStore as BaseRouterStore, syncHistoryWithStore } from 'mobx-react-router'
export class RouterStore extends BaseRouterStore {
constructor (history?: History) {
super()
if (history) {
this.history = syncHistoryWithStore(history, this)
}
}
}
export default RouterStore

2
src/stores/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './ConfigStore'
export * from './RouterStore'

View File

@ -1,6 +1,17 @@
import * as React from 'react'
import { inject, observer } from 'mobx-react'
import { storeKeys } from '@lib/createStore'
import { BaseRouterProps } from '@models'
@inject(...storeKeys)
@observer
export default class Overview extends React.Component<BaseRouterProps, {}> {
componentDidMount () {
// here can access stores
console.log(this.props)
}
export default class Overview extends React.Component<{}, {}> {
render () {
return 'Overview'
}

View File

@ -25,6 +25,8 @@
"@i18n/*": ["src/i18n/*"],
"@stores": ["src/stores"],
"@stores/*": ["src/stores/*"],
"@models": ["src/models"],
"@models/*": ["src/models/*"],
"@styles": ["src/styles"],
"@styles/*": ["src/styles/*"]
}