Skip to content

Commit

Permalink
feat: support clash meta memory usage display
Browse files Browse the repository at this point in the history
  • Loading branch information
zzzgydi committed Aug 5, 2023
1 parent 6f5acee commit 96ffbe2
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 57 deletions.
5 changes: 5 additions & 0 deletions src-tauri/src/config/verge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub struct IVerge {
/// enable traffic graph default is true
pub traffic_graph: Option<bool>,

/// show memory info (only for Clash Meta)
pub enable_memory_usage: Option<bool>,

/// clash tun mode
pub enable_tun_mode: Option<bool>,

Expand Down Expand Up @@ -125,6 +128,7 @@ impl IVerge {
theme_mode: Some("system".into()),
theme_blur: Some(false),
traffic_graph: Some(true),
enable_memory_usage: Some(true),
enable_auto_launch: Some(false),
enable_silent_start: Some(false),
enable_system_proxy: Some(false),
Expand Down Expand Up @@ -158,6 +162,7 @@ impl IVerge {
patch!(theme_mode);
patch!(theme_blur);
patch!(traffic_graph);
patch!(enable_memory_usage);

patch!(enable_tun_mode);
patch!(enable_service_mode);
Expand Down
83 changes: 63 additions & 20 deletions src/components/layout/layout-traffic.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useEffect, useRef, useState } from "react";
import { Box, Typography } from "@mui/material";
import { ArrowDownward, ArrowUpward } from "@mui/icons-material";
import {
ArrowDownward,
ArrowUpward,
MemoryOutlined,
} from "@mui/icons-material";
import { useClashInfo } from "@/hooks/use-clash";
import { useVerge } from "@/hooks/use-verge";
import { TrafficGraph, type TrafficRef } from "./traffic-graph";
Expand All @@ -12,13 +16,15 @@ import parseTraffic from "@/utils/parse-traffic";
// setup the traffic
const LayoutTraffic = () => {
const { clashInfo } = useClashInfo();
const { verge } = useVerge();

// whether hide traffic graph
const { verge } = useVerge();
const trafficGraph = verge?.traffic_graph ?? true;

const trafficRef = useRef<TrafficRef>(null);
const [traffic, setTraffic] = useState({ up: 0, down: 0 });
const [memory, setMemory] = useState({ inuse: 0 });
const pageVisible = useVisibility();

// setup log ws during layout
useLogSetup();
Expand All @@ -29,8 +35,6 @@ const LayoutTraffic = () => {
setTraffic(data);
});

const pageVisible = useVisibility();

useEffect(() => {
if (!clashInfo || !pageVisible) return;

Expand All @@ -42,14 +46,38 @@ const LayoutTraffic = () => {
};
}, [clashInfo, pageVisible]);

/* --------- meta memory information --------- */
const isMetaCore = verge?.clash_core === "clash-meta";
const displayMemory = isMetaCore && (verge?.enable_memory_usage ?? true);

const memoryWs = useWebsocket(
(event) => {
setMemory(JSON.parse(event.data));
},
{ onError: () => setMemory({ inuse: 0 }) }
);

useEffect(() => {
if (!clashInfo || !pageVisible || !displayMemory) return;
const { server = "", secret = "" } = clashInfo;
memoryWs.connect(
`ws://${server}/memory?token=${encodeURIComponent(secret)}`
);
return () => memoryWs.disconnect();
}, [clashInfo, pageVisible, displayMemory]);

const [up, upUnit] = parseTraffic(traffic.up);
const [down, downUnit] = parseTraffic(traffic.down);
const [inuse, inuseUnit] = parseTraffic(memory.inuse);

const iconStyle: any = {
sx: { mr: "8px", fontSize: 16 },
};
const valStyle: any = {
component: "span",
color: "primary",
textAlign: "center",
sx: { flex: "1 1 54px", userSelect: "none" },
sx: { flex: "1 1 56px", userSelect: "none" },
};
const unitStyle: any = {
component: "span",
Expand All @@ -71,22 +99,37 @@ const LayoutTraffic = () => {
</div>
)}

<Box mb={1.5} display="flex" alignItems="center" whiteSpace="nowrap">
<ArrowUpward
sx={{ mr: 0.75, fontSize: 18 }}
color={+up > 0 ? "primary" : "disabled"}
/>
<Typography {...valStyle}>{up}</Typography>
<Typography {...unitStyle}>{upUnit}/s</Typography>
</Box>
<Box display="flex" flexDirection="column" gap={0.75}>
<Box display="flex" alignItems="center" whiteSpace="nowrap">
<ArrowUpward
{...iconStyle}
color={+up > 0 ? "primary" : "disabled"}
/>
<Typography {...valStyle}>{up}</Typography>
<Typography {...unitStyle}>{upUnit}/s</Typography>
</Box>

<Box display="flex" alignItems="center" whiteSpace="nowrap">
<ArrowDownward
{...iconStyle}
color={+down > 0 ? "primary" : "disabled"}
/>
<Typography {...valStyle}>{down}</Typography>
<Typography {...unitStyle}>{downUnit}/s</Typography>
</Box>

<Box display="flex" alignItems="center" whiteSpace="nowrap">
<ArrowDownward
sx={{ mr: 0.75, fontSize: 18 }}
color={+down > 0 ? "primary" : "disabled"}
/>
<Typography {...valStyle}>{down}</Typography>
<Typography {...unitStyle}>{downUnit}/s</Typography>
{displayMemory && (
<Box
display="flex"
alignItems="center"
whiteSpace="nowrap"
title="Memory Usage"
>
<MemoryOutlined {...iconStyle} color="disabled" />
<Typography {...valStyle}>{inuse}</Typography>
<Typography {...unitStyle}>{inuseUnit}</Typography>
</Box>
)}
</Box>
</Box>
);
Expand Down
80 changes: 80 additions & 0 deletions src/components/setting/mods/layout-viewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";
import { List, Switch } from "@mui/material";
import { useVerge } from "@/hooks/use-verge";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { SettingItem } from "./setting-comp";
import { GuardState } from "./guard-state";

export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation();
const { verge, patchVerge, mutateVerge } = useVerge();

const [open, setOpen] = useState(false);

useImperativeHandle(ref, () => ({
open: () => setOpen(true),
close: () => setOpen(false),
}));

const onSwitchFormat = (_e: any, value: boolean) => value;
const onError = (err: any) => {
Notice.error(err.message || err.toString());
};
const onChangeData = (patch: Partial<IVergeConfig>) => {
mutateVerge({ ...verge, ...patch }, false);
};

return (
<BaseDialog
open={open}
title={t("Layout Setting")}
contentSx={{ width: 450 }}
disableOk
cancelBtn={t("Cancel")}
onClose={() => setOpen(false)}
onCancel={() => setOpen(false)}
>
<List>
<SettingItem label={t("Theme Blur")}>
<GuardState
value={verge?.theme_blur ?? false}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ theme_blur: e })}
onGuard={(e) => patchVerge({ theme_blur: e })}
>
<Switch edge="end" />
</GuardState>
</SettingItem>

