mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 05:51:56 +08:00
Feature: switch group can close its connections
This commit is contained in:
parent
027e54bf1f
commit
48668b9312
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { BaseComponentProps } from '@models/BaseProps'
|
||||
import { Spinner } from './Spinner'
|
||||
@ -19,17 +19,3 @@ export function Loading (props: LoadingProps) {
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
||||
export function useLoading (initial: boolean) {
|
||||
const [visible, setVisible] = useState(initial)
|
||||
|
||||
function hide () {
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
function show () {
|
||||
setVisible(true)
|
||||
}
|
||||
|
||||
return { visible, hide, show }
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { useState, useLayoutEffect } from 'react'
|
||||
import React, { useLayoutEffect } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { unmountComponentAtNode, render } from 'react-dom'
|
||||
import { Icon } from '@components'
|
||||
import { noop } from '@lib/helper'
|
||||
import { useVisible } from '@lib/hook'
|
||||
import './style.scss'
|
||||
|
||||
const TYPE_ICON_MAP = {
|
||||
@ -40,13 +41,13 @@ export function Message (props: MessageProps) {
|
||||
duration = 1500
|
||||
} = props
|
||||
|
||||
const [visible, setVisible] = useState(false)
|
||||
const { visible, show, hide } = useVisible()
|
||||
|
||||
useLayoutEffect(() => {
|
||||
window.setTimeout(() => setVisible(true), 0)
|
||||
window.setTimeout(() => show(), 0)
|
||||
|
||||
const id = window.setTimeout(() => {
|
||||
setVisible(false)
|
||||
hide()
|
||||
onClose()
|
||||
}, duration)
|
||||
return () => window.clearTimeout(id)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useRef, useLayoutEffect, MouseEvent, useState } from 'react'
|
||||
import React, { useRef, useLayoutEffect, MouseEvent } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { BaseComponentProps } from '@models'
|
||||
@ -90,17 +90,3 @@ export function Modal (props: ModalProps) {
|
||||
|
||||
return createPortal(modal, portalRef.current)
|
||||
}
|
||||
|
||||
export function useModal () {
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
function show () {
|
||||
setVisible(true)
|
||||
}
|
||||
|
||||
function hide () {
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
return { visible, show, hide }
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useMemo } from 'react'
|
||||
import { useBlockLayout, useResizeColumns, useTable } from 'react-table'
|
||||
import classnames from 'classnames'
|
||||
import { Header, Card, Checkbox, Modal, useModal, Icon } from '@components'
|
||||
import { Header, Card, Checkbox, Modal, Icon } from '@components'
|
||||
import { containers } from '@stores'
|
||||
import * as API from '@lib/request'
|
||||
import { StreamReader } from '@lib/streamer'
|
||||
import { useObject } from '@lib/hook'
|
||||
import { useObject, useVisible } from '@lib/hook'
|
||||
import { noop } from '@lib/helper'
|
||||
import { fromNow } from '@lib/date'
|
||||
import { useConnections } from './store'
|
||||
@ -87,7 +87,7 @@ export default function Connections () {
|
||||
}
|
||||
|
||||
// close all connections
|
||||
const { visible, show, hide } = useModal()
|
||||
const { visible, show, hide } = useVisible()
|
||||
function handleCloseConnections () {
|
||||
API.closeAllConnections().finally(() => hide())
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export default function ExternalController () {
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('Settings')
|
||||
const { data: info, update, fetch } = containers.useAPIInfo()
|
||||
const { unauthorized: { hidden, visible } } = containers.useData()
|
||||
const { unauthorized: { hide, visible } } = containers.useData()
|
||||
const [value, set] = useObject({
|
||||
hostname: '',
|
||||
port: '',
|
||||
@ -33,7 +33,7 @@ export default function ExternalController () {
|
||||
show={visible}
|
||||
title={t('externalControllerSetting.title')}
|
||||
bodyClassName="external-controller"
|
||||
onClose={hidden}
|
||||
onClose={hide}
|
||||
onOk={handleOk}
|
||||
>
|
||||
<Alert type="info" inside={true}>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from 'react'
|
||||
import { containers } from '@stores'
|
||||
import { changeProxySelected, Group as IGroup } from '@lib/request'
|
||||
import { changeProxySelected, Group as IGroup, getConnections, closeConnection } from '@lib/request'
|
||||
import { Tags, Tag } from '@components'
|
||||
import './style.scss'
|
||||
|
||||
@ -10,11 +10,25 @@ interface GroupProps {
|
||||
|
||||
export function Group (props: GroupProps) {
|
||||
const { fetch } = containers.useData()
|
||||
const { data: Config } = containers.useConfig()
|
||||
const { config } = props
|
||||
|
||||
async function handleChangeProxySelected (name: string) {
|
||||
await changeProxySelected(props.config.name, name)
|
||||
await fetch()
|
||||
if (Config.breakConnections) {
|
||||
const list: string[] = []
|
||||
const snapshot = await getConnections()
|
||||
for (const connection of snapshot.data.connections) {
|
||||
if (connection.chains.includes(name)) {
|
||||
list.push(connection.id)
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of list) {
|
||||
closeConnection(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const canClick = config.type === 'Selector'
|
||||
|
@ -1,9 +1,10 @@
|
||||
import * as React from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { Card, Tag, Icon, Loading, useLoading } from '@components'
|
||||
import { Card, Tag, Icon, Loading } from '@components'
|
||||
import { containers } from '@stores'
|
||||
import { fromNow } from '@lib/date'
|
||||
import { Provider as IProvider, Proxy as IProxy, updateProvider, healthCheckProvider } from '@lib/request'
|
||||
import { useVisible } from '@lib/hook'
|
||||
import { Proxy } from '../Proxy'
|
||||
import { compareDesc } from '../../'
|
||||
import './style.scss'
|
||||
@ -19,7 +20,7 @@ export function Provider (props: ProvidersProps) {
|
||||
|
||||
const { t } = useTranslation('Proxies')
|
||||
|
||||
const { visible, hide, show } = useLoading(false)
|
||||
const { visible, hide, show } = useVisible()
|
||||
|
||||
function handleHealthChech () {
|
||||
show()
|
||||
|
@ -2,7 +2,7 @@ import React, { useMemo } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import EE from '@lib/event'
|
||||
import { useRound } from '@lib/hook'
|
||||
import { Card, Header, Icon } from '@components'
|
||||
import { Card, Header, Icon, Checkbox } from '@components'
|
||||
import { containers } from '@stores'
|
||||
import * as API from '@lib/request'
|
||||
|
||||
@ -30,6 +30,7 @@ export function compareDesc (a: API.Proxy, b: API.Proxy) {
|
||||
export default function Proxies () {
|
||||
const { data, fetch } = containers.useData()
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { data: config, set: setConfig } = containers.useConfig()
|
||||
const { t } = useTranslation('Proxies')
|
||||
useSWR('data', fetch)
|
||||
|
||||
@ -57,7 +58,14 @@ export default function Proxies () {
|
||||
{
|
||||
data.proxyGroup.length !== 0 &&
|
||||
<div className="proxies-container">
|
||||
<Header title={t('groupTitle')} />
|
||||
<Header title={t('groupTitle')}>
|
||||
<Checkbox
|
||||
className="connections-filter"
|
||||
checked={config.breakConnections}
|
||||
onChange={value => setConfig('breakConnections', value)}>
|
||||
{t('breakConnectionsText')}
|
||||
</Checkbox>
|
||||
</Header>
|
||||
<Card className="proxies-group-card">
|
||||
<ul className="proxies-group-list">
|
||||
{
|
||||
|
@ -88,6 +88,7 @@ export default {
|
||||
providerUpdateTime: 'Last updated at',
|
||||
expandText: 'Expand',
|
||||
collapseText: 'Collapse',
|
||||
speedTestText: 'Speed Test'
|
||||
speedTestText: 'Speed Test',
|
||||
breakConnectionsText: 'Close connections which include the group'
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ export default {
|
||||
providerUpdateTime: '最后更新于',
|
||||
expandText: '展开',
|
||||
collapseText: '收起',
|
||||
speedTestText: '测速'
|
||||
speedTestText: '测速',
|
||||
breakConnectionsText: '切换时打断包含策略组的连接'
|
||||
}
|
||||
}
|
||||
|
@ -89,3 +89,16 @@ export function useRound<T> (list: T[], defidx = 0) {
|
||||
|
||||
return { current, next }
|
||||
}
|
||||
|
||||
export function useVisible (initial = false) {
|
||||
const [visible, setVisible] = useState(initial)
|
||||
|
||||
function hide () {
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
function show () {
|
||||
setVisible(true)
|
||||
}
|
||||
return { visible, hide, show }
|
||||
}
|
||||
|
@ -199,6 +199,16 @@ export async function closeAllConnections () {
|
||||
return req.delete('connections')
|
||||
}
|
||||
|
||||
export async function closeConnection (id: string) {
|
||||
const req = await getInstance()
|
||||
return req.delete(`connections/${id}`)
|
||||
}
|
||||
|
||||
export async function getConnections () {
|
||||
const req = await getInstance()
|
||||
return req.get<Snapshot>('connections')
|
||||
}
|
||||
|
||||
export async function changeProxySelected (name: string, select: string) {
|
||||
const req = await getInstance()
|
||||
return req.put<void>(`proxies/${name}`, { name: select })
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useState } from 'react'
|
||||
import * as Models from '@models'
|
||||
import * as API from '@lib/request'
|
||||
import { useObject, composeContainer } from '@lib/hook'
|
||||
import { useObject, composeContainer, useVisible } from '@lib/hook'
|
||||
import { jsBridge } from '@lib/jsBridge'
|
||||
import { setLocalStorageItem, partition, to } from '@lib/helper'
|
||||
import { useI18n } from '@i18n'
|
||||
import { AxiosError } from 'axios'
|
||||
|
||||
function useData () {
|
||||
const [data, set] = useObject<Models.Data>({
|
||||
@ -16,19 +16,12 @@ function useData () {
|
||||
rules: []
|
||||
})
|
||||
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
function show () {
|
||||
setVisible(true)
|
||||
}
|
||||
|
||||
function hidden () {
|
||||
setVisible(false)
|
||||
}
|
||||
const { visible, show, hide } = useVisible()
|
||||
|
||||
async function fetch () {
|
||||
const [resp, err] = await to(Promise.all([API.getConfig(), API.getProxies(), API.getRules(), API.getProxyProviders()]))
|
||||
if (err && (!err.response || err.response.status === 401)) {
|
||||
const rErr = err as AxiosError
|
||||
if (rErr && (!rErr.response || rErr.response.status === 401)) {
|
||||
show()
|
||||
}
|
||||
|
||||
@ -82,7 +75,7 @@ function useData () {
|
||||
})
|
||||
}
|
||||
|
||||
return { data, fetch, unauthorized: { visible, show, hidden }, updateDelay }
|
||||
return { data, fetch, unauthorized: { visible, show, hide }, updateDelay }
|
||||
}
|
||||
|
||||
function useAPIInfo () {
|
||||
@ -108,6 +101,14 @@ function useAPIInfo () {
|
||||
return { data, fetch, update }
|
||||
}
|
||||
|
||||
function useConfig () {
|
||||
const [data, set] = useObject({
|
||||
breakConnections: false
|
||||
})
|
||||
|
||||
return { data, set }
|
||||
}
|
||||
|
||||
function useClashXData () {
|
||||
const [data, set] = useObject<Models.ClashXData>({
|
||||
startAtLogin: false,
|
||||
@ -128,7 +129,8 @@ const { Provider, containers } = composeContainer({
|
||||
useData,
|
||||
useAPIInfo,
|
||||
useClashXData,
|
||||
useI18n
|
||||
useI18n,
|
||||
useConfig
|
||||
})
|
||||
|
||||
export { Provider, containers }
|
||||
|
Loading…
x
Reference in New Issue
Block a user