fix: standardize RunningMode handling between frontend and backend

This commit improves the type consistency between the Rust backend and TypeScript frontend by:

1. Modifying the Rust `get_running_mode()` command to return a String instead of RunningMode enum directly
2. Removing the RunningMode enum and IRunningMode interface from TypeScript types
3. Using string literals for mode comparison in frontend components
4. Standardizing on capitalized mode names (e.g., "Sidecar" instead of "sidecar")

These changes ensure proper serialization/deserialization between backend and frontend,
making the code more maintainable and reducing potential inconsistencies.
This commit is contained in:
Tunglies 2025-03-26 22:04:16 +08:00
parent 6e40dd9862
commit 7ede91599c
7 changed files with 134 additions and 145 deletions

View File

@ -35,11 +35,8 @@ impl PlatformSpecification {
// Get running mode asynchronously // Get running mode asynchronously
let running_mode = tokio::task::block_in_place(|| { let running_mode = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async { tokio::runtime::Handle::current().block_on(async {
match CoreManager::global().get_running_mode().await { let running_mode = CoreManager::global().get_running_mode().await;
crate::core::RunningMode::Service => "Service".to_string(), running_mode.to_string()
crate::core::RunningMode::Sidecar => "Standalone".to_string(),
crate::core::RunningMode::NotRunning => "Not Running".to_string(),
}
}) })
}); });

View File

@ -32,71 +32,64 @@ interface TabButtonProps {
} }
// 抽取Tab组件以减少重复代码 // 抽取Tab组件以减少重复代码
const TabButton: FC<TabButtonProps> = memo(({ const TabButton: FC<TabButtonProps> = memo(
isActive, ({ isActive, onClick, icon: Icon, label, hasIndicator = false }) => (
onClick, <Paper
icon: Icon, elevation={isActive ? 2 : 0}
label, onClick={onClick}
hasIndicator = false sx={{
}) => ( cursor: "pointer",
<Paper px: 2,
elevation={isActive ? 2 : 0} py: 1,
onClick={onClick} display: "flex",
sx={{ alignItems: "center",
cursor: "pointer", justifyContent: "center",
px: 2, gap: 1,
py: 1, bgcolor: isActive ? "primary.main" : "background.paper",
display: "flex", color: isActive ? "primary.contrastText" : "text.primary",
alignItems: "center", borderRadius: 1.5,
justifyContent: "center", flex: 1,
gap: 1, maxWidth: 160,
bgcolor: isActive ? "primary.main" : "background.paper", transition: "all 0.2s ease-in-out",
color: isActive ? "primary.contrastText" : "text.primary", position: "relative",
borderRadius: 1.5, "&:hover": {
flex: 1, transform: "translateY(-1px)",
maxWidth: 160, boxShadow: 1,
transition: "all 0.2s ease-in-out", },
position: "relative", "&:after": isActive
"&:hover": { ? {
transform: "translateY(-1px)", content: '""',
boxShadow: 1, position: "absolute",
}, bottom: -9,
"&:after": isActive left: "50%",
? { width: 2,
content: '""', height: 9,
position: "absolute", bgcolor: "primary.main",
bottom: -9, transform: "translateX(-50%)",
left: "50%", }
width: 2, : {},
height: 9, }}
bgcolor: "primary.main",
transform: "translateX(-50%)",
}
: {},
}}
>
<Icon fontSize="small" />
<Typography
variant="body2"
sx={{ fontWeight: isActive ? 600 : 400 }}
> >
{label} <Icon fontSize="small" />
</Typography> <Typography variant="body2" sx={{ fontWeight: isActive ? 600 : 400 }}>
{hasIndicator && ( {label}
<Box </Typography>
sx={{ {hasIndicator && (
width: 8, <Box
height: 8, sx={{
borderRadius: "50%", width: 8,
bgcolor: isActive ? "#fff" : "success.main", height: 8,
position: "absolute", borderRadius: "50%",
top: 8, bgcolor: isActive ? "#fff" : "success.main",
right: 8, position: "absolute",
}} top: 8,
/> right: 8,
)} }}
</Paper> />
)); )}
</Paper>
),
);
interface TabDescriptionProps { interface TabDescriptionProps {
description: string; description: string;
@ -104,44 +97,46 @@ interface TabDescriptionProps {
} }
// 抽取描述文本组件 // 抽取描述文本组件
const TabDescription: FC<TabDescriptionProps> = memo(({ description, tooltipTitle }) => ( const TabDescription: FC<TabDescriptionProps> = memo(
<Fade in={true} timeout={200}> ({ description, tooltipTitle }) => (
<Typography <Fade in={true} timeout={200}>
variant="caption" <Typography
component="div" variant="caption"
sx={{ component="div"
width: "95%", sx={{
textAlign: "center", width: "95%",
color: "text.secondary", textAlign: "center",
p: 0.8, color: "text.secondary",
borderRadius: 1, p: 0.8,
borderColor: "primary.main", borderRadius: 1,
borderWidth: 1, borderColor: "primary.main",
borderStyle: "solid", borderWidth: 1,
backgroundColor: "background.paper", borderStyle: "solid",
display: "flex", backgroundColor: "background.paper",
alignItems: "center", display: "flex",
justifyContent: "center", alignItems: "center",
gap: 0.5, justifyContent: "center",
wordBreak: "break-word", gap: 0.5,
hyphens: "auto", wordBreak: "break-word",
}} hyphens: "auto",
> }}
{description} >
<Tooltip title={tooltipTitle}> {description}
<HelpOutlineRounded <Tooltip title={tooltipTitle}>
sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }} <HelpOutlineRounded
/> sx={{ fontSize: 14, opacity: 0.7, flexShrink: 0 }}
</Tooltip> />
</Typography> </Tooltip>
</Fade> </Typography>
)); </Fade>
),
);
export const ProxyTunCard: FC = () => { export const ProxyTunCard: FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
const [activeTab, setActiveTab] = useState<string>(() => const [activeTab, setActiveTab] = useState<string>(
localStorage.getItem(LOCAL_STORAGE_TAB_KEY) || "system" () => localStorage.getItem(LOCAL_STORAGE_TAB_KEY) || "system",
); );
// 获取代理状态信息 // 获取代理状态信息
@ -152,7 +147,7 @@ export const ProxyTunCard: FC = () => {
const { enable_system_proxy, enable_tun_mode } = verge ?? {}; const { enable_system_proxy, enable_tun_mode } = verge ?? {};
// 是否以sidecar模式运行 // 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar"; const isSidecarMode = runningMode === "Sidecar";
// 处理错误 // 处理错误
const handleError = (err: Error) => { const handleError = (err: Error) => {
@ -172,7 +167,7 @@ export const ProxyTunCard: FC = () => {
text: enable_system_proxy text: enable_system_proxy
? t("System Proxy Enabled") ? t("System Proxy Enabled")
: t("System Proxy Disabled"), : t("System Proxy Disabled"),
tooltip: t("System Proxy Info") tooltip: t("System Proxy Info"),
}; };
} else { } else {
return { return {
@ -181,7 +176,7 @@ export const ProxyTunCard: FC = () => {
: enable_tun_mode : enable_tun_mode
? t("TUN Mode Enabled") ? t("TUN Mode Enabled")
: t("TUN Mode Disabled"), : t("TUN Mode Disabled"),
tooltip: t("TUN Mode Intercept Info") tooltip: t("TUN Mode Intercept Info"),
}; };
} }
}, [activeTab, enable_system_proxy, enable_tun_mode, isSidecarMode, t]); }, [activeTab, enable_system_proxy, enable_tun_mode, isSidecarMode, t]);

