Chore: code improvement

This commit is contained in:
Dreamacro 2019-09-30 11:11:40 +08:00
parent 8199612bc7
commit d66cf8f9fa
6 changed files with 514 additions and 343 deletions

730
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,17 +28,17 @@
"contributors:generate": "all-contributors generate" "contributors:generate": "all-contributors generate"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.6.0", "@babel/cli": "^7.6.2",
"@babel/core": "^7.6.0", "@babel/core": "^7.6.2",
"@babel/preset-env": "^7.6.0", "@babel/preset-env": "^7.6.2",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@hot-loader/react-dom": "^16.9.0", "@hot-loader/react-dom": "^16.9.0",
"@types/classnames": "^2.2.8", "@types/classnames": "^2.2.8",
"@types/lodash-es": "^4.17.3", "@types/lodash-es": "^4.17.3",
"@types/node": "^12.7.5", "@types/node": "^12.7.8",
"@types/react": "^16.9.2", "@types/react": "^16.9.3",
"@types/react-dom": "^16.9.0", "@types/react-dom": "^16.9.1",
"@types/react-router-dom": "^4.3.5", "@types/react-router-dom": "^5.1.0",
"@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.1", "@types/react-window": "^1.8.1",
"autoprefixer": "^9.6.1", "autoprefixer": "^9.6.1",
@ -52,22 +52,22 @@
"mini-css-extract-plugin": "^0.8.0", "mini-css-extract-plugin": "^0.8.0",
"offline-plugin": "^5.0.7", "offline-plugin": "^5.0.7",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"react-hot-loader": "^4.12.13", "react-hot-loader": "^4.12.14",
"sass": "^1.22.12", "sass": "^1.22.12",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"style-loader": "^1.0.0", "style-loader": "^1.0.0",
"stylelint": "^10.1.0", "stylelint": "^11.0.0",
"stylelint-config-standard": "^18.3.0", "stylelint-config-standard": "^19.0.0",
"stylelint-webpack-plugin": "^0.10.5", "stylelint-webpack-plugin": "^0.10.5",
"terser-webpack-plugin": "^2.0.1", "terser-webpack-plugin": "^2.1.2",
"tslint": "^5.20.0", "tslint": "^5.20.0",
"tslint-config-standard": "^8.0.1", "tslint-config-standard": "^8.0.1",
"tslint-loader": "^3.6.0", "tslint-loader": "^3.6.0",
"typescript": "^3.6.3", "typescript": "^3.6.3",
"webpack": "^4.40.1", "webpack": "^4.41.0",
"webpack-cli": "^3.3.8", "webpack-cli": "^3.3.9",
"webpack-dev-middleware": "^3.7.1", "webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.8.0", "webpack-dev-server": "^3.8.1",
"webpack-merge": "^4.2.2", "webpack-merge": "^4.2.2",
"webpack-pwa-manifest": "^4.0.0" "webpack-pwa-manifest": "^4.0.0"
}, },
@ -78,12 +78,12 @@
"eventemitter3": "^4.0.0", "eventemitter3": "^4.0.0",
"immer": "^4.0.0", "immer": "^4.0.0",
"lodash-es": "^4.17.15", "lodash-es": "^4.17.15",
"react": "^16.9.0", "react": "^16.10.1",
"react-dom": "^16.9.0", "react-dom": "^16.10.1",
"react-router-dom": "^5.0.1", "react-router-dom": "^5.1.1",
"react-virtualized-auto-sizer": "^1.0.2", "react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5", "react-window": "^1.8.5",
"unstated-next": "^1.1.0", "unstated-next": "^1.1.0",
"use-immer": "^0.3.3" "use-immer": "^0.3.4"
} }
} }

View File

