feat: comprehensive oxlint cleanup - remove unused code

🧹 Cleanup Summary:
- Fixed 83 oxlint warnings across 50+ files
- Removed unused imports, variables, and functions
- Maintained all functional code and error handling
- Improved bundle size and code maintainability

📝 Key Changes:
- Cleaned unused React hooks (useState, useEffect, useClashInfo)
- Removed unused Material-UI imports (useTheme, styled components)
- Deleted unused interfaces and type definitions
- Fixed spread operator usage and boolean casting
- Simplified catch parameters where appropriate

🎯 Files Modified:
- React components: home.tsx, settings, profiles, etc.
- Custom hooks: use-*.ts files
- Utility functions and type definitions
- Configuration files

 Result: 0 oxlint warnings (from 83 warnings)
🔧 All functionality preserved
📦 Reduced bundle size through dead code elimination
This commit is contained in:
Tunglies
2025-08-22 18:48:56 +08:00
parent 6a1fce69e0
commit 475a09bb54
53 changed files with 254 additions and 254 deletions

View File

@@ -62,6 +62,7 @@ export const BaseSearchBox = (props: SearchProps) => {
new RegExp(pattern);
return true;
} catch (e) {
console.warn("[BaseSearchBox] validateRegex error:", e);
return false;
}
};

View File

