summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorHaishan <[email protected]>2021-02-28 17:06:00 +0800
committerHaishan <[email protected]>2021-02-28 18:04:18 +0800
commitec4586ef3c92f7d5125cb06286a2e44c59a24bb3 (patch)
treea9f217c163ae7218160fce5545d167d74ee9a591 /src/components
parent27a66043403c7e619029bcf50dbc29893e173d07 (diff)
feat: add FAB action button to update all proxy providers
Diffstat (limited to 'src/components')
-rw-r--r--src/components/proxies/Proxies.tsx60
-rw-r--r--src/components/proxies/ProxyPageFab.tsx83
-rw-r--r--src/components/proxies/ProxyProvider.tsx30
-rw-r--r--src/components/proxies/ProxyProviderList.tsx12
-rw-r--r--src/components/proxies/proxies.hooks.tsx50
-rw-r--r--src/components/rules/RuleProviderItem.tsx2
-rw-r--r--src/components/rules/RulesPageFab.tsx2
-rw-r--r--src/components/shared/RotateIcon.module.css (renamed from src/components/rules/RotateIcon.module.css)0
-rw-r--r--src/components/shared/RotateIcon.tsx (renamed from src/components/rules/RotateIcon.tsx)0
9 files changed, 176 insertions, 63 deletions
diff --git a/src/components/proxies/Proxies.tsx b/src/components/proxies/Proxies.tsx
index d36a568..e162a17 100644
--- a/src/components/proxies/Proxies.tsx
+++ b/src/components/proxies/Proxies.tsx
@@ -1,29 +1,28 @@
import Tooltip from '@reach/tooltip';
import * as React from 'react';
-import { Zap } from 'react-feather';
import { useTranslation } from 'react-i18next';
-
-import { getClashAPIConfig } from '../../store/app';
+import Button from 'src/components/Button';
+import ContentHeader from 'src/components/ContentHeader';
+import { ClosePrevConns } from 'src/components/proxies/ClosePrevConns';
+import { ProxyGroup } from 'src/components/proxies/ProxyGroup';
+import { ProxyPageFab } from 'src/components/proxies/ProxyPageFab';
+import { ProxyProviderList } from 'src/components/proxies/ProxyProviderList';
+import Settings from 'src/components/proxies/Settings';
+import { TextFilter } from 'src/components/proxies/TextFilter';
+import BaseModal from 'src/components/shared/BaseModal';
+import { connect, useStoreActions } from 'src/components/StateProvider';
+import Equalizer from 'src/components/svg/Equalizer';
+import { getClashAPIConfig } from 'src/store/app';
import {
fetchProxies,
getDelay,
getProxyGroupNames,
getProxyProviders,
getShowModalClosePrevConns,
- requestDelayAll,
-} from '../../store/proxies';
-import Button from '../Button';
-import ContentHeader from '../ContentHeader';
-import BaseModal from '../shared/BaseModal';
-import { Fab, IsFetching, position as fabPosition } from '../shared/Fab';
-import { connect, useStoreActions } from '../StateProvider';
-import Equalizer from '../svg/Equalizer';
-import { ClosePrevConns } from './ClosePrevConns';
+} from 'src/store/proxies';
+import type { State } from 'src/store/types';
+
import s0 from './Proxies.module.css';
-import { ProxyGroup } from './ProxyGroup';
-import { ProxyProviderList } from './ProxyProviderList';
-import Settings from './Settings';
-import { TextFilter } from './TextFilter';
const { useState, useEffect, useCallback, useRef } = React;
@@ -38,16 +37,6 @@ function Proxies({
const refFetchedTimestamp = useRef<{ startAt?: number; completeAt?: number }>(
{}
);
- const [isTestingLatency, setIsTestingLatency] = useState(false);
- const requestDelayAllFn = useCallback(() => {
- if (isTestingLatency) return;
-
- setIsTestingLatency(true);
- dispatch(requestDelayAll(apiConfig)).then(
- () => setIsTestingLatency(false),
- () => setIsTestingLatency(false)
- );
- }, [apiConfig, dispatch, isTestingLatency]);
const fetchProxiesHooked = useCallback(() => {
refFetchedTimestamp.current.startAt = Date.now();
@@ -120,19 +109,10 @@ function Proxies({
</div>
<ProxyProviderList items={proxyProviders} />
<div style={{ height: 60 }} />
- <Fab
- icon={
- isTestingLatency ? (
- <IsFetching>
- <Zap width={16} height={16} />
- </IsFetching>
- ) : (
- <Zap width={16} height={16} />
- )
- }
- onClick={requestDelayAllFn}
- text={t('Test Latency')}
- style={fabPosition}
+ <ProxyPageFab
+ dispatch={dispatch}
+ apiConfig={apiConfig}
+ proxyProviders={proxyProviders}
/>
<BaseModal
isOpen={showModalClosePrevConns}
@@ -147,7 +127,7 @@ function Proxies({
);
}
-const mapState = (s) => ({
+const mapState = (s: State) => ({
apiConfig: getClashAPIConfig(s),
groupNames: getProxyGroupNames(s),
proxyProviders: getProxyProviders(s),
diff --git a/src/components/proxies/ProxyPageFab.tsx b/src/components/proxies/ProxyPageFab.tsx
new file mode 100644
index 0000000..7cc6d03
--- /dev/null
+++ b/src/components/proxies/ProxyPageFab.tsx
@@ -0,0 +1,83 @@
+import * as React from 'react';
+import { Zap } from 'react-feather';
+import { useTranslation } from 'react-i18next';
+import { useUpdateProviderItems } from 'src/components/proxies/proxies.hooks';
+import {
+ Action,
+ Fab,
+ IsFetching,
+ position as fabPosition,
+} from 'src/components/shared/Fab';
+import { RotateIcon } from 'src/components/shared/RotateIcon';
+import { requestDelayAll } from 'src/store/proxies';
+import { DispatchFn, FormattedProxyProvider } from 'src/store/types';
+import { ClashAPIConfig } from 'src/types';
+
+const { useState, useCallback } = React;
+
+function StatefulZap({ isLoading }: { isLoading: boolean }) {
+ return isLoading ? (
+ <IsFetching>
+ <Zap width={16} height={16} />
+ </IsFetching>
+ ) : (
+ <Zap width={16} height={16} />
+ );
+}
+
+function useTestLatencyAction({
+ dispatch,
+ apiConfig,
+}: {
+ dispatch: DispatchFn;
+ apiConfig: ClashAPIConfig;
+}): [() => unknown, boolean] {
+ const [isTestingLatency, setIsTestingLatency] = useState(false);
+ const requestDelayAllFn = useCallback(() => {
+ if (isTestingLatency) return;
+
+ setIsTestingLatency(true);
+ dispatch(requestDelayAll(apiConfig)).then(
+ () => setIsTestingLatency(false),
+ () => setIsTestingLatency(false)
+ );
+ }, [apiConfig, dispatch, isTestingLatency]);
+ return [requestDelayAllFn, isTestingLatency];
+}
+
+export function ProxyPageFab({
+ dispatch,
+ apiConfig,
+ proxyProviders,
+}: {
+ dispatch: DispatchFn;
+ apiConfig: ClashAPIConfig;
+ proxyProviders: FormattedProxyProvider[];
+}) {
+ const { t } = useTranslation();
+ const [requestDelayAllFn, isTestingLatency] = useTestLatencyAction({
+ dispatch,
+ apiConfig,
+ });
+
+ const [updateProviders, isUpdating] = useUpdateProviderItems({
+ apiConfig,
+ dispatch,
+ names: proxyProviders.map((item) => item.name),
+ });
+
+ return (
+ <Fab
+ icon={<StatefulZap isLoading={isTestingLatency} />}
+ onClick={requestDelayAllFn}
+ text={t('Test Latency')}
+ style={fabPosition}
+ >
+ {proxyProviders.length > 0 ? (
+ <Action text={t('update_all_proxy_provider')} onClick={updateProviders}>
+ <RotateIcon isRotating={isUpdating} />
+ </Action>
+ ) : null}
+ </Fab>
+ );
+}
diff --git a/src/components/proxies/ProxyProvider.tsx b/src/components/proxies/ProxyProvider.tsx
index de0a94f..d7220b0 100644
--- a/src/components/proxies/ProxyProvider.tsx
+++ b/src/components/proxies/ProxyProvider.tsx
@@ -1,24 +1,21 @@
import { formatDistance } from 'date-fns';
import * as React from 'react';
import { RotateCw, Zap } from 'react-feather';
-import { DelayMapping } from 'src/store/types';
-
-import { framerMotionResouce } from '../../misc/motion';
+import Button from 'src/components/Button';
+import Collapsible from 'src/components/Collapsible';
+import CollapsibleSectionHeader from 'src/components/CollapsibleSectionHeader';
+import { useUpdateProviderItem } from 'src/components/proxies/proxies.hooks';
+import { connect, useStoreActions } from 'src/components/StateProvider';
+import { framerMotionResouce } from 'src/misc/motion';
import {
getClashAPIConfig,
getCollapsibleIsOpen,
getHideUnavailableProxies,
getProxySortBy,
-} from '../../store/app';
-import {
- getDelay,
- healthcheckProviderByName,
- updateProviderByName,
-} from '../../store/proxies';
-import Button from '../Button';
-import Collapsible from '../Collapsible';
-import CollapsibleSectionHeader from '../CollapsibleSectionHeader';
-import { connect, useStoreActions } from '../StateProvider';
+} from 'src/store/app';
+import { getDelay, healthcheckProviderByName } from 'src/store/proxies';
+import { DelayMapping } from 'src/store/types';
+
import { useFilteredAndSorted } from './hooks';
import { ProxyList, ProxyListSummaryView } from './ProxyList';
import s from './ProxyProvider.module.css';
@@ -58,10 +55,9 @@ function ProxyProviderImpl({
proxySortBy
);
const [isHealthcheckLoading, setIsHealthcheckLoading] = useState(false);
- const updateProvider = useCallback(
- () => dispatch(updateProviderByName(apiConfig, name)),
- [apiConfig, dispatch, name]
- );
+
+ const updateProvider = useUpdateProviderItem({ dispatch, apiConfig, name });
+
const healthcheckProvider = useCallback(async () => {
setIsHealthcheckLoading(true);
await dispatch(healthcheckProviderByName(apiConfig, name));
diff --git a/src/components/proxies/ProxyProviderList.tsx b/src/components/proxies/ProxyProviderList.tsx
index c0e6b15..1528f37 100644
--- a/src/components/proxies/ProxyProviderList.tsx
+++ b/src/components/proxies/ProxyProviderList.tsx
@@ -1,9 +1,13 @@
import * as React from 'react';
+import ContentHeader from 'src/components/ContentHeader';
+import { ProxyProvider } from 'src/components/proxies/ProxyProvider';
+import { FormattedProxyProvider } from 'src/store/types';
-import ContentHeader from '../ContentHeader';
-import { ProxyProvider } from './ProxyProvider';
-
-export function ProxyProviderList({ items }) {
+export function ProxyProviderList({
+ items,
+}: {
+ items: FormattedProxyProvider[];
+}) {
if (items.length === 0) return null;
return (
diff --git a/src/components/proxies/proxies.hooks.tsx b/src/components/proxies/proxies.hooks.tsx
new file mode 100644
index 0000000..ec51c9b
--- /dev/null
+++ b/src/components/proxies/proxies.hooks.tsx
@@ -0,0 +1,50 @@
+import * as React from 'react';
+import { updateProviderByName, updateProviders } from 'src/store/proxies';
+import { DispatchFn } from 'src/store/types';
+import { ClashAPIConfig } from 'src/types';
+
+const { useCallback, useState } = React;
+
+export function useUpdateProviderItem({
+ dispatch,
+ apiConfig,
+ name,
+}: {
+ dispatch: DispatchFn;
+ apiConfig: ClashAPIConfig;
+ name: string;
+}) {
+ return useCallback(() => dispatch(updateProviderByName(apiConfig, name)), [
+ apiConfig,
+ dispatch,
+ name,
+ ]);
+}
+
+export function useUpdateProviderItems({
+ dispatch,
+ apiConfig,
+ names,
+}: {
+ dispatch: DispatchFn;
+ apiConfig: ClashAPIConfig;
+ names: string[];
+}): [() => unknown, boolean] {
+ const [isLoading, setIsLoading] = useState(false);
+
+ const action = useCallback(async () => {
+ if (isLoading) {
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ await dispatch(updateProviders(apiConfig, names));
+ } catch (e) {
+ // ignore
+ }
+ setIsLoading(false);
+ }, [apiConfig, dispatch, names, isLoading]);
+
+ return [action, isLoading];
+}
diff --git a/src/components/rules/RuleProviderItem.tsx b/src/components/rules/RuleProviderItem.tsx
index 9d439c7..c92cd05 100644
--- a/src/components/rules/RuleProviderItem.tsx
+++ b/src/components/rules/RuleProviderItem.tsx
@@ -1,9 +1,9 @@
import { formatDistance } from 'date-fns';
import * as React from 'react';
import Button from 'src/components/Button';
-import { RotateIcon } from 'src/components/rules/RotateIcon';
import { useUpdateRuleProviderItem } from 'src/components/rules/rules.hooks';
import { SectionNameType } from 'src/components/shared/Basic';
+import { RotateIcon } from 'src/components/shared/RotateIcon';
import s from './RuleProviderItem.module.css';
diff --git a/src/components/rules/RulesPageFab.tsx b/src/components/rules/RulesPageFab.tsx
index 3bf99ad..ce52a9a 100644
--- a/src/components/rules/RulesPageFab.tsx
+++ b/src/components/rules/RulesPageFab.tsx
@@ -1,8 +1,8 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
-import { RotateIcon } from 'src/components/rules/RotateIcon';
import { useUpdateAllRuleProviderItems } from 'src/components/rules/rules.hooks';
import { Fab, position as fabPosition } from 'src/components/shared/Fab';
+import { RotateIcon } from 'src/components/shared/RotateIcon';
import { ClashAPIConfig } from 'src/types';
type RulesPageFabProps = {
diff --git a/src/components/rules/RotateIcon.module.css b/src/components/shared/RotateIcon.module.css
index 60748de..60748de 100644
--- a/src/components/rules/RotateIcon.module.css
+++ b/src/components/shared/RotateIcon.module.css
diff --git a/src/components/rules/RotateIcon.tsx b/src/components/shared/RotateIcon.tsx
index e2d4ad8..e2d4ad8 100644
--- a/src/components/rules/RotateIcon.tsx
+++ b/src/components/shared/RotateIcon.tsx