From 48668b93128ec9f1524f98df1da7bbe31872d4ee Mon Sep 17 00:00:00 2001 From: Dreamacro <305009791@qq.com> Date: Fri, 6 Mar 2020 21:55:48 +0800 Subject: [PATCH] Feature: switch group can close its connections --- src/components/Loading/index.tsx | 16 +--------- src/components/Message/index.tsx | 9 +++--- src/components/Modal/index.tsx | 16 +--------- src/containers/Connections/index.tsx | 6 ++-- .../ExternalControllerDrawer/index.tsx | 4 +-- .../Proxies/components/Group/index.tsx | 16 +++++++++- .../Proxies/components/Provider/index.tsx | 5 ++-- src/containers/Proxies/index.tsx | 12 ++++++-- src/i18n/en_US.ts | 3 +- src/i18n/zh_CN.ts | 3 +- src/lib/hook.ts | 13 ++++++++ src/lib/request.ts | 10 +++++++ src/stores/HookStore.ts | 30 ++++++++++--------- 13 files changed, 83 insertions(+), 60 deletions(-) diff --git a/src/components/Loading/index.tsx b/src/components/Loading/index.tsx index 29c5615..993a55c 100644 --- a/src/components/Loading/index.tsx +++ b/src/components/Loading/index.tsx @@ -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 } -} diff --git a/src/components/Message/index.tsx b/src/components/Message/index.tsx index cea0108..dc2b7e6 100644 --- a/src/components/Message/index.tsx +++ b/src/components/Message/index.tsx @@ -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) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 8c86fd9..3e9687a 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -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 } -} diff --git a/src/containers/Connections/index.tsx b/src/containers/Connections/index.tsx index 0d936b0..af76115 100644 --- a/src/containers/Connections/index.tsx +++ b/src/containers/Connections/index.tsx @@ -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()) } diff --git a/src/containers/ExternalControllerDrawer/index.tsx b/src/containers/ExternalControllerDrawer/index.tsx index 8f03adc..317fdb4 100644 --- a/src/containers/ExternalControllerDrawer/index.tsx +++ b/src/containers/ExternalControllerDrawer/index.tsx @@ -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} > diff --git a/src/containers/Proxies/components/Group/index.tsx b/src/containers/Proxies/components/Group/index.tsx index 2fa7fa0..12f5c1b 100644 --- a/src/containers/Proxies/components/Group/index.tsx +++ b/src/containers/Proxies/components/Group/index.tsx @@ -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' diff --git a/src/containers/Proxies/components/Provider/index.tsx b/src/containers/Proxies/components/Provider/index.tsx index 6e278ef..17a5336 100644 --- a/src/containers/Proxies/components/Provider/index.tsx +++ b/src/containers/Proxies/components/Provider/index.tsx @@ -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() diff --git a/src/containers/Proxies/index.tsx b/src/containers/Proxies/index.tsx index ce01024..cbcd8d7 100644 --- a/src/containers/Proxies/index.tsx +++ b/src/containers/Proxies/index.tsx @@ -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 &&
-
+
+ setConfig('breakConnections', value)}> + {t('breakConnectionsText')} + +
    { diff --git a/src/i18n/en_US.ts b/src/i18n/en_US.ts index de0de67..9e8004c 100644 --- a/src/i18n/en_US.ts +++ b/src/i18n/en_US.ts @@ -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' } } diff --git a/src/i18n/zh_CN.ts b/src/i18n/zh_CN.ts index ccbaa51..e0ed898 100644 --- a/src/i18n/zh_CN.ts +++ b/src/i18n/zh_CN.ts @@ -88,6 +88,7 @@ export default { providerUpdateTime: '最后更新于', expandText: '展开', collapseText: '收起', - speedTestText: '测速' + speedTestText: '测速', + breakConnectionsText: '切换时打断包含策略组的连接' } } diff --git a/src/lib/hook.ts b/src/lib/hook.ts index 10aa28b..6bef9f2 100644 --- a/src/lib/hook.ts +++ b/src/lib/hook.ts @@ -89,3 +89,16 @@ export function useRound (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 } +} diff --git a/src/lib/request.ts b/src/lib/request.ts index bc0ba10..a82a9e4 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -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('connections') +} + export async function changeProxySelected (name: string, select: string) { const req = await getInstance() return req.put(`proxies/${name}`, { name: select }) diff --git a/src/stores/HookStore.ts b/src/stores/HookStore.ts index 19e0f0f..ea04573 100644 --- a/src/stores/HookStore.ts +++ b/src/stores/HookStore.ts @@ -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({ @@ -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({ startAtLogin: false, @@ -128,7 +129,8 @@ const { Provider, containers } = composeContainer({ useData, useAPIInfo, useClashXData, - useI18n + useI18n, + useConfig }) export { Provider, containers }