Skip to content

Commit

Permalink
feat(providers): new design ProvidersPage
Browse files Browse the repository at this point in the history
  • Loading branch information
keiko233 committed Jun 7, 2024
1 parent b2fcc55 commit 8eee106
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 249 deletions.
45 changes: 14 additions & 31 deletions frontend/interface/ipc/useClashCore.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -47,42 +47,25 @@ export const useClashCore = () => {

const getRules = useSWR("getRules", clash.getRules);

const getRulesProviders = useSWR<{ [name: string]: ProviderRules }>(
"getRulesProviders",
clash.getRulesProviders,
);

const updateRulesProviders = async (name: string) => {
await clash.updateRulesProviders(name);

await getRulesProviders.mutate();
};

return {
data,
isLoading,
updateGroupDelay,
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]) => {

// // })
// }
// }
18 changes: 18 additions & 0 deletions frontend/interface/service/clash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -157,5 +173,7 @@ export const clash = () => {
getProxies,
setProxies,
deleteConnections,
getRulesProviders,
updateRulesProviders,
};
};
10 changes: 10 additions & 0 deletions frontend/interface/service/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
142 changes: 55 additions & 87 deletions frontend/nyanpasu/src/components/providers/rules-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,112 +1,80 @@
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";
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 (
<Card variant="outlined">
<CardContent
style={{
position: "relative",
}}
>
<IconButton
onClick={onUpdate}
disabled={updating}
sx={{
position: "absolute",
top: "50%",
right: "0.5em",
transform: "translateY(-50%)",
}}
size="medium"
>
{updating ? (
<CircularProgress size="1.11em" />
) : (
<Refresh
style={{
fontSize: "1.11em",
}}
/>
)}
</IconButton>
<Paper
className="p-5 flex flex-col gap-2"
sx={{
borderRadius: 6,
}}
>
<div className="flex items-start justify-between gap-2">
<div className="ml-1">
<p className="text-lg font-bold truncate">{provider.name}</p>

<p className="truncate text-sm">
{provider.vehicleType}/{provider.behavior}
</p>
</div>

<Stack gap={1}>
<Box display="flex" gap={1} alignItems="end">
<Typography
variant="h6"
component="div"
sx={{
padding: 0,
margin: 0,
lineHeight: "inherit",
}}
>
<b>{provider.name}</b>
</Typography>
<Typography variant="caption" color="GrayText">
{provider.vehicleType}/{provider.behavior}
</Typography>
</Box>
<div className="text-sm text-right">
{t("Last Update", {
fromNow: dayjs(provider.updatedAt).fromNow(),
})}
</div>
</div>

<Stack>
<Typography variant="body1">
{t("Rule Set rules", {
rule: provider.ruleCount,
})}
</Typography>
<Typography variant="body2">
{t("Last Update", {
fromNow: dayjs(provider.updatedAt).fromNow(),
})}
</Typography>
</Stack>
</Stack>
</CardContent>
</Card>
<div className="flex items-center justify-between">
<Chip
className="font-bold truncate"
label={t("Rule Set rules", {
rule: provider.ruleCount,
})}
/>

<LoadingButton
loading={loading}
size="small"
variant="contained"
className="!size-8 !min-w-0"
onClick={handleClick}
>
<Refresh />
</LoadingButton>
</div>
</Paper>
);
}
58 changes: 58 additions & 0 deletions frontend/nyanpasu/src/components/providers/update-providers.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<LoadingButton
variant="contained"
loading={loading}
startIcon={<Refresh />}
onClick={handleProviderUpdate}
>
{t("Update Rules Providers All")}
</LoadingButton>
);
};

export default UpdateProviders;
Loading

0 comments on commit 8eee106

Please sign in to comment.