View File

@ -24,14 +24,14 @@ export const SystemInfoCard = () => {
}); });
// 获取运行模式 // 获取运行模式
const { data: runningMode = "sidecar", mutate: mutateRunningMode } = useSWR( const { data: runningMode = "Sidecar", mutate: mutateRunningMode } = useSWR(
"getRunningMode", "getRunningMode",
getRunningMode, getRunningMode,
{ suspense: false, revalidateOnFocus: false } { suspense: false, revalidateOnFocus: false },
); );
// 是否以sidecar模式运行 // 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar"; const isSidecarMode = runningMode === "Sidecar";
// 初始化系统信息 // 初始化系统信息
useEffect(() => { useEffect(() => {
@ -42,7 +42,10 @@ export const SystemInfoCard = () => {
if (lines.length > 0) { if (lines.length > 0) {
const sysName = lines[0].split(": ")[1] || ""; const sysName = lines[0].split(": ")[1] || "";
const sysVersion = lines[1].split(": ")[1] || ""; const sysVersion = lines[1].split(": ")[1] || "";
setSystemState(prev => ({ ...prev, osInfo: `${sysName} ${sysVersion}` })); setSystemState((prev) => ({
...prev,
osInfo: `${sysName} ${sysVersion}`,
}));
} }
}) })
.catch(console.error); .catch(console.error);
@ -53,9 +56,9 @@ export const SystemInfoCard = () => {
try { try {
const timestamp = parseInt(lastCheck, 10); const timestamp = parseInt(lastCheck, 10);
if (!isNaN(timestamp)) { if (!isNaN(timestamp)) {
setSystemState(prev => ({ setSystemState((prev) => ({
...prev, ...prev,
lastCheckUpdate: new Date(timestamp).toLocaleString() lastCheckUpdate: new Date(timestamp).toLocaleString(),
})); }));
} }
} catch (e) { } catch (e) {
@ -65,11 +68,11 @@ export const SystemInfoCard = () => {
// 如果启用了自动检查更新但没有记录,设置当前时间并延迟检查 // 如果启用了自动检查更新但没有记录,设置当前时间并延迟检查
const now = Date.now(); const now = Date.now();
localStorage.setItem("last_check_update", now.toString()); localStorage.setItem("last_check_update", now.toString());
setSystemState(prev => ({ setSystemState((prev) => ({
...prev, ...prev,
lastCheckUpdate: new Date(now).toLocaleString() lastCheckUpdate: new Date(now).toLocaleString(),
})); }));
setTimeout(() => { setTimeout(() => {
if (verge?.auto_check_update) { if (verge?.auto_check_update) {
checkUpdate().catch(console.error); checkUpdate().catch(console.error);
@ -84,9 +87,9 @@ export const SystemInfoCard = () => {
async () => { async () => {
const now = Date.now(); const now = Date.now();
localStorage.setItem("last_check_update", now.toString()); localStorage.setItem("last_check_update", now.toString());
setSystemState(prev => ({ setSystemState((prev) => ({
...prev, ...prev,
lastCheckUpdate: new Date(now).toLocaleString() lastCheckUpdate: new Date(now).toLocaleString(),
})); }));
return await checkUpdate(); return await checkUpdate();
}, },
@ -94,7 +97,7 @@ export const SystemInfoCard = () => {
revalidateOnFocus: false, revalidateOnFocus: false,
refreshInterval: 24 * 60 * 60 * 1000, // 每天检查一次 refreshInterval: 24 * 60 * 60 * 1000, // 每天检查一次
dedupingInterval: 60 * 60 * 1000, // 1小时内不重复检查 dedupingInterval: 60 * 60 * 1000, // 1小时内不重复检查
} },
); );
// 导航到设置页面 // 导航到设置页面
@ -147,16 +150,22 @@ export const SystemInfoCard = () => {
}); });
// 是否启用自启动 // 是否启用自启动
const autoLaunchEnabled = useMemo(() => verge?.enable_auto_launch || false, [verge]); const autoLaunchEnabled = useMemo(
() => verge?.enable_auto_launch || false,
[verge],
);
// 运行模式样式 // 运行模式样式
const runningModeStyle = useMemo(() => ({ const runningModeStyle = useMemo(
cursor: isSidecarMode ? "pointer" : "default", () => ({
textDecoration: isSidecarMode ? "underline" : "none", cursor: isSidecarMode ? "pointer" : "default",
"&:hover": { textDecoration: isSidecarMode ? "underline" : "none",
opacity: isSidecarMode ? 0.7 : 1, "&:hover": {
}, opacity: isSidecarMode ? 0.7 : 1,
}), [isSidecarMode]); },
}),
[isSidecarMode],
);
// 只有当verge存在时才渲染内容 // 只有当verge存在时才渲染内容
if (!verge) return null; if (!verge) return null;

View File

@ -58,7 +58,7 @@ const SettingSystem = ({ onError }: Props) => {
}, [autoLaunchEnabled]); }, [autoLaunchEnabled]);
// 是否以sidecar模式运行 // 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar"; const isSidecarMode = runningMode === "Sidecar";
const sysproxyRef = useRef<DialogRef>(null); const sysproxyRef = useRef<DialogRef>(null);
const tunRef = useRef<DialogRef>(null); const tunRef = useRef<DialogRef>(null);

View File

@ -52,7 +52,7 @@ const ProxyControlSwitches = ({ label, onError }: ProxySwitchProps) => {
); );
// 是否以sidecar模式运行 // 是否以sidecar模式运行
const isSidecarMode = runningMode === "sidecar"; const isSidecarMode = runningMode === "Sidecar";
const sysproxyRef = useRef<DialogRef>(null); const sysproxyRef = useRef<DialogRef>(null);
const tunRef = useRef<DialogRef>(null); const tunRef = useRef<DialogRef>(null);
@ -138,7 +138,7 @@ const ProxyControlSwitches = ({ label, onError }: ProxySwitchProps) => {
{t("System Proxy")} {t("System Proxy")}
</Typography> </Typography>
{/* <Typography variant="caption" color="text.secondary"> {/* <Typography variant="caption" color="text.secondary">
{sysproxy?.enable {sysproxy?.enable
? t("Proxy is active") ? t("Proxy is active")
: t("Enable this for most users") : t("Enable this for most users")
} }

View File

@ -1,7 +1,5 @@
import dayjs from "dayjs";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { IRunningMode } from "./types";
export async function copyClashEnv() { export async function copyClashEnv() {
return invoke<void>("copy_clash_env"); return invoke<void>("copy_clash_env");
@ -313,7 +311,7 @@ export async function validateScriptFile(filePath: string) {
// 获取当前运行模式 // 获取当前运行模式
export const getRunningMode = async () => { export const getRunningMode = async () => {
return invoke<IRunningMode>("get_running_mode"); return invoke<string>("get_running_mode");
}; };
// 获取应用运行时间 // 获取应用运行时间

View File

@ -806,13 +806,3 @@ interface IWebDavConfig {
username: string; username: string;
password: string; password: string;
} }
export enum RunningMode {
Service = "Service",
Sidecar = "Sidecar",
NotRunning = "NotRunning",
}
export interface IRunningMode {
mode: RunningMode;
}