mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Add: add mobx for state management
This commit is contained in:
parent
8f97504649
commit
b47b6e0e5b
14
src/lib/createStore.ts
Normal file
14
src/lib/createStore.ts
Normal 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
24
src/lib/helper.ts
Normal 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
17
src/models/BaseProps.ts
Normal 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
35
src/models/Config.ts
Normal 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
93
src/models/Proxy.ts
Normal 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
11
src/models/Rule.ts
Normal 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
4
src/models/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './BaseProps'
|
||||||
|
export * from './Config'
|
||||||
|
export * from './Proxy'
|
||||||
|
export * from './Rule'
|
@ -1,14 +1,14 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { render } from 'react-dom'
|
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 { I18nextProvider } from 'react-i18next'
|
||||||
import { AppContainer } from 'react-hot-loader'
|
import { AppContainer } from 'react-hot-loader'
|
||||||
import { isClashX } from '@lib/jsBridge'
|
import { rootStores } from '@lib/createStore'
|
||||||
import App from '@views/App'
|
import App from '@views/App'
|
||||||
import i18n from '@i18n'
|
import i18n from '@i18n'
|
||||||
|
|
||||||
const rootEl = document.getElementById('root')
|
const rootEl = document.getElementById('root')
|
||||||
const Router = isClashX() ? HashRouter : BrowserRouter
|
|
||||||
|
|
||||||
// Hot Module Replacement API
|
// Hot Module Replacement API
|
||||||
declare let module: { hot: any }
|
declare let module: { hot: any }
|
||||||
@ -16,11 +16,13 @@ declare let module: { hot: any }
|
|||||||
export default function renderApp () {
|
export default function renderApp () {
|
||||||
render(
|
render(
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
<Router>
|
<Provider {...rootStores}>
|
||||||
|
<HashRouter>
|
||||||
<I18nextProvider i18n={ i18n }>
|
<I18nextProvider i18n={ i18n }>
|
||||||
<App />
|
<App />
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</Router>
|
</HashRouter>
|
||||||
|
</Provider>
|
||||||
</AppContainer>,
|
</AppContainer>,
|
||||||
rootEl
|
rootEl
|
||||||
)
|
)
|
||||||
@ -30,11 +32,13 @@ export default function renderApp () {
|
|||||||
const NewApp = require('./views/App').default
|
const NewApp = require('./views/App').default
|
||||||
render(
|
render(
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
<Router>
|
<Provider {...rootStores}>
|
||||||
|
<HashRouter>
|
||||||
<I18nextProvider i18n={ i18n }>
|
<I18nextProvider i18n={ i18n }>
|
||||||
<NewApp />
|
<NewApp />
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</Router>
|
</HashRouter>
|
||||||
|
</Provider>
|
||||||
</AppContainer>,
|
</AppContainer>,
|
||||||
rootEl
|
rootEl
|
||||||
)
|
)
|
||||||
|
38
src/stores/ConfigStore.ts
Normal file
38
src/stores/ConfigStore.ts
Normal 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
14
src/stores/RouterStore.ts
Normal 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
2
src/stores/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './ConfigStore'
|
||||||
|
export * from './RouterStore'
|
@ -1,6 +1,17 @@
|
|||||||
import * as React from 'react'
|
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 () {
|
render () {
|
||||||
return 'Overview'
|
return 'Overview'
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
"@i18n/*": ["src/i18n/*"],
|
"@i18n/*": ["src/i18n/*"],
|
||||||
"@stores": ["src/stores"],
|
"@stores": ["src/stores"],
|
||||||
"@stores/*": ["src/stores/*"],
|
"@stores/*": ["src/stores/*"],
|
||||||
|
"@models": ["src/models"],
|
||||||
|
"@models/*": ["src/models/*"],
|
||||||
"@styles": ["src/styles"],
|
"@styles": ["src/styles"],
|
||||||
"@styles/*": ["src/styles/*"]
|
"@styles/*": ["src/styles/*"]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user