From 459de998751bee702615eb790e2ad2313877b386 Mon Sep 17 00:00:00 2001
From: Haishan <haishanhan@gmail.com>
Date: Sat, 11 Jun 2022 17:07:59 +0800
Subject: Resolve a few "ts-expect-error"s

---
 src/components/StyleGuide.tsx | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

(limited to 'src/components/StyleGuide.tsx')

diff --git a/src/components/StyleGuide.tsx b/src/components/StyleGuide.tsx
index ee38697..1f5f3be 100644
--- a/src/components/StyleGuide.tsx
+++ b/src/components/StyleGuide.tsx
@@ -21,7 +21,9 @@ const optionsRule = [
   { label: 'Direct', value: 'Direct' },
 ];
 
-const Pane = ({ children, style }) => <div style={{ ...paneStyle, ...style }}>{children}</div>;
+const Pane = ({ children, style }: { children: React.ReactNode; style?: React.CSSProperties }) => (
+  <div style={{ ...paneStyle, ...style }}>{children}</div>
+);
 
 function useToggle(initialState = false) {
   const [onoff, setonoff] = React.useState(initialState);
@@ -40,19 +42,15 @@ class StyleGuide extends PureComponent {
   render() {
     return (
       <div>
-        {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
         <Pane>
           <SwitchExample />
         </Pane>
-        {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
         <Pane>
           <Input />
         </Pane>
-        {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
         <Pane>
           <ToggleSwitch name="test" options={optionsRule} value="Rule" onChange={noop} />
         </Pane>
-        {/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
         <Pane>
           <Button text="Test Lxatency" start={<Zap size={16} />} />
           <Button text="Test Lxatency" start={<Zap size={16} />} isLoading />
-- 
cgit v1.3.1


From 69423bdfff89e6c00db4406319189a8852c05aa7 Mon Sep 17 00:00:00 2001
From: Haishan <haishanhan@gmail.com>
Date: Sun, 12 Jun 2022 09:30:45 +0800
Subject: Always show update and health check buttons for proxy provider

ref #649
---
 src/components/StyleGuide.tsx                    |  4 ++
 src/components/proxies/ProxyGroup.module.scss    | 13 ++----
 src/components/proxies/ProxyGroup.tsx            | 39 +++++++----------
 src/components/proxies/ProxyList.module.scss     |  3 +-
 src/components/proxies/ProxyProvider.module.scss | 20 +++++----
 src/components/proxies/ProxyProvider.tsx         | 53 +++++++++++++-----------
 src/components/shared/ZapAnimated.module.scss    | 12 ++++++
 src/components/shared/ZapAnimated.tsx            | 25 +++++++++++
 8 files changed, 102 insertions(+), 67 deletions(-)
 create mode 100644 src/components/shared/ZapAnimated.module.scss
 create mode 100644 src/components/shared/ZapAnimated.tsx

(limited to 'src/components/StyleGuide.tsx')

diff --git a/src/components/StyleGuide.tsx b/src/components/StyleGuide.tsx
index 1f5f3be..910c538 100644
--- a/src/components/StyleGuide.tsx
+++ b/src/components/StyleGuide.tsx
@@ -4,6 +4,7 @@ import Loading from 'src/components/Loading';
 
 import Button from './Button';
 import Input from './Input';
+import { ZapAnimated } from './shared/ZapAnimated';
 import SwitchThemed from './SwitchThemed';
 import ToggleSwitch from './ToggleSwitch';
 
@@ -42,6 +43,9 @@ class StyleGuide extends PureComponent {
   render() {
     return (
       <div>
+        <Pane>
+          <ZapAnimated />
+        </Pane>
         <Pane>
           <SwitchExample />
         </Pane>
diff --git a/src/components/proxies/ProxyGroup.module.scss b/src/components/proxies/ProxyGroup.module.scss
index 6e9d91f..85b68b6 100644
--- a/src/components/proxies/ProxyGroup.module.scss
+++ b/src/components/proxies/ProxyGroup.module.scss
@@ -4,17 +4,10 @@
 
 .groupHead {
   display: flex;
+  flex-wrap: wrap;
   align-items: center;
 }
 
-.latencyButton {
-  margin-left: 5px;
-}
-
-.zapWrapper {
-  width: 20px;
-  height: 20px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
+.action {
+  margin: 0 5px;
 }
diff --git a/src/components/proxies/ProxyGroup.tsx b/src/components/proxies/ProxyGroup.tsx
index d6a4e99..6d6c59e 100644
--- a/src/components/proxies/ProxyGroup.tsx
+++ b/src/components/proxies/ProxyGroup.tsx
@@ -1,26 +1,20 @@
+import Tooltip from '@reach/tooltip';
 import * as React from 'react';
-import { Zap } from 'react-feather';
 
+import { useState2 } from '$src/hooks/basic';
 import { State } from '$src/store/types';
 
 import { getCollapsibleIsOpen, getHideUnavailableProxies, getProxySortBy } from '../../store/app';
 import { getProxies, switchProxy } from '../../store/proxies';
 import Button from '../Button';
 import CollapsibleSectionHeader from '../CollapsibleSectionHeader';
+import { ZapAnimated } from '../shared/ZapAnimated';
 import { connect, useStoreActions } from '../StateProvider';
 import { useFilteredAndSorted } from './hooks';
 import s0 from './ProxyGroup.module.scss';
 import { ProxyList, ProxyListSummaryView } from './ProxyList';
 
-const { createElement, useCallback, useMemo, useState } = React;
-
-function ZapWrapper() {
-  return (
-    <div className={s0.zapWrapper}>
-      <Zap size={16} />
-    </div>
-  );
-}
+const { createElement, useCallback, useMemo } = React;
 
 function ProxyGroupImpl({
   name,
@@ -56,14 +50,15 @@ function ProxyGroupImpl({
     [apiConfig, dispatch, name, isSelectable]
   );
 
-  const [isTestingLatency, setIsTestingLatency] = useState(false);
+  const testingLatency = useState2(false);
   const testLatency = useCallback(async () => {
-    setIsTestingLatency(true);
+    if (testingLatency.value) return;
+    testingLatency.set(true);
     try {
       await requestDelayForProxies(apiConfig, all);
     } catch (err) {}
-    setIsTestingLatency(false);
-  }, [all, apiConfig, requestDelayForProxies]);
+    testingLatency.set(false);
+  }, [all, apiConfig, requestDelayForProxies, testingLatency]);
 
   return (
     <div className={s0.group}>
@@ -75,15 +70,13 @@ function ProxyGroupImpl({
           qty={all.length}
           isOpen={isOpen}
         />
-        <Button
-          className={s0.latencyButton}
-          title="Test latency"
-          kind="minimal"
-          onClick={testLatency}
-          isLoading={isTestingLatency}
-        >
-          <ZapWrapper />
-        </Button>
+        <div className={s0.action}>
+          <Tooltip label={'Test latency'}>
+            <Button kind="circular" onClick={testLatency}>
+              <ZapAnimated animate={testingLatency.value} size={16} />
+            </Button>
+          </Tooltip>
+        </div>
       </div>
       {createElement(isOpen ? ProxyList : ProxyListSummaryView, {
         all,
diff --git a/src/components/proxies/ProxyList.module.scss b/src/components/proxies/ProxyList.module.scss
index d548572..12fea7e 100644
--- a/src/components/proxies/ProxyList.module.scss
+++ b/src/components/proxies/ProxyList.module.scss
@@ -6,9 +6,10 @@
 }
 
 .listSummaryView {
-  margin: 8px 0;
+  margin: 14px 0;
   display: grid;
   grid-template-columns: repeat(auto-fill, 13px);
   grid-gap: 10px;
   place-items: center;
+  max-width: 900px;
 }
diff --git a/src/components/proxies/ProxyProvider.module.scss b/src/components/proxies/ProxyProvider.module.scss
index 534305b..bc66bca 100644
--- a/src/components/proxies/ProxyProvider.module.scss
+++ b/src/components/proxies/ProxyProvider.module.scss
@@ -5,21 +5,25 @@
   }
 }
 
-.body {
+.main {
   padding: 10px 15px;
   @media (--breakpoint-not-small) {
     padding: 10px 40px;
   }
 }
 
-.actionFooter {
+.head {
   display: flex;
-  button {
-    margin: 0 5px;
-    &:first-child {
-      margin-left: 0;
-    }
-  }
+  align-items: center;
+  flex-wrap: wrap;
+}
+
+.action {
+  margin: 0 5px;
+  display: grid;
+  grid-template-columns: auto auto;
+  gap: 10px;
+  place-items: center;
 }
 
 .refresh {
diff --git a/src/components/proxies/ProxyProvider.tsx b/src/components/proxies/ProxyProvider.tsx
index 972a735..7939190 100644
--- a/src/components/proxies/ProxyProvider.tsx
+++ b/src/components/proxies/ProxyProvider.tsx
@@ -1,8 +1,8 @@
+import Tooltip from '@reach/tooltip';
 import { formatDistance } from 'date-fns';
 import * as React from 'react';
-import { RotateCw, Zap } from 'react-feather';
+import { RotateCw } from 'react-feather';
 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';
@@ -18,6 +18,7 @@ import { DelayMapping, State } from 'src/store/types';
 
 import { useState2 } from '$src/hooks/basic';
 
+import { ZapAnimated } from '../shared/ZapAnimated';
 import { useFilteredAndSorted } from './hooks';
 import { ProxyList, ProxyListSummaryView } from './ProxyList';
 import s from './ProxyProvider.module.scss';
@@ -56,6 +57,7 @@ function ProxyProviderImpl({
   const updateProvider = useUpdateProviderItem({ dispatch, apiConfig, name });
 
   const healthcheckProvider = useCallback(() => {
+    if (checkingHealth.value) return;
     checkingHealth.set(true);
     const stop = () => checkingHealth.set(false);
     dispatch(healthcheckProviderByName(apiConfig, name)).then(stop, stop);
@@ -71,32 +73,33 @@ function ProxyProviderImpl({
 
   const timeAgo = formatDistance(new Date(updatedAt), new Date());
   return (
-    <div className={s.body}>
-      <CollapsibleSectionHeader
-        name={name}
-        toggle={toggle}
-        type={vehicleType}
-        isOpen={isOpen}
-        qty={proxies.length}
-      />
+    <div className={s.main}>
+      <div className={s.head}>
+        <CollapsibleSectionHeader
+          name={name}
+          toggle={toggle}
+          type={vehicleType}
+          isOpen={isOpen}
+          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}>
         <small>Updated {timeAgo} ago</small>
       </div>
-      <Collapsible isOpen={isOpen}>
-        <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>
+      {isOpen ? <ProxyList all={proxies} /> : <ProxyListSummaryView all={proxies} />}
     </div>
   );
 }
diff --git a/src/components/shared/ZapAnimated.module.scss b/src/components/shared/ZapAnimated.module.scss
new file mode 100644
index 0000000..e4cb37b
--- /dev/null
+++ b/src/components/shared/ZapAnimated.module.scss
@@ -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%); }
+}
diff --git a/src/components/shared/ZapAnimated.tsx b/src/components/shared/ZapAnimated.tsx
new file mode 100644
index 0000000..e3b153a
--- /dev/null
+++ b/src/components/shared/ZapAnimated.tsx
@@ -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>
+  );
+}
-- 
cgit v1.3.1

