feat(settings page): add loading state (#1157)

* feat(settings page): add loading state

* fix: type
This commit is contained in:
Eric Huang 2024-06-09 06:26:07 +08:00 committed by GitHub
parent ca323371a7
commit 2913b911e3
4 changed files with 29 additions and 8 deletions

View File

@ -1,4 +1,4 @@
import React, { ReactNode } from "react"; import React, { ReactNode, useState } from "react";
import { import {
Box, Box,
List, List,
@ -8,13 +8,15 @@ import {
ListSubheader, ListSubheader,
} from "@mui/material"; } from "@mui/material";
import { ChevronRightRounded } from "@mui/icons-material"; import { ChevronRightRounded } from "@mui/icons-material";
import CircularProgress from "@mui/material/CircularProgress";
import isAsyncFunction from "@/utils/is-async-function";
interface ItemProps { interface ItemProps {
label: ReactNode; label: ReactNode;
extra?: ReactNode; extra?: ReactNode;
children?: ReactNode; children?: ReactNode;
secondary?: ReactNode; secondary?: ReactNode;
onClick?: () => void; onClick?: () => void | Promise<any>;
} }
export const SettingItem: React.FC<ItemProps> = (props) => { export const SettingItem: React.FC<ItemProps> = (props) => {
@ -28,11 +30,27 @@ export const SettingItem: React.FC<ItemProps> = (props) => {
</Box> </Box>
); );
const [isLoading, setIsLoading] = useState(false);
const handleClick = () => {
if (onClick) {
if (isAsyncFunction(onClick)) {
setIsLoading(true);
onClick()!.finally(() => setIsLoading(false));
} else {
onClick();
}
}
};
return clickable ? ( return clickable ? (
<ListItem disablePadding> <ListItem disablePadding>
<ListItemButton onClick={onClick}> <ListItemButton onClick={handleClick} disabled={isLoading}>
<ListItemText primary={primary} secondary={secondary} /> <ListItemText primary={primary} secondary={secondary} />
<ChevronRightRounded /> {isLoading ? (
<CircularProgress color="inherit" size={20} />
) : (
<ChevronRightRounded />
)}
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
) : ( ) : (

View File

@ -51,14 +51,14 @@ const SettingClash = ({ onError }: Props) => {
const onChangeVerge = (patch: Partial<IVergeConfig>) => { const onChangeVerge = (patch: Partial<IVergeConfig>) => {
mutateVerge({ ...verge, ...patch }, false); mutateVerge({ ...verge, ...patch }, false);
}; };
const onUpdateGeo = useLockFn(async () => { const onUpdateGeo = async () => {
try { try {
await updateGeoData(); await updateGeoData();
Notice.success(t("GeoData Updated")); Notice.success(t("GeoData Updated"));
} catch (err: any) { } catch (err: any) {
Notice.error(err?.response.data.message || err.toString()); Notice.error(err?.response.data.message || err.toString());
} }
}); };
return ( return (
<SettingList title={t("Clash Setting")}> <SettingList title={t("Clash Setting")}>

View File

@ -55,7 +55,7 @@ const SettingVerge = ({ onError }: Props) => {
mutateVerge({ ...verge, ...patch }, false); mutateVerge({ ...verge, ...patch }, false);
}; };
const onCheckUpdate = useLockFn(async () => { const onCheckUpdate = async () => {
try { try {
const info = await checkUpdate(); const info = await checkUpdate();
if (!info?.shouldUpdate) { if (!info?.shouldUpdate) {
@ -66,7 +66,7 @@ const SettingVerge = ({ onError }: Props) => {
} catch (err: any) { } catch (err: any) {
Notice.error(err.message || err.toString()); Notice.error(err.message || err.toString());
} }
}); };
return ( return (
<SettingList title={t("Verge Setting")}> <SettingList title={t("Verge Setting")}>

View File

@ -0,0 +1,3 @@
export default function isAsyncFunction(fn: Function): boolean {
return fn.constructor.name === "AsyncFunction";
}