@ -9,7 +9,7 @@ export default function ExternalController () {
const { t } = useTranslation('Settings') const { t } = useTranslation('Settings')
const { data: info, update, fetch } = containers.useAPIInfo() const { data: info, update, fetch } = containers.useAPIInfo()
const { unauthorized: { hidden, visible } } = containers.useData() const { unauthorized: { hidden, visible } } = containers.useData()
const { value, setMulti, setSingle } = useObject({ const [value, set] = useObject({
hostname: '', hostname: '',
port: '', port: '',
secret: '' secret: ''
@ -20,7 +20,7 @@ export default function ExternalController () {
}, []) }, [])
useEffect(() => { useEffect(() => {
setMulti({ hostname: info.hostname, port: info.port, secret: info.secret }) set({ hostname: info.hostname, port: info.port, secret: info.secret })
}, [info]) }, [info])
function handleOk () { function handleOk () {
@ -46,7 +46,7 @@ export default function ExternalController () {
align="left" align="left"
inside={true} inside={true}
value={value.hostname} value={value.hostname}
onChange={hostname => setSingle('hostname', hostname)} onChange={hostname => set('hostname', hostname)}
/> />
</Col> </Col>
</Row> </Row>
@ -57,7 +57,7 @@ export default function ExternalController () {
align="left" align="left"
inside={true} inside={true}
value={value.port} value={value.port}
onChange={port => setSingle('port', port)} onChange={port => set('port', port)}
/> />
</Col> </Col>
</Row> </Row>
@ -68,7 +68,7 @@ export default function ExternalController () {
align="left" align="left"
inside={true} inside={true}
value={value.secret} value={value.secret}
onChange={secret => setSingle('secret', secret)} onChange={secret => set('secret', secret)}
/> />
</Col> </Col>
</Row> </Row>

View File

@ -15,7 +15,7 @@ export default function Settings () {
const { data: apiInfo } = containers.useAPIInfo() const { data: apiInfo } = containers.useAPIInfo()
const { useTranslation, setLang, lang } = containers.useI18n() const { useTranslation, setLang, lang } = containers.useI18n()
const { t } = useTranslation('Settings') const { t } = useTranslation('Settings')
const { value: info, setSingle } = useObject({ const [info, set] = useObject({
socks5ProxyPort: 7891, socks5ProxyPort: 7891,
httpProxyPort: 7890, httpProxyPort: 7890,
isClashX: false isClashX: false
@ -24,13 +24,13 @@ export default function Settings () {
useEffect(() => { useEffect(() => {
fetch() fetch()
if (isClashX()) { if (isClashX()) {
fetchClashXData().then(() => setSingle('isClashX', true)) fetchClashXData().then(() => set('isClashX', true))
} }
}, []) }, [])
useEffect(() => { useEffect(() => {
setSingle('socks5ProxyPort', data.general.socksPort) set('socks5ProxyPort', data.general.socksPort)
setSingle('httpProxyPort', data.general.port) set('httpProxyPort', data.general.port)
}, [data]) }, [data])
async function handleProxyModeChange (mode: string) { async function handleProxyModeChange (mode: string) {
@ -58,7 +58,7 @@ export default function Settings () {
const [, err] = await to(updateConfig({ 'port': info.httpProxyPort })) const [, err] = await to(updateConfig({ 'port': info.httpProxyPort }))
if (!err) { if (!err) {
await fetch() await fetch()
setSingle('httpProxyPort', data.general.port) set('httpProxyPort', data.general.port)
} }
} }
@ -66,7 +66,7 @@ export default function Settings () {
const [, err] = await to(updateConfig({ 'socks-port': info.socks5ProxyPort })) const [, err] = await to(updateConfig({ 'socks-port': info.socks5ProxyPort }))
if (!err) { if (!err) {
await fetch() await fetch()
setSingle('socks5ProxyPort', data.general.socksPort) set('socks5ProxyPort', data.general.socksPort)
} }
} }
@ -164,7 +164,7 @@ export default function Settings () {
<Col span={8}> <Col span={8}>
<Input <Input
value={info.socks5ProxyPort} value={info.socks5ProxyPort}
onChange={socks5ProxyPort => setSingle('socks5ProxyPort', parseInt(socks5ProxyPort, 10))} onChange={socks5ProxyPort => set('socks5ProxyPort', parseInt(socks5ProxyPort, 10))}
onBlur={handleSocksPortSave} onBlur={handleSocksPortSave}
/> />
</Col> </Col>
@ -178,7 +178,7 @@ export default function Settings () {
<Col span={8}> <Col span={8}>
<Input <Input
value={info.httpProxyPort} value={info.httpProxyPort}
onChange={httpProxyPort => setSingle('httpProxyPort', parseInt(httpProxyPort, 10))} onChange={httpProxyPort => set('httpProxyPort', parseInt(httpProxyPort, 10))}
onBlur={handleHttpPortSave} onBlur={handleHttpPortSave}
/> />
</Col> </Col>

View File

@ -3,43 +3,50 @@ import { useImmer } from 'use-immer'
import { createContainer } from 'unstated-next' import { createContainer } from 'unstated-next'
export function useObject<T extends object> (initialValue: T) { export function useObject<T extends object> (initialValue: T) {
let [copy, set] = useImmer(initialValue) const [copy, rawSet] = useImmer(initialValue)
function setSingle<K extends keyof Draft<T>> (key: K, value: Draft<T>[K]) { function set<K extends keyof Draft<T>> (key: K, value: Draft<T>[K]): void
set(draft => { function set<K extends keyof Draft<T>> (data: Partial<T>): void
draft[key] = value function set<K extends keyof Draft<T>> (f: (draft: Draft<T>) => void | T): void
function set<K extends keyof Draft<T>> (data: any, value?: Draft<T>[K]): void {
if (typeof data === 'string') {
rawSet(draft => {
const key = data as K
const v = value
draft[key] = v
}) })
} } else if (typeof data === 'function') {
rawSet(data)
function setMulti<K extends keyof Draft<T>, U extends keyof T> (newValue: Partial<T>) { } else if (typeof data === 'object') {
set((draft: Draft<T>) => { rawSet((draft: Draft<T>) => {
for (const key of Object.keys(newValue)) { const obj = data as Draft<T>
draft[key as K] = newValue[key as U] as any for (const key of Object.keys(obj)) {
const k = key as keyof Draft<T>
draft[k] = obj[k]
} }
}) })
} }
}
return { value: copy, setSingle, setMulti, set } return [copy, set] as [T, typeof set]
} }
type containerFn<Value, State = void> = (initialState?: State) => Value type containerFn<Value, State = void> = (initialState?: State) => Value
export function composeContainer<T, C = containerFn<T>, U = { [key: string]: C }> (mapping: U) { export function composeContainer<T, C extends containerFn<T>, U extends { [key: string]: C }, K extends keyof U> (mapping: U) {
function Global () { function Global () {
return Object.keys(mapping).reduce((obj, key) => { return Object.keys(mapping).reduce((obj, key) => {
obj[key] = mapping[key]() obj[key as K] = mapping[key]()
return obj return obj
}, {}) as { [K in keyof U]: T } }, {} as { [K in keyof U]: T })
} }
const allContainer = createContainer(Global) const allContainer = createContainer(Global)
return { return {
Provider: allContainer.Provider, Provider: allContainer.Provider,
containers: Object.keys(mapping).reduce((obj, key) => { containers: Object.keys(mapping).reduce((obj, key) => {
obj[key] = function () { obj[key as K] = (() => allContainer.useContainer()[key]) as U[K]
return allContainer.useContainer()[key]
}
return obj return obj
}, {}) as { [K in keyof U]: U[K] } }, {} as { [K in keyof U]: U[K] })
} }
} }

View File

@ -7,7 +7,7 @@ import { setLocalStorageItem, partition, to } from '@lib/helper'
import { useI18n } from '@i18n' import { useI18n } from '@i18n'
function useData () { function useData () {
const { value: data, setSingle, setMulti, set } = useObject<Models.Data>({ const [data, set] = useObject<Models.Data>({
general: {}, general: {},
proxy: [], proxy: [],
proxyGroup: [], proxyGroup: [],
@ -32,7 +32,7 @@ function useData () {
const [{ data: general }, rawProxies, rules] = resp const [{ data: general }, rawProxies, rules] = resp
setSingle('general', { set('general', {
port: general.port, port: general.port,
socksPort: general['socks-port'], socksPort: general['socks-port'],
redirPort: general['redir-port'], redirPort: general['redir-port'],
@ -51,7 +51,7 @@ function useData () {
.map(key => ({ ...rawProxies.data.proxies[key], name: key })) .map(key => ({ ...rawProxies.data.proxies[key], name: key }))
const [proxy, groups] = partition(proxies, proxy => !policyGroup.has(proxy.type)) const [proxy, groups] = partition(proxies, proxy => !policyGroup.has(proxy.type))
setMulti({ set({
proxy: proxy as API.Proxy[], proxy: proxy as API.Proxy[],
proxyGroup: general.mode === 'Global' ? [proxyList] : groups as API.Group[], proxyGroup: general.mode === 'Global' ? [proxyList] : groups as API.Group[],
rules: rules.data.rules rules: rules.data.rules
@ -71,7 +71,7 @@ function useData () {
} }
function useAPIInfo () { function useAPIInfo () {
const { value: data, setMulti } = useObject<Models.APIInfo>({ const [data, set] = useObject<Models.APIInfo>({
hostname: '127.0.0.1', hostname: '127.0.0.1',
port: '9090', port: '9090',
secret: '' secret: ''
@ -79,7 +79,7 @@ function useAPIInfo () {
async function fetch () { async function fetch () {
const info = await API.getExternalControllerConfig() const info = await API.getExternalControllerConfig()
setMulti({ ...info }) set({ ...info })
} }
async function update (info: Models.APIInfo) { async function update (info: Models.APIInfo) {
@ -94,7 +94,7 @@ function useAPIInfo () {
} }
function useClashXData () { function useClashXData () {
const { value: data, setMulti } = useObject<Models.ClashXData>({ const [data, set] = useObject<Models.ClashXData>({
startAtLogin: false, startAtLogin: false,
systemProxy: false systemProxy: false
}) })
@ -103,7 +103,7 @@ function useClashXData () {
const startAtLogin = await jsBridge.getStartAtLogin() const startAtLogin = await jsBridge.getStartAtLogin()
const systemProxy = await jsBridge.isSystemProxySet() const systemProxy = await jsBridge.isSystemProxySet()
setMulti({ startAtLogin, systemProxy }) set({ startAtLogin, systemProxy })
} }
return { data, fetch } return { data, fetch }