mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Chore: use eslint
This commit is contained in:
parent
11db44dd1c
commit
9cfd15b40a
29
.eslintrc.yml
Normal file
29
.eslintrc.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
es6: true
|
||||||
|
extends:
|
||||||
|
- 'plugin:react/recommended'
|
||||||
|
- 'plugin:@typescript-eslint/recommended'
|
||||||
|
- standard
|
||||||
|
globals:
|
||||||
|
Atomics: readonly
|
||||||
|
SharedArrayBuffer: readonly
|
||||||
|
parser: '@typescript-eslint/parser'
|
||||||
|
parserOptions:
|
||||||
|
ecmaFeatures:
|
||||||
|
jsx: true
|
||||||
|
ecmaVersion: 2018
|
||||||
|
sourceType: module
|
||||||
|
project: './tsconfig.json'
|
||||||
|
plugins:
|
||||||
|
- react
|
||||||
|
- '@typescript-eslint'
|
||||||
|
settings:
|
||||||
|
react:
|
||||||
|
version: 'detect'
|
||||||
|
rules:
|
||||||
|
'@typescript-eslint/indent': ['error', 4, { 'SwitchCase': 0 }]
|
||||||
|
'indent': 'off'
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||||
|
'@typescript-eslint/member-delimiter-style': ['warn', { multiline: { delimiter: 'none' }, singleline: { delimiter: 'comma' } }]
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off'
|
@ -22,7 +22,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
use: ['tslint-loader']
|
use: ['eslint-loader']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
|
4044
package-lock.json
generated
4044
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
67
package.json
67
package.json
@ -20,7 +20,7 @@
|
|||||||
"build": "npm run clean-dist && NODE_ENV=production webpack --config=configs/webpack/prod.js",
|
"build": "npm run clean-dist && NODE_ENV=production webpack --config=configs/webpack/prod.js",
|
||||||
"clean-dist": "rm -rf dist",
|
"clean-dist": "rm -rf dist",
|
||||||
"lint": "npm run lint:ts && npm run lint:sass",
|
"lint": "npm run lint:ts && npm run lint:sass",
|
||||||
"lint:ts": "tslint './src/**/*.ts*' --format stylish",
|
"lint:ts": "eslint --ext=jsx,ts,tsx --fix src",
|
||||||
"lint:sass": "stylelint './src/**/*.scss'",
|
"lint:sass": "stylelint './src/**/*.scss'",
|
||||||
"start": "npm run start-dev",
|
"start": "npm run start-dev",
|
||||||
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js",
|
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js",
|
||||||
@ -28,55 +28,62 @@
|
|||||||
"contributors:generate": "all-contributors generate"
|
"contributors:generate": "all-contributors generate"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.7.7",
|
"@babel/cli": "^7.8.4",
|
||||||
"@babel/core": "^7.7.7",
|
"@babel/core": "^7.8.4",
|
||||||
"@babel/preset-env": "^7.7.7",
|
"@babel/preset-env": "^7.8.4",
|
||||||
"@babel/preset-react": "^7.7.4",
|
"@babel/preset-react": "^7.8.3",
|
||||||
"@hot-loader/react-dom": "^16.11.0",
|
"@hot-loader/react-dom": "^16.11.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": "^13.1.1",
|
"@types/node": "^13.7.1",
|
||||||
"@types/react": "^16.9.17",
|
"@types/react": "^16.9.20",
|
||||||
"@types/react-dom": "^16.9.4",
|
"@types/react-dom": "^16.9.5",
|
||||||
"@types/react-router-dom": "^5.1.3",
|
"@types/react-router-dom": "^5.1.3",
|
||||||
"@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.7.3",
|
"@typescript-eslint/eslint-plugin": "^2.20.0",
|
||||||
|
"@typescript-eslint/parser": "^2.20.0",
|
||||||
|
"autoprefixer": "^9.7.4",
|
||||||
"awesome-typescript-loader": "^5.2.1",
|
"awesome-typescript-loader": "^5.2.1",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-preset-minify": "^0.5.1",
|
"babel-preset-minify": "^0.5.1",
|
||||||
"css-loader": "^3.4.0",
|
"css-loader": "^3.4.2",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"eslint-config-standard": "^14.1.0",
|
||||||
|
"eslint-loader": "^3.0.3",
|
||||||
|
"eslint-plugin-import": "^2.20.1",
|
||||||
|
"eslint-plugin-node": "^11.0.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-react": "^7.18.3",
|
||||||
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
"file-loader": "^5.0.2",
|
"file-loader": "^5.0.2",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"image-webpack-loader": "^6.0.0",
|
"image-webpack-loader": "^6.0.0",
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
"mini-css-extract-plugin": "^0.9.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.18",
|
"react-hot-loader": "^4.12.19",
|
||||||
"sass": "^1.24.0",
|
"sass": "^1.25.0",
|
||||||
"sass-loader": "^8.0.0",
|
"sass-loader": "^8.0.2",
|
||||||
"style-loader": "^1.1.2",
|
"style-loader": "^1.1.3",
|
||||||
"stylelint": "^12.0.1",
|
"stylelint": "^13.2.0",
|
||||||
"stylelint-config-standard": "^19.0.0",
|
"stylelint-config-standard": "^20.0.0",
|
||||||
"stylelint-webpack-plugin": "^1.1.2",
|
"stylelint-webpack-plugin": "^1.2.3",
|
||||||
"terser-webpack-plugin": "^2.3.1",
|
"terser-webpack-plugin": "^2.3.5",
|
||||||
"tslint": "^5.20.1",
|
"typescript": "^3.7.5",
|
||||||
"tslint-config-standard": "^9.0.0",
|
"webpack": "^4.41.6",
|
||||||
"tslint-loader": "^3.6.0",
|
"webpack-cli": "^3.3.11",
|
||||||
"typescript": "^3.7.4",
|
|
||||||
"webpack": "^4.41.5",
|
|
||||||
"webpack-cli": "^3.3.10",
|
|
||||||
"webpack-dev-middleware": "^3.7.2",
|
"webpack-dev-middleware": "^3.7.2",
|
||||||
"webpack-dev-server": "^3.10.1",
|
"webpack-dev-server": "^3.10.3",
|
||||||
"webpack-merge": "^4.2.2",
|
"webpack-merge": "^4.2.2",
|
||||||
"webpack-pwa-manifest": "^4.1.1"
|
"webpack-pwa-manifest": "^4.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"dayjs": "^1.8.18",
|
"dayjs": "^1.8.20",
|
||||||
"eventemitter3": "^4.0.0",
|
"eventemitter3": "^4.0.0",
|
||||||
"immer": "^5.1.0",
|
"immer": "^5.3.6",
|
||||||
"lodash-es": "^4.17.15",
|
"lodash-es": "^4.17.15",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
@ -84,7 +91,7 @@
|
|||||||
"react-table": "^7.0.0-beta.12",
|
"react-table": "^7.0.0-beta.12",
|
||||||
"react-virtualized-auto-sizer": "^1.0.2",
|
"react-virtualized-auto-sizer": "^1.0.2",
|
||||||
"react-window": "^1.8.5",
|
"react-window": "^1.8.5",
|
||||||
"swr": "^0.1.16",
|
"swr": "^0.1.17",
|
||||||
"unstated-next": "^1.1.0",
|
"unstated-next": "^1.1.0",
|
||||||
"use-immer": "^0.3.5"
|
"use-immer": "^0.3.5"
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,20 @@ import { BaseComponentProps } from '@models/BaseProps'
|
|||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
|
|
||||||
export interface ButtonSelectOptions {
|
export interface ButtonSelectOptions<T = string> {
|
||||||
label: string,
|
label: string
|
||||||
value: any
|
value: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ButtonSelectProps extends BaseComponentProps {
|
export interface ButtonSelectProps<T = string> extends BaseComponentProps {
|
||||||
// options
|
// options
|
||||||
options: ButtonSelectOptions[]
|
options: ButtonSelectOptions<T>[]
|
||||||
|
|
||||||
// active value
|
// active value
|
||||||
value: any
|
value: T
|
||||||
|
|
||||||
// select callback
|
// select callback
|
||||||
onSelect?: (value: any) => void
|
onSelect?: (value: T) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ButtonSelect (props: ButtonSelectProps) {
|
export function ButtonSelect (props: ButtonSelectProps) {
|
||||||
|
@ -3,7 +3,7 @@ import { BaseComponentProps } from '@models/BaseProps'
|
|||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
|
|
||||||
interface CardProps extends BaseComponentProps {}
|
type CardProps = BaseComponentProps
|
||||||
|
|
||||||
export function Card (props: CardProps) {
|
export function Card (props: CardProps) {
|
||||||
const { className, style, children } = props
|
const { className, style, children } = props
|
||||||
|
@ -4,7 +4,7 @@ import { BaseComponentProps } from '@models/BaseProps'
|
|||||||
|
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
|
|
||||||
interface SpinnerProps extends BaseComponentProps {}
|
type SpinnerProps = BaseComponentProps
|
||||||
|
|
||||||
export function Spinner (props: SpinnerProps) {
|
export function Spinner (props: SpinnerProps) {
|
||||||
const classname = classnames('spinner', props.className)
|
const classname = classnames('spinner', props.className)
|
||||||
|
@ -63,30 +63,6 @@ export function Message (props: MessageProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const info = (
|
|
||||||
content: string,
|
|
||||||
duration?: number,
|
|
||||||
onClose?: typeof noop
|
|
||||||
) => showMessage({ type: 'info', content, duration, onClose })
|
|
||||||
|
|
||||||
export const success = (
|
|
||||||
content: string,
|
|
||||||
duration?: number,
|
|
||||||
onClose?: typeof noop
|
|
||||||
) => showMessage({ type: 'success', content, duration, onClose })
|
|
||||||
|
|
||||||
export const warning = (
|
|
||||||
content: string,
|
|
||||||
duration?: number,
|
|
||||||
onClose?: typeof noop
|
|
||||||
) => showMessage({ type: 'warning', content, duration, onClose })
|
|
||||||
|
|
||||||
export const error = (
|
|
||||||
content: string,
|
|
||||||
duration?: number,
|
|
||||||
onClose?: typeof noop
|
|
||||||
) => showMessage({ type: 'error', content, duration, onClose })
|
|
||||||
|
|
||||||
export function showMessage (args: ArgsProps) {
|
export function showMessage (args: ArgsProps) {
|
||||||
// create container element
|
// create container element
|
||||||
const container = document.createElement('div')
|
const container = document.createElement('div')
|
||||||
@ -113,3 +89,27 @@ export function showMessage (args: ArgsProps) {
|
|||||||
|
|
||||||
render(<Message {...props} />, container)
|
render(<Message {...props} />, container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const info = (
|
||||||
|
content: string,
|
||||||
|
duration?: number,
|
||||||
|
onClose?: typeof noop
|
||||||
|
) => showMessage({ type: 'info', content, duration, onClose })
|
||||||
|
|
||||||
|
export const success = (
|
||||||
|
content: string,
|
||||||
|
duration?: number,
|
||||||
|
onClose?: typeof noop
|
||||||
|
) => showMessage({ type: 'success', content, duration, onClose })
|
||||||
|
|
||||||
|
export const warning = (
|
||||||
|
content: string,
|
||||||
|
duration?: number,
|
||||||
|
onClose?: typeof noop
|
||||||
|
) => showMessage({ type: 'warning', content, duration, onClose })
|
||||||
|
|
||||||
|
export const error = (
|
||||||
|
content: string,
|
||||||
|
duration?: number,
|
||||||
|
onClose?: typeof noop
|
||||||
|
) => showMessage({ type: 'error', content, duration, onClose })
|
||||||
|
@ -37,7 +37,7 @@ export function Modal (props: ModalProps) {
|
|||||||
show = true,
|
show = true,
|
||||||
title = 'Modal',
|
title = 'Modal',
|
||||||
size = 'small',
|
size = 'small',
|
||||||
footer= true,
|
footer = true,
|
||||||
onOk = noop,
|
onOk = noop,
|
||||||
onClose = noop,
|
onClose = noop,
|
||||||
bodyClassName,
|
bodyClassName,
|
||||||
|
@ -2,6 +2,7 @@ import React, { useRef, useLayoutEffect, useState, useMemo } from 'react'
|
|||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Icon } from '@components'
|
import { Icon } from '@components'
|
||||||
import { BaseComponentProps } from '@models'
|
import { BaseComponentProps } from '@models'
|
||||||
|
import { noop } from '@lib/helper'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import './style.scss'
|
import './style.scss'
|
||||||
|
|
||||||
@ -24,16 +25,6 @@ export function Select (props: SelectProps) {
|
|||||||
const attachmentRef = useRef<HTMLDivElement>()
|
const attachmentRef = useRef<HTMLDivElement>()
|
||||||
const targetRef = useRef<HTMLDivElement>()
|
const targetRef = useRef<HTMLDivElement>()
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
document.addEventListener('click', handleGlobalClick, true)
|
|
||||||
return () => {
|
|
||||||
document.addEventListener('click', handleGlobalClick, true)
|
|
||||||
if (portalRef.current) {
|
|
||||||
document.body.removeChild(portalRef.current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const [showDropDownList, setShowDropDownList] = useState(false)
|
const [showDropDownList, setShowDropDownList] = useState(false)
|
||||||
const [hasCreateDropList, setHasCreateDropList] = useState(false)
|
const [hasCreateDropList, setHasCreateDropList] = useState(false)
|
||||||
const dropdownListStyles = useMemo(() => {
|
const dropdownListStyles = useMemo(() => {
|
||||||
@ -47,15 +38,25 @@ export function Select (props: SelectProps) {
|
|||||||
return {}
|
return {}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
function handleGlobalClick (e) {
|
function handleGlobalClick (e: MouseEvent) {
|
||||||
const el = attachmentRef.current
|
const el = attachmentRef.current
|
||||||
|
|
||||||
if (el && !el.contains(e.target)) {
|
if (el?.contains(e.target as Node)) {
|
||||||
setShowDropDownList(false)
|
setShowDropDownList(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShowDropList (e) {
|
useLayoutEffect(() => {
|
||||||
|
document.addEventListener('click', handleGlobalClick, true)
|
||||||
|
return () => {
|
||||||
|
document.addEventListener('click', handleGlobalClick, true)
|
||||||
|
if (portalRef.current) {
|
||||||
|
document.body.removeChild(portalRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
function handleShowDropList () {
|
||||||
if (!hasCreateDropList) {
|
if (!hasCreateDropList) {
|
||||||
if (!portalRef.current) {
|
if (!portalRef.current) {
|
||||||
// create container element
|
// create container element
|
||||||
@ -140,7 +141,7 @@ interface OptionProps extends BaseComponentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Option (props: OptionProps) {
|
export function Option (props: OptionProps) {
|
||||||
const { className: cn, style, key, disabled = false, children, onClick = () => {} } = props
|
const { className: cn, style, key, disabled = false, children, onClick = noop } = props
|
||||||
const className = classnames('option', { disabled }, cn)
|
const className = classnames('option', { disabled }, cn)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -21,7 +21,7 @@ function App () {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
// { path: '/', name: 'Overview', component: Overview, exact: true },
|
// { path: '/', name: 'Overview', component: Overview, exact: true },
|
||||||
{ path: '/proxies', name: 'Proxies', component: Proxies },
|
{ path: '/proxies', name: 'Proxies', component: Proxies },
|
||||||
{ path: '/logs', name: 'Logs', component: Logs },
|
{ path: '/logs', name: 'Logs', component: Logs },
|
||||||
{ path: '/rules', name: 'Rules', component: Rules, noMobile: true },
|
{ path: '/rules', name: 'Rules', component: Rules, noMobile: true },
|
||||||
|
@ -109,7 +109,7 @@ export default function Connections () {
|
|||||||
})
|
})
|
||||||
.map(c => ({
|
.map(c => ({
|
||||||
id: c.id,
|
id: c.id,
|
||||||
host: `${ c.metadata.host || c.metadata.destinationIP }:${ c.metadata.destinationPort }`,
|
host: `${c.metadata.host || c.metadata.destinationIP}:${c.metadata.destinationPort}`,
|
||||||
chains: c.chains.slice().reverse().join(' --> '),
|
chains: c.chains.slice().reverse().join(' --> '),
|
||||||
rule: c.rule,
|
rule: c.rule,
|
||||||
time: fromNow(new Date(c.start), lang),
|
time: fromNow(new Date(c.start), lang),
|
||||||
@ -141,7 +141,7 @@ export default function Connections () {
|
|||||||
), [lang, t])
|
), [lang, t])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let streamReader: StreamReader<API.Snapshot> = null
|
const streamReader: StreamReader<API.Snapshot> = null
|
||||||
|
|
||||||
function handleConnection (snapshots: API.Snapshot[]) {
|
function handleConnection (snapshots: API.Snapshot[]) {
|
||||||
for (const snapshot of snapshots) {
|
for (const snapshot of snapshots) {
|
||||||
@ -154,10 +154,10 @@ export default function Connections () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void async function () {
|
;(async function () {
|
||||||
const streamReader = await API.getConnectionStreamReader()
|
const streamReader = await API.getConnectionStreamReader()
|
||||||
streamReader.subscribe('data', handleConnection)
|
streamReader.subscribe('data', handleConnection)
|
||||||
}()
|
}())
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (streamReader) {
|
if (streamReader) {
|
||||||
@ -179,18 +179,18 @@ export default function Connections () {
|
|||||||
useResizeColumns
|
useResizeColumns
|
||||||
)
|
)
|
||||||
const headerGroup = useMemo(() => headerGroups[0], [headerGroups])
|
const headerGroup = useMemo(() => headerGroups[0], [headerGroups])
|
||||||
const renderItem = useMemo(() => rows.map((row, index) => {
|
const renderItem = useMemo(() => rows.map((row, i) => {
|
||||||
prepareRow(row)
|
prepareRow(row)
|
||||||
return (
|
return (
|
||||||
<div {...row.getRowProps()} className="connections-item">
|
<div {...row.getRowProps()} className="connections-item" key={i}>
|
||||||
{
|
{
|
||||||
row.cells.map((cell) => {
|
row.cells.map((cell, j) => {
|
||||||
const classname = classnames(
|
const classname = classnames(
|
||||||
'connections-block',
|
'connections-block',
|
||||||
{ center: shouldCenter.has(cell.column.id), completed: !!(row.original as any).completed }
|
{ center: shouldCenter.has(cell.column.id), completed: !!(row.original as any).completed }
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<div {...cell.getCellProps()} className={classname}>
|
<div {...cell.getCellProps()} className={classname} key={j}>
|
||||||
{ cell.render('Cell') }
|
{ cell.render('Cell') }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -204,7 +204,7 @@ export default function Connections () {
|
|||||||
<div className="page">
|
<div className="page">
|
||||||
<Header title={t('title')}>
|
<Header title={t('title')}>
|
||||||
<span className="connections-filter total">
|
<span className="connections-filter total">
|
||||||
{ `(${t('total.text')}: ${t('total.upload')} ${ formatTraffic(traffic.uploadTotal) } ${t('total.download')} ${ formatTraffic(traffic.downloadTotal) })` }
|
{ `(${t('total.text')}: ${t('total.upload')} ${formatTraffic(traffic.uploadTotal)} ${t('total.download')} ${formatTraffic(traffic.downloadTotal)})` }
|
||||||
</span>
|
</span>
|
||||||
<Checkbox className="connections-filter" checked={save} onChange={toggleSave}>{ t('keepClosed') }</Checkbox>
|
<Checkbox className="connections-filter" checked={save} onChange={toggleSave}>{ t('keepClosed') }</Checkbox>
|
||||||
<Icon className="connections-filter dangerous" onClick={show} type="close-all" size={20} />
|
<Icon className="connections-filter dangerous" onClick={show} type="close-all" size={20} />
|
||||||
@ -217,7 +217,7 @@ export default function Connections () {
|
|||||||
const id = column.id
|
const id = column.id
|
||||||
const handleClick = couldSort.has(id) ? () => handleSort(id) : noop
|
const handleClick = couldSort.has(id) ? () => handleSort(id) : noop
|
||||||
return (
|
return (
|
||||||
<div {...column.getHeaderProps()} className="connections-th" onClick={handleClick}>
|
<div {...column.getHeaderProps()} className="connections-th" onClick={handleClick} key={id}>
|
||||||
{ column.render('Header') }
|
{ column.render('Header') }
|
||||||
{
|
{
|
||||||
sort.column === id && (sort.asc ? ' ↑' : ' ↓')
|
sort.column === id && (sort.asc ? ' ↑' : ' ↓')
|
||||||
|
@ -20,19 +20,19 @@ export default function Logs () {
|
|||||||
}, [logsRef.current])
|
}, [logsRef.current])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let streamReader: StreamReader<Log> = null
|
const streamReader: StreamReader<Log> = null
|
||||||
|
|
||||||
function handleLog (newLogs: Log[]) {
|
function handleLog (newLogs: Log[]) {
|
||||||
logsRef.current = logsRef.current.slice().concat(newLogs.map(d => ({ ...d, time: new Date() })))
|
logsRef.current = logsRef.current.slice().concat(newLogs.map(d => ({ ...d, time: new Date() })))
|
||||||
setLogs(logsRef.current)
|
setLogs(logsRef.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
void async function () {
|
;(async function () {
|
||||||
const streamReader = await getLogsStreamReader()
|
const streamReader = await getLogsStreamReader()
|
||||||
logsRef.current = streamReader.buffer()
|
logsRef.current = streamReader.buffer()
|
||||||
setLogs(logsRef.current)
|
setLogs(logsRef.current)
|
||||||
streamReader.subscribe('data', handleLog)
|
streamReader.subscribe('data', handleLog)
|
||||||
}()
|
}())
|
||||||
|
|
||||||
return () => streamReader && streamReader.unsubscribe('data', handleLog)
|
return () => streamReader && streamReader.unsubscribe('data', handleLog)
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
const logo = require('@assets/LOGO-fixing.svg')
|
import logo from '@assets/logo-fixing.svg'
|
||||||
|
|
||||||
export default function Overview () {
|
export default function Overview () {
|
||||||
return (
|
return (
|
||||||
|
@ -15,7 +15,7 @@ export default function Rules () {
|
|||||||
fetch()
|
fetch()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
function renderRuleItem ({ index, style }) {
|
function renderRuleItem ({ index, style }: { index: number, style: React.CSSProperties }) {
|
||||||
const rule = rules[index]
|
const rule = rules[index]
|
||||||
return (
|
return (
|
||||||
<li className="rule-item" style={style}>
|
<li className="rule-item" style={style}>
|
||||||
|
@ -56,7 +56,7 @@ export default function Settings () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleHttpPortSave () {
|
async function handleHttpPortSave () {
|
||||||
const [, err] = await to(updateConfig({ 'port': info.httpProxyPort }))
|
const [, err] = await to(updateConfig({ port: info.httpProxyPort }))
|
||||||
if (!err) {
|
if (!err) {
|
||||||
await fetch()
|
await fetch()
|
||||||
set('httpProxyPort', data.general.port)
|
set('httpProxyPort', data.general.port)
|
||||||
|
5
src/global.d.ts
vendored
5
src/global.d.ts
vendored
@ -2,3 +2,8 @@ declare module '*.png' {
|
|||||||
const content: string
|
const content: string
|
||||||
export default content
|
export default content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '*.svg' {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
/* eslint-disable @typescript-eslint/camelcase */
|
||||||
import { useState, useCallback } from 'react'
|
import { useState, useCallback } from 'react'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
import { getLocalStorageItem, setLocalStorageItem } from '@lib/helper'
|
import { getLocalStorageItem, setLocalStorageItem } from '@lib/helper'
|
||||||
|
@ -10,6 +10,7 @@ export function removeLocalStorageItem (key: string) {
|
|||||||
return window.localStorage.removeItem(key)
|
return window.localStorage.removeItem(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
export function noop () {}
|
export function noop () {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +37,9 @@ export function useObject<T extends object> (initialValue: T) {
|
|||||||
export function useInterval (callback: () => void, delay: number) {
|
export function useInterval (callback: () => void, delay: number) {
|
||||||
const savedCallback = useRef(noop)
|
const savedCallback = useRef(noop)
|
||||||
|
|
||||||
useEffect(() => savedCallback.current = callback, [callback])
|
useEffect(() => {
|
||||||
|
savedCallback.current = callback
|
||||||
|
}, [callback])
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
|
@ -52,6 +52,8 @@ declare global {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JsBridgeCallback = (jsbridge: JsBridgeAPI) => void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if perched in ClashX Runtime
|
* Check if perched in ClashX Runtime
|
||||||
*/
|
*/
|
||||||
@ -68,10 +70,9 @@ export let jsBridge: JsBridge = null
|
|||||||
* JsBridge class
|
* JsBridge class
|
||||||
*/
|
*/
|
||||||
export class JsBridge {
|
export class JsBridge {
|
||||||
|
|
||||||
instance: JsBridgeAPI = null
|
instance: JsBridgeAPI = null
|
||||||
|
|
||||||
constructor (callback = jsbridge => {}) {
|
constructor (callback: JsBridgeCallback) {
|
||||||
if (window.WebViewJavascriptBridge) {
|
if (window.WebViewJavascriptBridge) {
|
||||||
this.instance = window.WebViewJavascriptBridge
|
this.instance = window.WebViewJavascriptBridge
|
||||||
callback(this.instance)
|
callback(this.instance)
|
||||||
@ -89,7 +90,7 @@ export class JsBridge {
|
|||||||
* @param {Function} cb callback when jsbridge initialized
|
* @param {Function} cb callback when jsbridge initialized
|
||||||
* @see https://github.com/marcuswestin/WebViewJavascriptBridge
|
* @see https://github.com/marcuswestin/WebViewJavascriptBridge
|
||||||
*/
|
*/
|
||||||
private initBridge (callback) {
|
private initBridge (callback: JsBridgeCallback) {
|
||||||
/**
|
/**
|
||||||
* You need check if inClashX first
|
* You need check if inClashX first
|
||||||
*/
|
*/
|
||||||
|
@ -87,6 +87,28 @@ export interface Connections {
|
|||||||
rule: string
|
rule: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getExternalControllerConfig () {
|
||||||
|
if (isClashX()) {
|
||||||
|
const info = await jsBridge.getAPIInfo()
|
||||||
|
|
||||||
|
return {
|
||||||
|
hostname: info.host,
|
||||||
|
port: info.port,
|
||||||
|
secret: info.secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostname = getLocalStorageItem('externalControllerAddr', '127.0.0.1')
|
||||||
|
const port = getLocalStorageItem('externalControllerPort', '9090')
|
||||||
|
const secret = getLocalStorageItem('secret', '')
|
||||||
|
|
||||||
|
if (!hostname || !port) {
|
||||||
|
throw new Error('can\'t get hostname or port')
|
||||||
|
}
|
||||||
|
|
||||||
|
return { hostname, port, secret }
|
||||||
|
}
|
||||||
|
|
||||||
export const getInstance = createAsyncSingleton(async () => {
|
export const getInstance = createAsyncSingleton(async () => {
|
||||||
const {
|
const {
|
||||||
hostname,
|
hostname,
|
||||||
@ -129,12 +151,12 @@ export async function getProxyProviders () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
// compatible old version
|
// compatible old version
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (resp.status === 404) {
|
if (resp.status === 404) {
|
||||||
resp.data = { providers: {} }
|
resp.data = { providers: {} }
|
||||||
}
|
}
|
||||||
return resp
|
return resp
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProvider (name: string) {
|
export async function updateProvider (name: string) {
|
||||||
@ -182,28 +204,6 @@ export async function changeProxySelected (name: string, select: string) {
|
|||||||
return req.put<void>(`proxies/${name}`, { name: select })
|
return req.put<void>(`proxies/${name}`, { name: select })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getExternalControllerConfig () {
|
|
||||||
if (isClashX()) {
|
|
||||||
const info = await jsBridge.getAPIInfo()
|
|
||||||
|
|
||||||
return {
|
|
||||||
hostname: info.host,
|
|
||||||
port: info.port,
|
|
||||||
secret: info.secret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hostname = getLocalStorageItem('externalControllerAddr', '127.0.0.1')
|
|
||||||
const port = getLocalStorageItem('externalControllerPort', '9090')
|
|
||||||
const secret = getLocalStorageItem('secret', '')
|
|
||||||
|
|
||||||
if (!hostname || !port) {
|
|
||||||
throw new Error('can\'t get hostname or port')
|
|
||||||
}
|
|
||||||
|
|
||||||
return { hostname, port, secret }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getLogsStreamReader = createAsyncSingleton(async function () {
|
export const getLogsStreamReader = createAsyncSingleton(async function () {
|
||||||
const externalController = await getExternalControllerConfig()
|
const externalController = await getExternalControllerConfig()
|
||||||
const { data: config } = await getConfig()
|
const { data: config } = await getConfig()
|
||||||
|
@ -5,7 +5,7 @@ import { RouteComponentProps } from 'react-router'
|
|||||||
* expose base router component props
|
* expose base router component props
|
||||||
* and mobx store to props
|
* and mobx store to props
|
||||||
*/
|
*/
|
||||||
export interface BaseRouterProps extends RouteComponentProps<any> {}
|
export type BaseRouterProps = RouteComponentProps
|
||||||
|
|
||||||
export interface BaseComponentProps {
|
export interface BaseComponentProps {
|
||||||
className?: string
|
className?: string
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export interface Log {
|
export interface Log {
|
||||||
type: string
|
type: string
|
||||||
payload: string,
|
payload: string
|
||||||
time: Date
|
time: Date
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ export interface FallbackProxyGroup {
|
|||||||
|
|
||||||
url?: string
|
url?: string
|
||||||
|
|
||||||
interval?: number // second
|
interval?: number // second
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UrlTestProxyGroup {
|
export interface UrlTestProxyGroup {
|
||||||
@ -102,5 +102,5 @@ export interface UrlTestProxyGroup {
|
|||||||
|
|
||||||
url?: string
|
url?: string
|
||||||
|
|
||||||
interval?: number // second
|
interval?: number // second
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ export interface Rule {
|
|||||||
|
|
||||||
payload?: string
|
payload?: string
|
||||||
|
|
||||||
proxy?: string // proxy or proxy group name
|
proxy?: string // proxy or proxy group name
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ function useData () {
|
|||||||
|
|
||||||
const policyGroup = new Set(['Selector', 'URLTest', 'Fallback', 'LoadBalance'])
|
const policyGroup = new Set(['Selector', 'URLTest', 'Fallback', 'LoadBalance'])
|
||||||
const unUsedProxy = new Set(['DIRECT', 'REJECT', 'GLOBAL'])
|
const unUsedProxy = new Set(['DIRECT', 'REJECT', 'GLOBAL'])
|
||||||
const proxyList = rawProxies.data.proxies['GLOBAL'] as API.Group
|
const proxyList = rawProxies.data.proxies.GLOBAL as API.Group
|
||||||
// fix missing name
|
// fix missing name
|
||||||
proxyList.name = 'GLOBAL'
|
proxyList.name = 'GLOBAL'
|
||||||
const proxies = proxyList.all
|
const proxies = proxyList.all
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "tslint-config-standard",
|
|
||||||
"rules": {
|
|
||||||
"indent": [true, "spaces", 4],
|
|
||||||
"ter-indent": [true, 4],
|
|
||||||
"no-empty": false,
|
|
||||||
"quotemark": [true, "single", "jsx-double"]
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user