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
let running_mode = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async {
match CoreManager::global().get_running_mode().await {
crate::core::RunningMode::Service => "Service".to_string(),
crate::core::RunningMode::Sidecar => "Standalone".to_string(),
crate::core::RunningMode::NotRunning => "Not Running".to_string(),
}
let running_mode = CoreManager::global().get_running_mode().await;
running_mode.to_string()
})
});

View File

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

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

View File

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

View File

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

View File

@ -1,7 +1,5 @@
import dayjs from "dayjs";
import { invoke } from "@tauri-apps/api/core";
import { Notice } from "@/components/base";
import { IRunningMode } from "./types";
export async function copyClashEnv() {
return invoke<void>("copy_clash_env");
@ -313,7 +311,7 @@ export async function validateScriptFile(filePath: string) {
// 获取当前运行模式
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;
password: string;
}
export enum RunningMode {
Service = "Service",
Sidecar = "Sidecar",
NotRunning = "NotRunning",
}
export interface IRunningMode {
mode: RunningMode;
}