diff --git a/frontend/interface/ipc/useClashCore.ts b/frontend/interface/ipc/useClashCore.ts index ed8f9bf2c48..fa8c42704f0 100644 --- a/frontend/interface/ipc/useClashCore.ts +++ b/frontend/interface/ipc/useClashCore.ts @@ -1,4 +1,4 @@ -import { Clash, clash as clashApi } from "@/service"; +import { Clash, ProviderRules, clash as clashApi } from "@/service"; import * as tauri from "@/service/tauri"; import useSWR from "swr"; @@ -47,6 +47,17 @@ export const useClashCore = () => { const getRules = useSWR("getRules", clash.getRules); + const getRulesProviders = useSWR( + "getRulesProviders", + clash.getRulesProviders, + ); + + const updateRulesProviders = async (name: string) => { + await clash.updateRulesProviders(name); + + await getRulesProviders.mutate(); + }; + return { data, isLoading, @@ -54,35 +65,7 @@ export const useClashCore = () => { updateProxiesDelay, setGroupProxy, getRules, + getRulesProviders, + updateRulesProviders, }; }; - -// export class UseClashCore { -// public proxies; - -// constructor() { -// this.proxies = useSWR("getProxies", getProxies); -// } - -// public async updateGroupDelay(index: number, options?: Clash.DelayOptions) { -// console.log(index); -// // const group = this.proxies.data?.groups[index]; -// console.log(this.proxies.data?.groups); - -// // if (!group) { -// // return; -// // } - -// // const result = await getGroupDelay(group?.name, options); - -// // console.log(result); - -// // group.all?.forEach((item) => { -// // if (result) -// // }) - -// // Object.entries(result).forEach(([name, delay]) => { - -// // }) -// } -// } diff --git a/frontend/interface/service/clash.ts b/frontend/interface/service/clash.ts index 97ffedbccd2..c08b8c015e6 100644 --- a/frontend/interface/service/clash.ts +++ b/frontend/interface/service/clash.ts @@ -147,6 +147,22 @@ export const clash = () => { }); }; + const getRulesProviders = async () => { + return ( + await ( + await buildRequest() + )("/providers/rules", { + method: "GET", + }) + )?.providers; + }; + + const updateRulesProviders = async (name: string) => { + return (await buildRequest())(`/providers/rules/${name}`, { + method: "PUT", + }); + }; + return { getConfigs, setConfigs, @@ -157,5 +173,7 @@ export const clash = () => { getProxies, setProxies, deleteConnections, + getRulesProviders, + updateRulesProviders, }; }; diff --git a/frontend/interface/service/types.ts b/frontend/interface/service/types.ts index 622c44f25e4..dbd8a66c075 100644 --- a/frontend/interface/service/types.ts +++ b/frontend/interface/service/types.ts @@ -171,3 +171,13 @@ export interface LogMessage { time?: string; payload: string; } + +export interface ProviderRules { + behavior: string; + format: string; + name: string; + ruleCount: number; + type: string; + updatedAt: string; + vehicleType: string; +} diff --git a/frontend/nyanpasu/src/components/providers/rules-provider.tsx b/frontend/nyanpasu/src/components/providers/rules-provider.tsx index a3b62005cc1..ec75bc13d13 100644 --- a/frontend/nyanpasu/src/components/providers/rules-provider.tsx +++ b/frontend/nyanpasu/src/components/providers/rules-provider.tsx @@ -1,15 +1,8 @@ -import { NotificationType, useNotification } from "@/hooks/use-notification"; -import { updateRulesProviders, type ProviderRules } from "@/services/api"; +import { useMessage } from "@/hooks/use-notification"; import { Refresh } from "@mui/icons-material"; -import { - Box, - Card, - CardContent, - CircularProgress, - IconButton, - Stack, - Typography, -} from "@mui/material"; +import LoadingButton from "@mui/lab/LoadingButton/LoadingButton"; +import { Chip, Paper } from "@mui/material"; +import { ProviderRules, useClashCore } from "@nyanpasu/interface"; import { useLockFn } from "ahooks"; import dayjs from "dayjs"; import { useState } from "react"; @@ -17,96 +10,71 @@ import { useTranslation } from "react-i18next"; export interface RulesProviderProps { provider: ProviderRules; - onRulesProviderUpdated: () => void; } -export default function RulesProvider(props: RulesProviderProps) { - const { provider, onRulesProviderUpdated } = props; +export default function RulesProvider({ provider }: RulesProviderProps) { const { t } = useTranslation(); - const [updating, setUpdating] = useState(false); - const onUpdate = useLockFn(async () => { - setUpdating(true); + const [loading, setLoading] = useState(false); + + const { updateRulesProviders } = useClashCore(); + + const handleClick = useLockFn(async () => { try { + setLoading(true); + await updateRulesProviders(provider.name); - onRulesProviderUpdated(); - useNotification({ - title: t("Success"), - body: t("Update Rules Providers Success"), - type: NotificationType.Success, - }); - } catch (err: any) { - useNotification({ + } catch (e) { + useMessage(`Update ${provider.name} failed.\n${String(e)}`, { + type: "error", title: t("Error"), - body: err.message || err.toString(), - type: NotificationType.Error, }); } finally { - setUpdating(false); + setLoading(false); } }); return ( - - - - {updating ? ( - - ) : ( - - )} - + +
+
+

{provider.name}

+ +

+ {provider.vehicleType}/{provider.behavior} +

+
- - - - {provider.name} - - - {provider.vehicleType}/{provider.behavior} - - +
+ {t("Last Update", { + fromNow: dayjs(provider.updatedAt).fromNow(), + })} +
+
- - - {t("Rule Set rules", { - rule: provider.ruleCount, - })} - - - {t("Last Update", { - fromNow: dayjs(provider.updatedAt).fromNow(), - })} - - - -
-
+
+ + + + + +
+ ); } diff --git a/frontend/nyanpasu/src/components/providers/update-providers.tsx b/frontend/nyanpasu/src/components/providers/update-providers.tsx new file mode 100644 index 00000000000..8e06cc4f443 --- /dev/null +++ b/frontend/nyanpasu/src/components/providers/update-providers.tsx @@ -0,0 +1,58 @@ +import { useMessage } from "@/hooks/use-notification"; +import LoadingButton from "@mui/lab/LoadingButton"; +import { useClashCore } from "@nyanpasu/interface"; +import { useLockFn } from "ahooks"; +import { useState } from "react"; +import { Refresh } from "@mui/icons-material"; +import { useTranslation } from "react-i18next"; + +export const UpdateProviders = () => { + const { t } = useTranslation(); + + const [loading, setLoading] = useState(false); + + const { getRulesProviders, updateRulesProviders } = useClashCore(); + + const handleProviderUpdate = useLockFn(async () => { + if (!getRulesProviders.data) { + useMessage(`No Providers.`, { + type: "info", + title: t("Info"), + }); + + return; + } + + try { + setLoading(true); + + const providers = Object.entries(getRulesProviders.data).map( + ([name]) => name, + ); + + await Promise.all( + providers.map((provider) => updateRulesProviders(provider)), + ); + } catch (e) { + useMessage(`Update all failed.\n${String(e)}`, { + type: "error", + title: t("Error"), + }); + } finally { + setLoading(false); + } + }); + + return ( + } + onClick={handleProviderUpdate} + > + {t("Update Rules Providers All")} + + ); +}; + +export default UpdateProviders; diff --git a/frontend/nyanpasu/src/pages/providers.tsx b/frontend/nyanpasu/src/pages/providers.tsx index 3274216cad7..81e6266fe19 100644 --- a/frontend/nyanpasu/src/pages/providers.tsx +++ b/frontend/nyanpasu/src/pages/providers.tsx @@ -1,144 +1,38 @@ -import { BasePage } from "@/components/base"; import RulesProvider from "@/components/providers/rules-provider"; -import { NotificationType, useNotification } from "@/hooks/use-notification"; -import { getRulesProviders, updateRulesProviders } from "@/services/api"; -import { Error as ErrorIcon } from "@mui/icons-material"; -import { LoadingButton } from "@mui/lab"; -import { - Box, - Button, - CircularProgress, - Stack, - Typography, -} from "@mui/material"; +import UpdateProviders from "@/components/providers/update-providers"; +import { Chip } from "@mui/material"; import Grid from "@mui/material/Unstable_Grid2"; -import { useLockFn } from "ahooks"; -import { useState } from "react"; +import { useClashCore } from "@nyanpasu/interface"; +import { BasePage } from "@nyanpasu/ui"; import { useTranslation } from "react-i18next"; -import useSWR from "swr"; export default function ProvidersPage() { const { t } = useTranslation(); - const { - data: rulesProviders, - error: rulesProvidersError, - isLoading: rulesProvidersLoading, - mutate: mutateRulesProviders, - } = useSWR("getClashProvidersRules", getRulesProviders); - const [updating, setUpdating] = useState(false); - const onUpdateAll = useLockFn(async () => { - setUpdating(true); - try { - const queue = rulesProviders!.map((provider) => - updateRulesProviders(provider.name), - ); - await Promise.all(queue); - await mutateRulesProviders(); - useNotification({ - title: t("Success"), - body: t("Update Rules Providers Success"), - type: NotificationType.Success, - }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - useNotification({ - title: t("Error"), - body: err.message || err.toString(), - type: NotificationType.Error, - }); - } finally { - setUpdating(false); - } - }); - - const onRulesProviderUpdated = () => { - mutateRulesProviders(); - }; + const { getRulesProviders } = useClashCore(); return ( - - - {t("Rules Providers")} - - - {t("Update Rules Providers All")} - - - - { - /* 这里需要抽象个状态机组件出来 */ - rulesProvidersLoading || rulesProvidersError ? ( - - {rulesProvidersLoading ? ( - - ) : ( - -

- {t("Error")} -

- - -
- )} -
- ) : ( - - {rulesProviders!.map((provider) => ( - - - - ))} - - ) - } -
+
+
+ + + +
+ + {getRulesProviders.data && ( + + {Object.entries(getRulesProviders.data).map(([name, provider]) => ( + + + + ))} + + )} +
); }