@@ -1,7 +1,6 @@
import dayjs from "dayjs";
import { useMemo, useState } from "react";
import { DataGrid, GridColDef, GridColumnResizeParams } from "@mui/x-data-grid";
import { useThemeMode } from "@/services/states";
import { truncateStr } from "@/utils/truncate-str";
import parseTraffic from "@/utils/parse-traffic";
import { t } from "i18next";
@@ -14,9 +13,6 @@ interface Props {
export const ConnectionTable = (props: Props) => {
const { connections, onShowDetail } = props;
const mode = useThemeMode();
const isDark = mode === "light" ? false : true;
const backgroundColor = isDark ? "#282A36" : "#ffffff";
const [columnVisible, setColumnVisible] = useState<
Partial<Record<keyof IConnectionsItem, boolean>>

View File

@@ -8,7 +8,7 @@ import {
useRef,
memo,
} from "react";
import { Box, useTheme, Tooltip, Paper, Typography } from "@mui/material";
import { Box, useTheme } from "@mui/material";
import { useTranslation } from "react-i18next";
import parseTraffic from "@/utils/parse-traffic";
import {
@@ -126,13 +126,6 @@ export const EnhancedCanvasTrafficGraph = memo(
[theme],
);
// 根据时间范围获取数据点数量
const getPointsForTimeRange = useCallback(
(minutes: TimeRange): number =>
Math.min(minutes * 60, GRAPH_CONFIG.maxPoints),
[],
);
// 更新显示数据(防抖处理)
const updateDisplayDataDebounced = useMemo(() => {
let timeoutId: number;
@@ -283,7 +276,6 @@ export const EnhancedCanvasTrafficGraph = memo(
};
const padding = GRAPH_CONFIG.padding;
const effectiveHeight = height - padding.top - padding.bottom;
// 强制显示三个刻度:底部、中间、顶部
const topY = padding.top + 10; // 避免与顶部时间范围按钮重叠

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useRef, useCallback, memo, useMemo } from "react";
import { useRef, useCallback, memo, useMemo } from "react";
import { useTranslation } from "react-i18next";
import {
Typography,
@@ -20,10 +20,8 @@ import {
import {
EnhancedCanvasTrafficGraph,
type EnhancedCanvasTrafficGraphRef,
type ITrafficItem,
} from "./enhanced-canvas-traffic-graph";
import { useVisibility } from "@/hooks/use-visibility";
import { useClashInfo } from "@/hooks/use-clash";
import { useVerge } from "@/hooks/use-verge";
import parseTraffic from "@/utils/parse-traffic";
import { isDebugEnabled, gc } from "@/services/cmds";
@@ -33,17 +31,6 @@ import { useTrafficDataEnhanced } from "@/hooks/use-traffic-monitor";
import { TrafficErrorBoundary } from "@/components/common/traffic-error-boundary";
import useSWR from "swr";
interface MemoryUsage {
inuse: number;
oslimit?: number;
}
interface TrafficStatData {
uploadTotal: number;
downloadTotal: number;
activeConnections: number;
}
interface StatCardProps {
icon: ReactNode;
title: string;
@@ -64,9 +51,6 @@ declare global {
}
}
// 控制更新频率
const CONNECTIONS_UPDATE_INTERVAL = 5000; // 5秒更新一次连接数据
// 统计卡片组件 - 使用memo优化
const CompactStatCard = memo(
({ icon, title, value, unit, color, onClick }: StatCardProps) => {
@@ -159,13 +143,12 @@ CompactStatCard.displayName = "CompactStatCard";
export const EnhancedTrafficStats = () => {
const { t } = useTranslation();
const theme = useTheme();
const { clashInfo } = useClashInfo();
const { verge } = useVerge();
const trafficRef = useRef<EnhancedCanvasTrafficGraphRef>(null);
const pageVisible = useVisibility();
// 使用AppDataProvider
const { connections, uptime } = useAppData();
const { connections } = useAppData();
// 使用增强版的统一流量数据Hook
const { traffic, memory, isLoading, isDataFresh, hasValidData } =
@@ -258,7 +241,7 @@ export const EnhancedTrafficStats = () => {
borderRadius: "4px",
}}
>
DEBUG: {!!trafficRef.current ? "图表已初始化" : "图表未初始化"}
DEBUG: {trafficRef.current ? "图表已初始化" : "图表未初始化"}
<br />
: {isDataFresh ? "active" : "inactive"}
<br />

View File

@@ -73,14 +73,6 @@ export interface HomeProfileCardProps {
onProfileUpdated?: () => void;
}
// 添加一个通用的截断样式
const truncateStyle = {
maxWidth: "calc(100% - 28px)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
};
// 提取独立组件减少主组件复杂度
const ProfileDetails = ({
current,

View File

@@ -32,7 +32,7 @@ export const SystemInfoCard = () => {
const { t } = useTranslation();
const { verge, patchVerge } = useVerge();
const navigate = useNavigate();
const { isAdminMode, isSidecarMode, mutateRunningMode } = useSystemState();
const { isAdminMode, isSidecarMode } = useSystemState();
const { installServiceAndRestartCore } = useServiceInstaller();
// 系统信息状态

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef } from "react";
import { Box, Typography } from "@mui/material";
import {
ArrowDownwardRounded,
@@ -16,11 +16,6 @@ import { useTrafficDataEnhanced } from "@/hooks/use-traffic-monitor";
import { LightweightTrafficErrorBoundary } from "@/components/common/traffic-error-boundary";
import useSWR from "swr";
interface MemoryUsage {
inuse: number;
oslimit?: number;
}
// setup the traffic
export const LayoutTraffic = () => {
const { data: isDebug } = useSWR(
@@ -46,8 +41,7 @@ export const LayoutTraffic = () => {
const pageVisible = useVisibility();
// 使用增强版的统一流量数据Hook
const { traffic, memory, isLoading, isDataFresh, hasValidData } =
useTrafficDataEnhanced();
const { traffic, memory } = useTrafficDataEnhanced();
// 启动流量服务
useEffect(() => {

View File

@@ -75,7 +75,7 @@ export const GroupsEditorViewer = (props: Props) => {
const [visualization, setVisualization] = useState(true);
const [match, setMatch] = useState(() => (_: string) => true);
const [interfaceNameList, setInterfaceNameList] = useState<string[]>([]);
const { control, watch, register, ...formIns } = useForm<IProxyGroupConfig>({
const { control, ...formIns } = useForm<IProxyGroupConfig>({
defaultValues: {
type: "select",
name: "",
@@ -196,6 +196,7 @@ export const GroupsEditorViewer = (props: Props) => {
),
);
} catch (e) {
console.warn("[GroupsEditorViewer] yaml.dump failed:", e);
// 防止异常导致UI卡死
}
};

View File

@@ -315,7 +315,7 @@ export const ProfileItem = (props: Props) => {
// 更新成功,刷新列表
showNotice("success", t("Update subscription successfully"));
mutate("getProfiles");
} catch (err: any) {
} catch {
// 更新完全失败(包括后端的回退尝试)
// 不需要做处理,后端会通过事件通知系统发送错误
} finally {

View File

@@ -47,11 +47,6 @@ export const ProfileMore = (props: Props) => {
}
});
const fnWrapper = (fn: () => void) => () => {
setAnchorEl(null);
return fn();
};
const hasError = !!logInfo.find((e) => e[0] === "exception");
const itemMenu = [

View File

@@ -47,7 +47,12 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
// file input
const fileDataRef = useRef<string | null>(null);
const { control, watch, register, ...formIns } = useForm<IProfileItem>({
const {
control,
watch,
register: _register,
...formIns
} = useForm<IProfileItem>({
defaultValues: {
type: "remote",
name: "",
@@ -144,7 +149,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
if (!form.uid) throw new Error("UID not found");
await patchProfile(form.uid, item);
}
} catch (err) {
} catch {
// 首次创建/更新失败,尝试使用自身代理
showNotice(
"info",
@@ -201,7 +206,9 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
setOpen(false);
fileDataRef.current = null;
setTimeout(() => formIns.reset(), 500);
} catch {}
} catch (e) {
console.warn("[ProfileViewer] handleClose error:", e);
}
};
const text = {

View File

@@ -154,6 +154,11 @@ export const ProxiesEditorViewer = (props: Props) => {
names.push(proxy.name);
}
} catch (err: any) {
console.warn(
"[ProxiesEditorViewer] parseUri failed for line:",
uri,
err?.message || err,
);
// 不阻塞主流程
}
}
@@ -212,6 +217,7 @@ export const ProxiesEditorViewer = (props: Props) => {
),
);
} catch (e) {
console.warn("[ProxiesEditorViewer] yaml.dump failed:", e);
// 防止异常导致UI卡死
}
};

View File

@@ -15,7 +15,6 @@ import {
LinearProgress,
alpha,
styled,
useTheme,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
@@ -61,7 +60,6 @@ const parseExpire = (expire?: number) => {
export const ProviderButton = () => {
const { t } = useTranslation();
const theme = useTheme();
const [open, setOpen] = useState(false);
const { proxyProviders, refreshProxy, refreshProxyProviders } = useAppData();
const [updating, setUpdating] = useState<Record<string, boolean>>({});
@@ -312,7 +310,7 @@ export const ProviderButton = () => {
<IconButton
size="small"
color="primary"
onClick={(e) => {
onClick={() => {
updateProvider(key);
}}
disabled={isUpdating}

View File

@@ -594,15 +594,3 @@ function throttle<T extends (...args: any[]) => any>(
}
};
}
// 保留防抖函数以兼容其他地方可能的使用
function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number,
): (...args: Parameters<T>) => void {
let timeout: ReturnType<typeof setTimeout> | null = null;
return (...args: Parameters<T>) => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}

View File

@@ -48,7 +48,7 @@ export const ProxyHead = (props: Props) => {
useEffect(() => {
delayManager.setUrl(
groupName,
testUrl || url || verge?.default_latency_test!,
testUrl || url || verge?.default_latency_test,
);
}, [groupName, testUrl, verge?.default_latency_test]);

View File

@@ -251,7 +251,7 @@ const Widget = styled(Box)(({ theme: { typography } }) => ({
const TypeBox = styled(Box, {
shouldForwardProp: (prop) => prop !== "component",
})<{ component?: React.ElementType }>(({ theme: { palette, typography } }) => ({
})<{ component?: React.ElementType }>(({ theme: { typography } }) => ({
display: "inline-block",
border: "1px solid #ccc",
borderColor: "text.secondary",

View File

@@ -14,7 +14,6 @@ import {
Divider,
alpha,
styled,
useTheme,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
@@ -47,7 +46,6 @@ const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
export const ProviderButton = () => {
const { t } = useTranslation();
const theme = useTheme();
const [open, setOpen] = useState(false);
const { ruleProviders, refreshRules, refreshRuleProviders } = useAppData();
const [updating, setUpdating] = useState<Record<string, boolean>>({});

View File

@@ -7,7 +7,6 @@ import {
} from "react";
import { useTranslation } from "react-i18next";
import { BaseDialog, DialogRef } from "@/components/base";
import getSystem from "@/utils/get-system";
import { BaseLoadingOverlay } from "@/components/base";
import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
@@ -33,8 +32,6 @@ export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
const [total, setTotal] = useState(0);
const [page, setPage] = useState(0);
const OS = getSystem();
useImperativeHandle(ref, () => ({
open: () => {
setOpen(true);

View File

@@ -150,22 +150,6 @@ export const ClashPortViewer = forwardRef<
await saveSettings({ clashConfig, vergeConfig });
});
// 优化的数字输入处理
const handleNumericChange =
(setter: (value: number) => void) =>
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value.replace(/\D+/, "");
if (value === "") {
setter(0);
return;
}
const num = parseInt(value, 10);
if (!isNaN(num) && num >= 0 && num <= 65535) {
setter(num);
}
};
return (
<BaseDialog
open={open}

View File

@@ -91,6 +91,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
setCopySuccess(type);
setTimeout(() => setCopySuccess(null));
} catch (err) {
console.warn("[ControllerViewer] copy to clipboard failed:", err);
showNotice("error", t("Failed to copy"));
}
},

View File

@@ -24,7 +24,7 @@ import getSystem from "@/utils/get-system";
import { invoke } from "@tauri-apps/api/core";
import { showNotice } from "@/services/noticeService";
const Item = styled(ListItem)(({ theme }) => ({
const Item = styled(ListItem)(() => ({
padding: "5px 2px",
"& textarea": {
lineHeight: 1.5,
@@ -88,7 +88,7 @@ const DEFAULT_DNS_CONFIG = {
export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation();
const { clash, mutateClash, patchClash } = useClash();
const { clash, mutateClash } = useClash();
const themeMode = useThemeMode();
const [open, setOpen] = useState(false);
@@ -325,7 +325,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
if (!parsedYaml) return;
updateValuesFromConfig(parsedYaml);
} catch (err: any) {
} catch {
showNotice("error", t("Invalid YAML format"));
}
};

View File

@@ -2,15 +2,7 @@ import { BaseDialog, Switch } from "@/components/base";
import { useClash } from "@/hooks/use-clash";
import { showNotice } from "@/services/noticeService";
import { Delete as DeleteIcon } from "@mui/icons-material";
import {
Box,
Button,
Divider,
List,
ListItem,
styled,
TextField,
} from "@mui/material";
import { Box, Button, Divider, List, ListItem, TextField } from "@mui/material";
import { useLockFn, useRequest } from "ahooks";
import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,4 +1,4 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";
import { BaseDialog, DialogRef } from "@/components/base";
import { getNetworkInterfacesInfo } from "@/services/cmds";

View File

@@ -122,16 +122,12 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
return "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,localhost,*.local,*.crashlytics.com,<local>";
};
const { data: clashConfig, mutate: mutateClash } = useSWR(
"getClashConfig",
getClashConfig,
{
revalidateOnFocus: false,
revalidateIfStale: true,
dedupingInterval: 1000,
errorRetryInterval: 5000,
},
);
const { data: clashConfig } = useSWR("getClashConfig", getClashConfig, {
revalidateOnFocus: false,
revalidateIfStale: true,
dedupingInterval: 1000,
errorRetryInterval: 5000,
});
const [prevMixedPort, setPrevMixedPort] = useState(
clashConfig?.["mixed-port"],
@@ -299,7 +295,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
const ipv6Regex =
/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
const hostnameRegex =
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/;
if (
!ipv4Regex.test(value.proxy_host) &&

View File

@@ -70,7 +70,7 @@ export const TunViewer = forwardRef<DialogRef>((props, ref) => {
await patchClash({ tun });
await mutateClash(
(old) => ({
...(old! || {}),
...old!,
tun,
}),
false,
@@ -118,7 +118,7 @@ export const TunViewer = forwardRef<DialogRef>((props, ref) => {
await patchClash({ tun });
await mutateClash(
(old) => ({
...(old! || {}),
...old!,
tun,
}),
false,

View File

@@ -143,7 +143,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
<Box sx={{ height: "calc(100% - 10px)", overflow: "auto" }}>
<ReactMarkdown
components={{
a: ({ node, ...props }) => {
a: ({ ...props }) => {
const { children } = props;
return (
<a {...props} target="_blank">

View File

@@ -1,7 +1,6 @@
import { DialogRef, Switch } from "@/components/base";
import { TooltipIcon } from "@/components/base/base-tooltip-icon";
import { useClash } from "@/hooks/use-clash";
import { useListen } from "@/hooks/use-listen";
import { useVerge } from "@/hooks/use-verge";
import { updateGeoData } from "@/services/cmds";
import { invoke_uwp_tool } from "@/services/cmds";
@@ -33,25 +32,22 @@ const SettingClash = ({ onError }: Props) => {
const { t } = useTranslation();
const { clash, version, mutateClash, patchClash } = useClash();
const { verge, mutateVerge, patchVerge } = useVerge();
const { verge, patchVerge } = useVerge();
const {
ipv6,
"allow-lan": allowLan,
"log-level": logLevel,
"unified-delay": unifiedDelay,
dns,
} = clash ?? {};
const { enable_random_port = false, verge_mixed_port } = verge ?? {};
const { verge_mixed_port } = verge ?? {};
// 独立跟踪DNS设置开关状态
const [dnsSettingsEnabled, setDnsSettingsEnabled] = useState(() => {
return verge?.enable_dns_settings ?? false;
});
const { addListener } = useListen();
const webRef = useRef<DialogRef>(null);
const portRef = useRef<DialogRef>(null);
const ctrlRef = useRef<DialogRef>(null);
@@ -62,10 +58,7 @@ const SettingClash = ({ onError }: Props) => {
const onSwitchFormat = (_e: any, value: boolean) => value;
const onChangeData = (patch: Partial<IConfigData>) => {
mutateClash((old) => ({ ...(old! || {}), ...patch }), false);
};
const onChangeVerge = (patch: Partial<IVergeConfig>) => {
mutateVerge({ ...verge, ...patch }, false);
mutateClash((old) => ({ ...old!, ...patch }), false);
};
const onUpdateGeo = async () => {
try {

View File

@@ -10,7 +10,6 @@ import {
exportDiagnosticInfo,
} from "@/services/cmds";
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { useVerge } from "@/hooks/use-verge";
import { version } from "@root/package.json";
import { DialogRef } from "@/components/base";
import { SettingList, SettingItem } from "./mods/setting-comp";
@@ -30,10 +29,9 @@ interface Props {
onError?: (err: Error) => void;
}
const SettingVergeAdvanced = ({ onError }: Props) => {
const SettingVergeAdvanced = ({ onError: _ }: Props) => {
const { t } = useTranslation();
const { verge, patchVerge, mutateVerge } = useVerge();
const configRef = useRef<DialogRef>(null);
const hotkeyRef = useRef<DialogRef>(null);
const miscRef = useRef<DialogRef>(null);

View File

@@ -25,7 +25,12 @@ export const TestViewer = forwardRef<TestViewerRef, Props>((props, ref) => {
const [loading, setLoading] = useState(false);
const { verge, patchVerge } = useVerge();
const testList = verge?.test_list ?? [];
const { control, watch, register, ...formIns } = useForm<IVergeTestItem>({
const {
control,
watch: _watch,
register: _register,
...formIns
} = useForm<IVergeTestItem>({
defaultValues: {
name: "",
icon: "",