Always show update and health check buttons for proxy provider
ref #649
This commit is contained in:
parent
e62b958e30
commit
69423bdfff
8 changed files with 102 additions and 67 deletions
|
@ -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>
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
12
src/components/shared/ZapAnimated.module.scss
Normal file
12
src/components/shared/ZapAnimated.module.scss
Normal 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%); }
|
||||||
|
}
|
25
src/components/shared/ZapAnimated.tsx
Normal file
25
src/components/shared/ZapAnimated.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue