Always show update and health check buttons for proxy provider

ref #649
This commit is contained in:
Haishan 2022-06-12 09:30:45 +08:00
parent e62b958e30
commit 69423bdfff
8 changed files with 102 additions and 67 deletions

View file

@ -4,6 +4,7 @@ import Loading from 'src/components/Loading';
import Button from './Button'; import Button from './Button';
import Input from './Input'; import Input from './Input';
import { ZapAnimated } from './shared/ZapAnimated';
import SwitchThemed from './SwitchThemed'; import SwitchThemed from './SwitchThemed';
import ToggleSwitch from './ToggleSwitch'; import ToggleSwitch from './ToggleSwitch';
@ -42,6 +43,9 @@ class StyleGuide extends PureComponent {
render() { render() {
return ( return (
<div> <div>
<Pane>
<ZapAnimated />
</Pane>
<Pane> <Pane>
<SwitchExample /> <SwitchExample />
</Pane> </Pane>

View file

@ -4,17 +4,10 @@
.groupHead { .groupHead {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center; align-items: center;
} }
.latencyButton { .action {
margin-left: 5px; margin: 0 5px;
}
.zapWrapper {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
} }

View file

@ -1,26 +1,20 @@
import Tooltip from '@reach/tooltip';
import * as React from 'react'; import * as React from 'react';
import { Zap } from 'react-feather';
import { useState2 } from '$src/hooks/basic';
import { State } from '$src/store/types'; import { State } from '$src/store/types';
import { getCollapsibleIsOpen, getHideUnavailableProxies, getProxySortBy } from '../../store/app'; import { getCollapsibleIsOpen, getHideUnavailableProxies, getProxySortBy } from '../../store/app';
import { getProxies, switchProxy } from '../../store/proxies'; import { getProxies, switchProxy } from '../../store/proxies';
import Button from '../Button'; import Button from '../Button';
import CollapsibleSectionHeader from '../CollapsibleSectionHeader'; import CollapsibleSectionHeader from '../CollapsibleSectionHeader';
import { ZapAnimated } from '../shared/ZapAnimated';
import { connect, useStoreActions } from '../StateProvider'; import { connect, useStoreActions } from '../StateProvider';
import { useFilteredAndSorted } from './hooks'; import { useFilteredAndSorted } from './hooks';
import s0 from './ProxyGroup.module.scss'; import s0 from './ProxyGroup.module.scss';
import { ProxyList, ProxyListSummaryView } from './ProxyList'; import { ProxyList, ProxyListSummaryView } from './ProxyList';
const { createElement, useCallback, useMemo, useState } = React; const { createElement, useCallback, useMemo } = React;
function ZapWrapper() {
return (
<div className={s0.zapWrapper}>
<Zap size={16} />
</div>
);
}
function ProxyGroupImpl({ function ProxyGroupImpl({
name, name,
@ -56,14 +50,15 @@ function ProxyGroupImpl({
[apiConfig, dispatch, name, isSelectable] [apiConfig, dispatch, name, isSelectable]
); );
const [isTestingLatency, setIsTestingLatency] = useState(false); const testingLatency = useState2(false);
const testLatency = useCallback(async () => { const testLatency = useCallback(async () => {
setIsTestingLatency(true); if (testingLatency.value) return;
testingLatency.set(true);
try { try {
await requestDelayForProxies(apiConfig, all); await requestDelayForProxies(apiConfig, all);
} catch (err) {} } catch (err) {}
setIsTestingLatency(false); testingLatency.set(false);
}, [all, apiConfig, requestDelayForProxies]); }, [all, apiConfig, requestDelayForProxies, testingLatency]);
return ( return (
<div className={s0.group}> <div className={s0.group}>
@ -75,15 +70,13 @@ function ProxyGroupImpl({
qty={all.length} qty={all.length}
isOpen={isOpen} isOpen={isOpen}
/> />
<Button <div className={s0.action}>
className={s0.latencyButton} <Tooltip label={'Test latency'}>
title="Test latency" <Button kind="circular" onClick={testLatency}>
kind="minimal" <ZapAnimated animate={testingLatency.value} size={16} />
onClick={testLatency}
isLoading={isTestingLatency}
>
<ZapWrapper />
</Button> </Button>
</Tooltip>
</div>
</div> </div>
{createElement(isOpen ? ProxyList : ProxyListSummaryView, { {createElement(isOpen ? ProxyList : ProxyListSummaryView, {
all, all,

View file

@ -6,9 +6,10 @@
} }
.listSummaryView { .listSummaryView {
margin: 8px 0; margin: 14px 0;
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, 13px); grid-template-columns: repeat(auto-fill, 13px);
grid-gap: 10px; grid-gap: 10px;
place-items: center; place-items: center;
max-width: 900px;
} }

View file

@ -5,21 +5,25 @@
} }
} }
.body { .main {
padding: 10px 15px; padding: 10px 15px;
@media (--breakpoint-not-small) { @media (--breakpoint-not-small) {
padding: 10px 40px; padding: 10px 40px;
} }
} }
.actionFooter { .head {
display: flex; display: flex;
button { align-items: center;
flex-wrap: wrap;
}
.action {
margin: 0 5px; margin: 0 5px;
&:first-child { display: grid;
margin-left: 0; grid-template-columns: auto auto;
} gap: 10px;
} place-items: center;
} }
.refresh { .refresh {

View file

@ -1,8 +1,8 @@
import Tooltip from '@reach/tooltip';
import { formatDistance } from 'date-fns'; import { formatDistance } from 'date-fns';
import * as React from 'react'; import * as React from 'react';
import { RotateCw, Zap } from 'react-feather'; import { RotateCw } from 'react-feather';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import Collapsible from 'src/components/Collapsible';
import CollapsibleSectionHeader from 'src/components/CollapsibleSectionHeader'; import CollapsibleSectionHeader from 'src/components/CollapsibleSectionHeader';
import { useUpdateProviderItem } from 'src/components/proxies/proxies.hooks'; import { useUpdateProviderItem } from 'src/components/proxies/proxies.hooks';
import { connect, useStoreActions } from 'src/components/StateProvider'; import { connect, useStoreActions } from 'src/components/StateProvider';
@ -18,6 +18,7 @@ import { DelayMapping, State } from 'src/store/types';
import { useState2 } from '$src/hooks/basic'; import { useState2 } from '$src/hooks/basic';
import { ZapAnimated } from '../shared/ZapAnimated';
import { useFilteredAndSorted } from './hooks'; import { useFilteredAndSorted } from './hooks';
import { ProxyList, ProxyListSummaryView } from './ProxyList'; import { ProxyList, ProxyListSummaryView } from './ProxyList';
import s from './ProxyProvider.module.scss'; import s from './ProxyProvider.module.scss';
@ -56,6 +57,7 @@ function ProxyProviderImpl({
const updateProvider = useUpdateProviderItem({ dispatch, apiConfig, name }); const updateProvider = useUpdateProviderItem({ dispatch, apiConfig, name });
const healthcheckProvider = useCallback(() => { const healthcheckProvider = useCallback(() => {
if (checkingHealth.value) return;
checkingHealth.set(true); checkingHealth.set(true);
const stop = () => checkingHealth.set(false); const stop = () => checkingHealth.set(false);
dispatch(healthcheckProviderByName(apiConfig, name)).then(stop, stop); dispatch(healthcheckProviderByName(apiConfig, name)).then(stop, stop);
@ -71,7 +73,8 @@ function ProxyProviderImpl({
const timeAgo = formatDistance(new Date(updatedAt), new Date()); const timeAgo = formatDistance(new Date(updatedAt), new Date());
return ( return (
<div className={s.body}> <div className={s.main}>
<div className={s.head}>
<CollapsibleSectionHeader <CollapsibleSectionHeader
name={name} name={name}
toggle={toggle} toggle={toggle}
@ -79,24 +82,24 @@ function ProxyProviderImpl({
isOpen={isOpen} isOpen={isOpen}
qty={proxies.length} qty={proxies.length}
/> />
<div className={s.action}>
<Tooltip label={'Update'}>
<Button kind="circular" onClick={updateProvider}>
<Refresh />
</Button>
</Tooltip>
<Tooltip label={'Health Check'}>
<Button kind="circular" onClick={healthcheckProvider}>
<ZapAnimated animate={checkingHealth.value} size={16} />
</Button>
</Tooltip>
</div>
</div>
<div className={s.updatedAt}> <div className={s.updatedAt}>
<small>Updated {timeAgo} ago</small> <small>Updated {timeAgo} ago</small>
</div> </div>
<Collapsible isOpen={isOpen}> {isOpen ? <ProxyList all={proxies} /> : <ProxyListSummaryView all={proxies} />}
<ProxyList all={proxies} />
<div className={s.actionFooter}>
<Button text="Update" start={<Refresh />} onClick={updateProvider} />
<Button
text="Health Check"
start={<Zap size={16} />}
onClick={healthcheckProvider}
isLoading={checkingHealth.value}
/>
</div>
</Collapsible>
<Collapsible isOpen={!isOpen}>
<ProxyListSummaryView all={proxies} />
</Collapsible>
</div> </div>
); );
} }

View file

@ -0,0 +1,12 @@
.animate {
--saturation: 70%;
stroke: hsl(46deg var(--saturation) 45%);
animation: zap-pulse 0.7s 0s ease-in-out none normal infinite;
}
// prettier-ignore
@keyframes zap-pulse {
0% { stroke: hsl(46deg var(--saturation) 45%); }
50% { stroke: hsl(46deg var(--saturation) 95%); }
100% { stroke: hsl(46deg var(--saturation) 45%); }
}

View file

@ -0,0 +1,25 @@
import cx from 'clsx';
import * as React from 'react';
import s from './ZapAnimated.module.scss';
export function ZapAnimated(props: { size?: number; animate?: boolean }) {
const size = props.size || 24;
const cls = cx({ [s.animate]: props.animate });
return (
<svg
className={cls}
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
</svg>
);
}