<SettingItem label={t("Traffic Graph")}>
<GuardState
value={verge?.traffic_graph ?? true}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ traffic_graph: e })}
onGuard={(e) => patchVerge({ traffic_graph: e })}
>
<Switch edge="end" />
</GuardState>
</SettingItem>

<SettingItem label={t("Memory Usage")}>
<GuardState
value={verge?.enable_memory_usage ?? true}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ enable_memory_usage: e })}
onGuard={(e) => patchVerge({ enable_memory_usage: e })}
>
<Switch edge="end" />
</GuardState>
</SettingItem>
</List>
</BaseDialog>
);
});
5 changes: 3 additions & 2 deletions src/components/setting/mods/setting-comp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ interface ItemProps {
label: ReactNode;
extra?: ReactNode;
children?: ReactNode;
secondary?: ReactNode;
}

export const SettingItem: React.FC<ItemProps> = (props) => {
const { label, extra, children } = props;
const { label, extra, children, secondary } = props;

const primary = !extra ? (
label
Expand All @@ -27,7 +28,7 @@ export const SettingItem: React.FC<ItemProps> = (props) => {

return (
<ListItem sx={{ pt: "5px", pb: "5px" }}>
<ListItemText primary={primary} />
<ListItemText primary={primary} secondary={secondary} />
{children}
</ListItem>
);
Expand Down
50 changes: 16 additions & 34 deletions src/components/setting/setting-verge.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { useRef } from "react";
import { useTranslation } from "react-i18next";
import {
IconButton,
MenuItem,
Select,
Switch,
Typography,
} from "@mui/material";
import { IconButton, MenuItem, Select, Typography } from "@mui/material";
import { openAppDir, openCoreDir, openLogsDir } from "@/services/cmds";
import { ArrowForward } from "@mui/icons-material";
import { useVerge } from "@/hooks/use-verge";
Expand All @@ -19,6 +13,7 @@ import { HotkeyViewer } from "./mods/hotkey-viewer";
import { MiscViewer } from "./mods/misc-viewer";
import { ThemeViewer } from "./mods/theme-viewer";
import { GuardState } from "./mods/guard-state";
import { LayoutViewer } from "./mods/layout-viewer";

interface Props {
onError?: (err: Error) => void;
Expand All @@ -35,6 +30,7 @@ const SettingVerge = ({ onError }: Props) => {
const hotkeyRef = useRef<DialogRef>(null);
const miscRef = useRef<DialogRef>(null);
const themeRef = useRef<DialogRef>(null);
const layoutRef = useRef<DialogRef>(null);

const onSwitchFormat = (_e: any, value: boolean) => value;
const onChangeData = (patch: Partial<IVergeConfig>) => {
Expand All @@ -47,6 +43,7 @@ const SettingVerge = ({ onError }: Props) => {
<ConfigViewer ref={configRef} />
<HotkeyViewer ref={hotkeyRef} />
<MiscViewer ref={miscRef} />
<LayoutViewer ref={layoutRef} />

<SettingItem label={t("Language")}>
<GuardState
Expand Down Expand Up @@ -75,49 +72,34 @@ const SettingVerge = ({ onError }: Props) => {
</GuardState>
</SettingItem>

<SettingItem label={t("Theme Blur")}>
<GuardState
value={theme_blur ?? false}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ theme_blur: e })}
onGuard={(e) => patchVerge({ theme_blur: e })}
>
<Switch edge="end" />
</GuardState>
</SettingItem>

<SettingItem label={t("Traffic Graph")}>
<GuardState
value={traffic_graph ?? true}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={(e) => onChangeData({ traffic_graph: e })}
onGuard={(e) => patchVerge({ traffic_graph: e })}
<SettingItem label={t("Theme Setting")}>
<IconButton
color="inherit"
size="small"
sx={{ my: "2px" }}
onClick={() => themeRef.current?.open()}
>
<Switch edge="end" />
</GuardState>
<ArrowForward />
</IconButton>
</SettingItem>

<SettingItem label={t("Miscellaneous")}>
<SettingItem label={t("Layout Setting")}>
<IconButton
color="inherit"
size="small"
sx={{ my: "2px" }}
onClick={() => miscRef.current?.open()}
onClick={() => layoutRef.current?.open()}
>
<ArrowForward />
</IconButton>
</SettingItem>

<SettingItem label={t("Theme Setting")}>
<SettingItem label={t("Miscellaneous")}>
<IconButton
color="inherit"
size="small"
sx={{ my: "2px" }}
onClick={() => themeRef.current?.open()}
onClick={() => miscRef.current?.open()}
>
<ArrowForward />
</IconButton>
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/use-websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type WsMsgFn = (event: MessageEvent<any>) => void;
export interface WsOptions {
errorCount?: number; // default is 5
retryInterval?: number; // default is 2500
onError?: () => void;
}

export const useWebsocket = (onMessage: WsMsgFn, options?: WsOptions) => {
Expand Down Expand Up @@ -38,6 +39,9 @@ export const useWebsocket = (onMessage: WsMsgFn, options?: WsOptions) => {

if (errorCount >= 0) {
timerRef.current = setTimeout(connectHelper, 2500);
} else {
disconnect();
options?.onError?.();
}
});
};
Expand Down
Loading

0 comments on commit 96ffbe2

Please sign in to comment.