From ea8f1c52f95f27606435779c00f99f56d26b0442 Mon Sep 17 00:00:00 2001 From: GyDi <segydi@foxmail.com> Date: Sat, 8 Jan 2022 16:52:18 +0800 Subject: [PATCH] feat: profiles add menu and delete button --- src/components/profile-item.tsx | 218 +++++++++++++++++++------------- src/pages/profile.tsx | 12 +- 2 files changed, 138 insertions(+), 92 deletions(-) diff --git a/src/components/profile-item.tsx b/src/components/profile-item.tsx index fb6f021..6f1f70f 100644 --- a/src/components/profile-item.tsx +++ b/src/components/profile-item.tsx @@ -8,13 +8,15 @@ import { LinearProgress, IconButton, keyframes, + MenuItem, + Menu, } from "@mui/material"; import { useSWRConfig } from "swr"; import { RefreshRounded } from "@mui/icons-material"; import { CmdType } from "../services/types"; +import { updateProfile, deleteProfile } from "../services/cmds"; import parseTraffic from "../utils/parse-traffic"; import relativeTime from "dayjs/plugin/relativeTime"; -import { updateProfile } from "../services/cmds"; dayjs.extend(relativeTime); @@ -46,6 +48,8 @@ const ProfileItemComp: React.FC<Props> = (props) => { const { mutate } = useSWRConfig(); const [loading, setLoading] = useState(false); + const [anchorEl, setAnchorEl] = useState<any>(null); + const [position, setPosition] = useState({ left: 0, top: 0 }); const { name = "Profile", extra, updated = 0 } = itemData; const { upload = 0, download = 0, total = 0 } = extra ?? {}; @@ -55,6 +59,7 @@ const ProfileItemComp: React.FC<Props> = (props) => { const fromnow = updated > 0 ? dayjs(updated * 1000).fromNow() : ""; const onUpdate = async () => { + setAnchorEl(null); if (loading) return; setLoading(true); try { @@ -67,98 +72,135 @@ const ProfileItemComp: React.FC<Props> = (props) => { } }; - return ( - <Wrapper - sx={({ palette }) => { - const { mode, primary, text, grey } = palette; - const isDark = mode === "dark"; - - if (selected) { - const bgcolor = isDark - ? alpha(primary.main, 0.35) - : alpha(primary.main, 0.15); - - return { - bgcolor, - color: isDark ? alpha(text.secondary, 0.6) : text.secondary, - "& h2": { - color: isDark ? primary.light : primary.main, - }, - }; - } - const bgcolor = isDark - ? alpha(grey[700], 0.35) - : palette.background.paper; - return { - bgcolor, - color: isDark ? alpha(text.secondary, 0.6) : text.secondary, - "& h2": { - color: isDark ? text.primary : text.primary, - }, - }; - }} - onClick={onClick} - > - <Box display="flex" justifyContent="space-between"> - <Typography - width="calc(100% - 40px)" - variant="h6" - component="h2" - noWrap - title={name} - > - {name} - </Typography> + const onDelete = async () => { + setAnchorEl(null); + try { + await deleteProfile(index); + mutate("getProfiles"); + } catch (err) { + console.error(err); + } + }; + + const handleContextMenu = ( + event: React.MouseEvent<HTMLDivElement, MouseEvent> + ) => { + const { clientX, clientY } = event; + setPosition({ top: clientY, left: clientX }); + setAnchorEl(event.currentTarget); + event.preventDefault(); + }; - <IconButton + return ( + <> + <Wrapper + sx={({ palette }) => { + const { mode, primary, text, grey } = palette; + const key = `${mode}-${selected}`; + + const bgcolor = { + "light-true": alpha(primary.main, 0.15), + "light-false": palette.background.paper, + "dark-true": alpha(primary.main, 0.35), + "dark-false": alpha(grey[700], 0.35), + }[key]!; + + const color = { + "light-true": text.secondary, + "light-false": text.secondary, + "dark-true": alpha(text.secondary, 0.6), + "dark-false": alpha(text.secondary, 0.6), + }[key]!; + + const h2color = { + "light-true": primary.main, + "light-false": text.primary, + "dark-true": primary.light, + "dark-false": text.primary, + }[key]!; + + return { bgcolor, color, "& h2": { color: h2color } }; + }} + onClick={onClick} + onContextMenu={handleContextMenu} + > + <Box display="flex" justifyContent="space-between"> + <Typography + width="calc(100% - 40px)" + variant="h6" + component="h2" + noWrap + title={name} + > + {name} + </Typography> + + <IconButton + sx={{ + width: 30, + height: 30, + animation: loading ? `1s linear infinite ${round}` : "none", + }} + color="inherit" + disabled={loading} + onClick={(e) => { + e.stopPropagation(); + onUpdate(); + }} + > + <RefreshRounded /> + </IconButton> + </Box> + + <Box display="flex" justifyContent="space-between" alignItems="center"> + <Typography noWrap title={`From: ${from}`}> + {from} + </Typography> + + <Typography + noWrap + flex="1 0 auto" + fontSize={14} + textAlign="right" + title="updated time" + > + {fromnow} + </Typography> + </Box> + + <Box sx={{ - width: 30, - height: 30, - animation: loading ? `1s linear infinite ${round}` : "none", + my: 0.5, + fontSize: 14, + display: "flex", + justifyContent: "space-between", }} - color="inherit" - disabled={loading} - onClick={(e) => { - e.stopPropagation(); - onUpdate(); - }} - > - <RefreshRounded /> - </IconButton> - </Box> - - <Box display="flex" justifyContent="space-between" alignItems="center"> - <Typography noWrap title={`From: ${from}`}> - {from} - </Typography> - - <Typography - noWrap - flex="1 0 auto" - fontSize={14} - textAlign="right" - title="updated time" > - {fromnow} - </Typography> - </Box> - - <Box - sx={{ - my: 0.5, - fontSize: 14, - display: "flex", - justifyContent: "space-between", - }} + <span title="used / total"> + {parseTraffic(upload + download)} / {parseTraffic(total)} + </span> + <span title="expire time">{expire}</span> + </Box> + + <LinearProgress + variant="determinate" + value={progress} + color="inherit" + /> + </Wrapper> + + <Menu + open={!!anchorEl} + anchorEl={anchorEl} + onClose={() => setAnchorEl(null)} + anchorPosition={position} + anchorReference="anchorPosition" > - <span title="used / total"> - {parseTraffic(upload + download)} / {parseTraffic(total)} - </span> - <span title="expire time">{expire}</span> - </Box> - - <LinearProgress variant="determinate" value={progress} color="inherit" /> - </Wrapper> + <MenuItem onClick={onUpdate}>Update</MenuItem> + <MenuItem onClick={onDelete}>Delete</MenuItem> + {/* <MenuItem>Update(proxy)</MenuItem> */} + </Menu> + </> ); }; diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index e374509..adeef37 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -24,16 +24,20 @@ const ProfilePage = () => { if (profiles.current == null) return; if (!profiles.items) profiles.items = []; - const profile = profiles.items![profiles.current]; + const current = profiles.current; + const profile = profiles.items![current]; if (!profile) return; - getProxies().then((proxiesData) => { + setTimeout(async () => { + const proxiesData = await getProxies(); mutate("getProxies", proxiesData); + // init selected array const { selected = [] } = profile; const selectedMap = Object.fromEntries( selected.map((each) => [each.name!, each.now!]) ); + // todo: enhance error handle let hasChange = false; proxiesData.groups.forEach((group) => { @@ -52,10 +56,10 @@ const ProfilePage = () => { name, now, })); - patchProfile(profiles.current!, profile).catch(console.error); + patchProfile(current!, profile).catch(console.error); // update proxies cache if (hasChange) mutate("getProxies", getProxies()); - }); + }, 100); }, [profiles]); const onImport = async () => { -- GitLab