feat: profile loading animation

This commit is contained in:
GyDi 2021-12-26 14:08:25 +08:00
parent cc96b5ad04
commit 2fdcc9c584
2 changed files with 37 additions and 24 deletions

View File

@ -1,18 +1,20 @@
import React from "react"; import React, { useState } from "react";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { import {
alpha, alpha,
Box, Box,
ButtonBase,
styled, styled,
Typography, Typography,
LinearProgress, LinearProgress,
IconButton, IconButton,
keyframes,
} from "@mui/material"; } from "@mui/material";
import { MenuRounded } from "@mui/icons-material"; import { useSWRConfig } from "swr";
import { RefreshRounded } from "@mui/icons-material";
import { CmdType } from "../services/types"; import { CmdType } from "../services/types";
import parseTraffic from "../utils/parse-traffic"; import parseTraffic from "../utils/parse-traffic";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
import { updateProfile } from "../services/cmds";
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
@ -27,15 +29,23 @@ const Wrapper = styled(Box)(({ theme }) => ({
boxSizing: "border-box", boxSizing: "border-box",
})); }));
const round = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
interface Props { interface Props {
index: number;
selected: boolean; selected: boolean;
itemData: CmdType.ProfileItem; itemData: CmdType.ProfileItem;
onClick: () => void; onClick: () => void;
onUpdate: () => void;
} }
const ProfileItemComp: React.FC<Props> = (props) => { const ProfileItemComp: React.FC<Props> = (props) => {
const { selected, itemData, onClick, onUpdate } = props; const { index, selected, itemData, onClick } = props;
const { mutate } = useSWRConfig();
const [loading, setLoading] = useState(false);
const { name = "Profile", extra, updated = 0 } = itemData; const { name = "Profile", extra, updated = 0 } = itemData;
const { upload = 0, download = 0, total = 0 } = extra ?? {}; const { upload = 0, download = 0, total = 0 } = extra ?? {};
@ -44,6 +54,19 @@ const ProfileItemComp: React.FC<Props> = (props) => {
const progress = Math.round(((download + upload) * 100) / (total + 0.1)); const progress = Math.round(((download + upload) * 100) / (total + 0.1));
const fromnow = updated > 0 ? dayjs(updated * 1000).fromNow() : ""; const fromnow = updated > 0 ? dayjs(updated * 1000).fromNow() : "";
const onUpdate = async () => {
if (loading) return;
setLoading(true);
try {
await updateProfile(index);
mutate("getProfiles");
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
return ( return (
<Wrapper <Wrapper
sx={({ palette }) => { sx={({ palette }) => {
@ -88,14 +111,19 @@ const ProfileItemComp: React.FC<Props> = (props) => {
</Typography> </Typography>
<IconButton <IconButton
sx={{ width: 30, height: 30 }} sx={{
width: 30,
height: 30,
animation: loading ? `1s linear infinite ${round}` : "none",
}}
color="inherit" color="inherit"
disabled={loading}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onUpdate(); onUpdate();
}} }}
> >
<MenuRounded /> <RefreshRounded />
</IconButton> </IconButton>
</Box> </Box>

View File

@ -1,12 +1,7 @@
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import useSWR, { useSWRConfig } from "swr"; import useSWR, { useSWRConfig } from "swr";
import { Box, Button, Grid, TextField, Typography } from "@mui/material"; import { Box, Button, Grid, TextField, Typography } from "@mui/material";
import { import { getProfiles, importProfile, putProfiles } from "../services/cmds";
getProfiles,
importProfile,
putProfiles,
updateProfile,
} from "../services/cmds";
import { getProxies } from "../services/api"; import { getProxies } from "../services/api";
import ProfileItemComp from "../components/profile-item"; import ProfileItemComp from "../components/profile-item";
import useNotice from "../utils/use-notice"; import useNotice from "../utils/use-notice";
@ -54,16 +49,6 @@ const ProfilePage = () => {
}); });
}; };
const onUpdateProfile = (index: number) => {
updateProfile(index)
.then(() => {
mutate("getProfiles");
})
.catch((err) => {
console.error(err);
});
};
return ( return (
<Box sx={{ width: 0.9, maxWidth: "850px", mx: "auto", mb: 2 }}> <Box sx={{ width: 0.9, maxWidth: "850px", mx: "auto", mb: 2 }}>
<Typography variant="h4" component="h1" sx={{ py: 2, mb: 1 }}> <Typography variant="h4" component="h1" sx={{ py: 2, mb: 1 }}>
@ -94,10 +79,10 @@ const ProfilePage = () => {
{profiles?.items?.map((item, idx) => ( {profiles?.items?.map((item, idx) => (
<Grid item xs={12} sm={6} key={item.file}> <Grid item xs={12} sm={6} key={item.file}>
<ProfileItemComp <ProfileItemComp
index={idx}
selected={profiles.current === idx} selected={profiles.current === idx}
itemData={item} itemData={item}
onClick={() => onProfileChange(idx)} onClick={() => onProfileChange(idx)}
onUpdate={() => onUpdateProfile(idx)}
/> />
</Grid> </Grid>
))} ))}