diff --git a/src/components/proxy/provider-button.tsx b/src/components/proxy/provider-button.tsx index 0f14b7eb..384336f3 100644 --- a/src/components/proxy/provider-button.tsx +++ b/src/components/proxy/provider-button.tsx @@ -13,14 +13,19 @@ import { Typography, Divider, LinearProgress, + keyframes, } from "@mui/material"; import { RefreshRounded } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; -import { useLockFn } from "ahooks"; import { getProxyProviders, proxyProviderUpdate } from "@/services/api"; import { BaseDialog } from "../base"; import parseTraffic from "@/utils/parse-traffic"; +const round = keyframes` + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +`; + export const ProviderButton = () => { const { t } = useTranslation(); const { data } = useSWR("getProxyProviders", getProxyProviders); @@ -28,12 +33,31 @@ export const ProviderButton = () => { const [open, setOpen] = useState(false); const hasProvider = Object.keys(data || {}).length > 0; + const [updating, setUpdating] = useState( + Object.keys(data || {}).map(() => false) + ); - const handleUpdate = useLockFn(async (key: string) => { - await proxyProviderUpdate(key); - await mutate("getProxies"); - await mutate("getProxyProviders"); - }); + const setUpdatingAt = (status: boolean, index: number) => { + setUpdating((prev) => { + const next = [...prev]; + next[index] = status; + return next; + }); + }; + const handleUpdate = async (key: string, index: number) => { + setUpdatingAt(true, index); + proxyProviderUpdate(key) + .then(async () => { + setUpdatingAt(false, index); + await mutate("getProxies"); + await mutate("getProxyProviders"); + }) + .catch(async () => { + setUpdatingAt(false, index); + await mutate("getProxies"); + await mutate("getProxyProviders"); + }); + }; if (!hasProvider) return null; @@ -57,11 +81,11 @@ export const ProviderButton = () => { variant="contained" size="small" onClick={async () => { - Object.entries(data || {}).forEach(async ([key, item]) => { - await proxyProviderUpdate(key); - await mutate("getProxies"); - await mutate("getProxyProviders"); - }); + Object.entries(data || {}).forEach( + async ([key, item], index) => { + await handleUpdate(key, index); + } + ); }} > {t("Update All")} @@ -75,7 +99,7 @@ export const ProviderButton = () => { onCancel={() => setOpen(false)} > - {Object.entries(data || {}).map(([key, item]) => { + {Object.entries(data || {}).map(([key, item], index) => { const time = dayjs(item.updatedAt); const sub = item.subscriptionInfo; const hasSubInfo = !!sub; @@ -149,7 +173,12 @@ export const ProviderButton = () => { size="small" color="inherit" title="Update Provider" - onClick={() => handleUpdate(key)} + onClick={() => handleUpdate(key, index)} + sx={{ + animation: updating[index] + ? `1s linear infinite ${round}` + : "none", + }} > diff --git a/src/components/rule/provider-button.tsx b/src/components/rule/provider-button.tsx index f08d80a7..97e87369 100644 --- a/src/components/rule/provider-button.tsx +++ b/src/components/rule/provider-button.tsx @@ -12,13 +12,18 @@ import { Box, alpha, Divider, + keyframes, } from "@mui/material"; import { RefreshRounded } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; -import { useLockFn } from "ahooks"; import { getRuleProviders, ruleProviderUpdate } from "@/services/api"; import { BaseDialog } from "../base"; +const round = keyframes` + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +`; + export const ProviderButton = () => { const { t } = useTranslation(); const { data } = useSWR("getRuleProviders", getRuleProviders); @@ -26,12 +31,31 @@ export const ProviderButton = () => { const [open, setOpen] = useState(false); const hasProvider = Object.keys(data || {}).length > 0; + const [updating, setUpdating] = useState( + Object.keys(data || {}).map(() => false) + ); - const handleUpdate = useLockFn(async (key: string) => { - await ruleProviderUpdate(key); - await mutate("getRules"); - await mutate("getRuleProviders"); - }); + const setUpdatingAt = (status: boolean, index: number) => { + setUpdating((prev) => { + const next = [...prev]; + next[index] = status; + return next; + }); + }; + const handleUpdate = async (key: string, index: number) => { + setUpdatingAt(true, index); + ruleProviderUpdate(key) + .then(async () => { + setUpdatingAt(false, index); + await mutate("getRules"); + await mutate("getRuleProviders"); + }) + .catch(async () => { + setUpdatingAt(false, index); + await mutate("getRules"); + await mutate("getRuleProviders"); + }); + }; if (!hasProvider) return null; @@ -55,11 +79,11 @@ export const ProviderButton = () => { variant="contained" size="small" onClick={async () => { - Object.entries(data || {}).forEach(async ([key, item]) => { - await ruleProviderUpdate(key); - await mutate("getRules"); - await mutate("getRuleProviders"); - }); + Object.entries(data || {}).forEach( + async ([key, item], index) => { + await handleUpdate(key, index); + } + ); }} > {t("Update All")} @@ -73,7 +97,7 @@ export const ProviderButton = () => { onCancel={() => setOpen(false)} > - {Object.entries(data || {}).map(([key, item]) => { + {Object.entries(data || {}).map(([key, item], index) => { const time = dayjs(item.updatedAt); return ( <> @@ -122,7 +146,12 @@ export const ProviderButton = () => { size="small" color="inherit" title="Update Provider" - onClick={() => handleUpdate(key)} + onClick={() => handleUpdate(key, index)} + sx={{ + animation: updating[index] + ? `1s linear infinite ${round}` + : "none", + }} > diff --git a/src/components/setting/mods/controller-viewer.tsx b/src/components/setting/mods/controller-viewer.tsx index 17205d9c..419da3cf 100644 --- a/src/components/setting/mods/controller-viewer.tsx +++ b/src/components/setting/mods/controller-viewer.tsx @@ -36,7 +36,7 @@ export const ControllerViewer = forwardRef((props, ref) => { return (