Revert "Refactor components to remove forwardRef and simplify props handling"

This reverts commit 1cd013fb94.
This commit is contained in:
Tunglies
2025-09-30 18:13:02 +08:00
parent 14288568bf
commit 8a4f2de887
25 changed files with 1474 additions and 1399 deletions

View File

@@ -2,7 +2,7 @@ import { Box, Button, Snackbar, useTheme } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { t } from "i18next"; import { t } from "i18next";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { deleteConnection } from "@/services/cmds"; import { deleteConnection } from "@/services/cmds";
import parseTraffic from "@/utils/parse-traffic"; import parseTraffic from "@/utils/parse-traffic";
@@ -11,7 +11,8 @@ export interface ConnectionDetailRef {
open: (detail: IConnectionsItem) => void; open: (detail: IConnectionsItem) => void;
} }
export const ConnectionDetail = ({ ref, ...props }) => { export const ConnectionDetail = forwardRef<ConnectionDetailRef>(
(props, ref) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [detail, setDetail] = useState<IConnectionsItem>(null!); const [detail, setDetail] = useState<IConnectionsItem>(null!);
const theme = useTheme(); const theme = useTheme();
@@ -47,7 +48,8 @@ export const ConnectionDetail = ({ ref, ...props }) => {
} }
/> />
); );
}; },
);
interface InnerProps { interface InnerProps {
data: IConnectionsItem; data: IConnectionsItem;

View File

@@ -1,5 +1,6 @@
import { Box, useTheme } from "@mui/material"; import { Box, useTheme } from "@mui/material";
import { import {
forwardRef,
useImperativeHandle, useImperativeHandle,
useState, useState,
useEffect, useEffect,
@@ -80,7 +81,8 @@ const GRAPH_CONFIG = {
* 稳定版Canvas流量图表组件 * 稳定版Canvas流量图表组件
* 修复闪烁问题,添加时间轴显示 * 修复闪烁问题,添加时间轴显示
*/ */
export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => { export const EnhancedCanvasTrafficGraph = memo(
forwardRef<EnhancedCanvasTrafficGraphRef>((props, ref) => {
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -140,7 +142,12 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
useEffect(() => { useEffect(() => {
const timeRangeData = getDataForTimeRange(timeRange); const timeRangeData = getDataForTimeRange(timeRange);
updateDisplayDataDebounced(timeRangeData); updateDisplayDataDebounced(timeRangeData);
}, [dataPoints, timeRange, getDataForTimeRange, updateDisplayDataDebounced]); }, [
dataPoints,
timeRange,
getDataForTimeRange,
updateDisplayDataDebounced,
]);
// Y轴坐标计算 - 基于刻度范围的线性映射 // Y轴坐标计算 - 基于刻度范围的线性映射
const calculateY = useCallback( const calculateY = useCallback(
@@ -152,7 +159,10 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
if (data.length === 0) return bottomY; if (data.length === 0) return bottomY;
// 获取当前的刻度范围 // 获取当前的刻度范围
const allValues = [...data.map((d) => d.up), ...data.map((d) => d.down)]; const allValues = [
...data.map((d) => d.up),
...data.map((d) => d.down),
];
const maxValue = Math.max(...allValues); const maxValue = Math.max(...allValues);
const minValue = Math.min(...allValues); const minValue = Math.min(...allValues);
@@ -221,7 +231,9 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
const upY = calculateY(dataPoint.up, rect.height, displayData); const upY = calculateY(dataPoint.up, rect.height, displayData);
const downY = calculateY(dataPoint.down, rect.height, displayData); const downY = calculateY(dataPoint.down, rect.height, displayData);
const highlightY = const highlightY =
Math.max(dataPoint.up, dataPoint.down) === dataPoint.up ? upY : downY; Math.max(dataPoint.up, dataPoint.down) === dataPoint.up
? upY
: downY;
setTooltipData({ setTooltipData({
x: mouseX, x: mouseX,
@@ -249,7 +261,10 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
if (data.length === 0) return []; if (data.length === 0) return [];
// 找到数据的最大值和最小值 // 找到数据的最大值和最小值
const allValues = [...data.map((d) => d.up), ...data.map((d) => d.down)]; const allValues = [
...data.map((d) => d.up),
...data.map((d) => d.down),
];
const maxValue = Math.max(...allValues); const maxValue = Math.max(...allValues);
const minValue = Math.min(...allValues); const minValue = Math.min(...allValues);
@@ -382,7 +397,8 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
); );
// 获取时间范围对应的最佳时间显示策略 // 获取时间范围对应的最佳时间显示策略
const getTimeDisplayStrategy = useCallback((timeRangeMinutes: TimeRange) => { const getTimeDisplayStrategy = useCallback(
(timeRangeMinutes: TimeRange) => {
switch (timeRangeMinutes) { switch (timeRangeMinutes) {
case 1: // 1分钟更密集的时间标签显示 MM:SS case 1: // 1分钟更密集的时间标签显示 MM:SS
return { return {
@@ -426,7 +442,9 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
minPixelDistance: 40, // 减少间距,允许更多标签 minPixelDistance: 40, // 减少间距,允许更多标签
}; };
} }
}, []); },
[],
);
// 绘制时间轴 // 绘制时间轴
const drawTimeAxis = useCallback( const drawTimeAxis = useCallback(
@@ -462,7 +480,8 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
); );
// 收集要显示的时间点 // 收集要显示的时间点
const timePoints: Array<{ index: number; x: number; label: string }> = []; const timePoints: Array<{ index: number; x: number; label: string }> =
[];
// 添加第一个时间点 // 添加第一个时间点
if (data.length > 0 && data[0].timestamp) { if (data.length > 0 && data[0].timestamp) {
@@ -474,7 +493,11 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
} }
// 添加中间的时间点 // 添加中间的时间点
for (let i = actualStep; i < data.length - actualStep; i += actualStep) { for (
let i = actualStep;
i < data.length - actualStep;
i += actualStep
) {
const point = data[i]; const point = data[i];
if (!point.timestamp) continue; if (!point.timestamp) continue;
@@ -703,7 +726,15 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
); );
// 绘制上传线(前景层) // 绘制上传线(前景层)
drawTrafficLine(ctx, upValues, width, height, colors.up, true, displayData); drawTrafficLine(
ctx,
upValues,
width,
height,
colors.up,
true,
displayData,
);
// 绘制悬浮高亮线 // 绘制悬浮高亮线
if (tooltipData.visible && tooltipData.dataIndex >= 0) { if (tooltipData.visible && tooltipData.dataIndex >= 0) {
@@ -968,6 +999,7 @@ export const EnhancedCanvasTrafficGraph = memo(({ ref, ...props }) => {
</Box> </Box>
</Box> </Box>
); );
}); }),
);
EnhancedCanvasTrafficGraph.displayName = "EnhancedCanvasTrafficGraph"; EnhancedCanvasTrafficGraph.displayName = "EnhancedCanvasTrafficGraph";

View File

@@ -1,5 +1,5 @@
import { useTheme } from "@mui/material"; import { useTheme } from "@mui/material";
import { useEffect, useImperativeHandle, useRef } from "react"; import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
const maxPoint = 30; const maxPoint = 30;
@@ -24,7 +24,7 @@ export interface TrafficRef {
/** /**
* draw the traffic graph * draw the traffic graph
*/ */
export const TrafficGraph = ({ ref, ...props }) => { export const TrafficGraph = forwardRef<TrafficRef>((props, ref) => {
const countRef = useRef(0); const countRef = useRef(0);
const styleRef = useRef(true); const styleRef = useRef(true);
const listRef = useRef<TrafficData[]>(defaultList); const listRef = useRef<TrafficData[]>(defaultList);
@@ -196,4 +196,4 @@ export const TrafficGraph = ({ ref, ...props }) => {
}, [palette]); }, [palette]);
return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />; return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
}; });

View File

@@ -9,7 +9,13 @@ import {
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useEffect, useImperativeHandle, useRef, useState } from "react"; import {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -32,10 +38,8 @@ export interface ProfileViewerRef {
// create or edit the profile // create or edit the profile
// remote / local // remote / local
export const ProfileViewer = ({ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
ref, (props, ref) => {
...props
}: Props & { ref?: React.RefObject<ProfileViewerRef | null> }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [openType, setOpenType] = useState<"new" | "edit">("new"); const [openType, setOpenType] = useState<"new" | "edit">("new");
@@ -120,7 +124,8 @@ export const ProfileViewer = ({
const isUpdate = openType === "edit"; const isUpdate = openType === "edit";
// 判断是否是当前激活的配置 // 判断是否是当前激活的配置
const isActivating = isUpdate && form.uid === (profiles?.current ?? ""); const isActivating =
isUpdate && form.uid === (profiles?.current ?? "");
// 保存原始代理设置以便回退成功后恢复 // 保存原始代理设置以便回退成功后恢复
const originalOptions = { const originalOptions = {
@@ -329,7 +334,9 @@ export const ProfileViewer = ({
slotProps={{ slotProps={{
input: { input: {
endAdornment: ( endAdornment: (
<InputAdornment position="end">{t("mins")}</InputAdornment> <InputAdornment position="end">
{t("mins")}
</InputAdornment>
), ),
}, },
}} }}
@@ -385,7 +392,8 @@ export const ProfileViewer = ({
)} )}
</BaseDialog> </BaseDialog>
); );
}; },
);
const StyledBox = styled(Box)(() => ({ const StyledBox = styled(Box)(() => ({
margin: "8px 0 8px 8px", margin: "8px 0 8px 8px",

View File

@@ -1,19 +1,23 @@
import { ExpandMoreRounded } from "@mui/icons-material";
import { import {
Box, Box,
Snackbar, Snackbar,
Alert, Alert,
Chip, Chip,
Stack,
Typography, Typography,
IconButton, IconButton,
Collapse,
Menu, Menu,
MenuItem, MenuItem,
Divider,
Button,
} from "@mui/material"; } from "@mui/material";
import { ArchiveOutlined, ExpandMoreRounded } from "@mui/icons-material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useRef, useState, useEffect, useCallback, useMemo } from "react"; import { useRef, useState, useEffect, useCallback, useMemo } from "react";
import useSWR from "swr";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"; import { Virtuoso, type VirtuosoHandle } from "react-virtuoso";
import useSWR from "swr";
import { useProxySelection } from "@/hooks/use-proxy-selection"; import { useProxySelection } from "@/hooks/use-proxy-selection";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
@@ -30,8 +34,8 @@ import { BaseEmpty } from "../base";
import { ScrollTopButton } from "../layout/scroll-top-button"; import { ScrollTopButton } from "../layout/scroll-top-button";
import { ProxyChain } from "./proxy-chain"; import { ProxyChain } from "./proxy-chain";
import { ProxyGroupNavigator } from "./proxy-group-navigator";
import { ProxyRender } from "./proxy-render"; import { ProxyRender } from "./proxy-render";
import { ProxyGroupNavigator } from "./proxy-group-navigator";
import { useRenderList } from "./use-render-list"; import { useRenderList } from "./use-render-list";
interface Props { interface Props {

View File

@@ -1,10 +1,16 @@
import { Box, Paper, Divider } from "@mui/material"; import { Box, Paper, Divider } from "@mui/material";
import dayjs from "dayjs"; import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat"; import customParseFormat from "dayjs/plugin/customParseFormat";
import { useImperativeHandle, useState, useCallback, useMemo } from "react"; import {
forwardRef,
useImperativeHandle,
useState,
useCallback,
useMemo,
} from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { BaseLoadingOverlay } from "@/components/base"; import { BaseLoadingOverlay } from "@/components/base";
import { listWebDavBackup } from "@/services/cmds"; import { listWebDavBackup } from "@/services/cmds";
@@ -19,7 +25,7 @@ dayjs.extend(customParseFormat);
const DATE_FORMAT = "YYYY-MM-DD_HH-mm-ss"; const DATE_FORMAT = "YYYY-MM-DD_HH-mm-ss";
const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/; const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/;
export const BackupViewer = ({ ref, ...props }) => { export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -125,4 +131,4 @@ export const BackupViewer = ({ ref, ...props }) => {
</Box> </Box>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -12,11 +12,11 @@ import {
ListItemText, ListItemText,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { mutate } from "swr"; import { mutate } from "swr";
import { BaseDialog } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { changeClashCore, restartCore } from "@/services/cmds"; import { changeClashCore, restartCore } from "@/services/cmds";
import { import {
@@ -31,7 +31,7 @@ const VALID_CORE = [
{ name: "Mihomo Alpha", core: "verge-mihomo-alpha", chip: "Alpha Version" }, { name: "Mihomo Alpha", core: "verge-mihomo-alpha", chip: "Alpha Version" },
]; ];
export const ClashCoreViewer = ({ ref, ...props }) => { export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { verge, mutateVerge } = useVerge(); const { verge, mutateVerge } = useVerge();
@@ -169,4 +169,4 @@ export const ClashCoreViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -9,7 +9,7 @@ import {
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { useLockFn, useRequest } from "ahooks"; import { useLockFn, useRequest } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, Switch } from "@/components/base";
@@ -30,12 +30,10 @@ interface ClashPortViewerRef {
const generateRandomPort = () => const generateRandomPort = () =>
Math.floor(Math.random() * (65535 - 1025 + 1)) + 1025; Math.floor(Math.random() * (65535 - 1025 + 1)) + 1025;
export const ClashPortViewer = ({ export const ClashPortViewer = forwardRef<
ref, ClashPortViewerRef,
...props ClashPortViewerProps
}: ClashPortViewerProps & { >((props, ref) => {
ref?: React.RefObject<ClashPortViewerRef | null>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { clashInfo, patchInfo } = useClashInfo(); const { clashInfo, patchInfo } = useClashInfo();
const { verge, patchVerge } = useVerge(); const { verge, patchVerge } = useVerge();
@@ -350,4 +348,4 @@ export const ClashPortViewer = ({
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -1,11 +1,12 @@
import { Box, Chip } from "@mui/material"; import { Box, Chip } from "@mui/material";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { DialogRef } from "@/components/base";
import { EditorViewer } from "@/components/profile/editor-viewer"; import { EditorViewer } from "@/components/profile/editor-viewer";
import { getRuntimeYaml } from "@/services/cmds"; import { getRuntimeYaml } from "@/services/cmds";
export const ConfigViewer = ({ ref, ..._ }) => { export const ConfigViewer = forwardRef<DialogRef>((_, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [runtimeConfig, setRuntimeConfig] = useState(""); const [runtimeConfig, setRuntimeConfig] = useState("");
@@ -37,4 +38,4 @@ export const ConfigViewer = ({ ref, ..._ }) => {
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
/> />
); );
}; });

View File

@@ -12,15 +12,15 @@ import {
Tooltip, Tooltip,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { useClashInfo } from "@/hooks/use-clash"; import { useClashInfo } from "@/hooks/use-clash";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
export const ControllerViewer = ({ ref, ...props }) => { export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [copySuccess, setCopySuccess] = useState<null | string>(null); const [copySuccess, setCopySuccess] = useState<null | string>(null);
@@ -217,4 +217,4 @@ export const ControllerViewer = ({ ref, ...props }) => {
</Snackbar> </Snackbar>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -15,11 +15,11 @@ import {
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import yaml from "js-yaml"; import yaml from "js-yaml";
import { useImperativeHandle, useState, useEffect } from "react"; import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import MonacoEditor from "react-monaco-editor"; import MonacoEditor from "react-monaco-editor";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { useClash } from "@/hooks/use-clash"; import { useClash } from "@/hooks/use-clash";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
import { useThemeMode } from "@/services/states"; import { useThemeMode } from "@/services/states";
@@ -87,7 +87,7 @@ const DEFAULT_DNS_CONFIG = {
}, },
}; };
export const DnsViewer = ({ ref, ...props }) => { export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { clash, mutateClash } = useClash(); const { clash, mutateClash } = useClash();
const themeMode = useThemeMode(); const themeMode = useThemeMode();
@@ -1034,4 +1034,4 @@ export const DnsViewer = ({ ref, ...props }) => {
)} )}
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -1,7 +1,7 @@
import { Delete as DeleteIcon } from "@mui/icons-material"; import { Delete as DeleteIcon } from "@mui/icons-material";
import { Box, Button, Divider, List, ListItem, TextField } from "@mui/material"; import { Box, Button, Divider, List, ListItem, TextField } from "@mui/material";
import { useLockFn, useRequest } from "ahooks"; import { useLockFn, useRequest } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, Switch } from "@/components/base";
@@ -71,7 +71,8 @@ interface ClashHeaderConfigingRef {
close: () => void; close: () => void;
} }
export const HeaderConfiguration = ({ ref, ...props }) => { export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
(props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { clash, mutateClash, patchClash } = useClash(); const { clash, mutateClash, patchClash } = useClash();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -190,7 +191,10 @@ export const HeaderConfiguration = ({ ref, ...props }) => {
edge="end" edge="end"
checked={corsConfig.allowPrivateNetwork} checked={corsConfig.allowPrivateNetwork}
onChange={(e) => onChange={(e) =>
handleCorsConfigChange("allowPrivateNetwork", e.target.checked) handleCorsConfigChange(
"allowPrivateNetwork",
e.target.checked,
)
} }
/> />
</Box> </Box>
@@ -250,7 +254,9 @@ export const HeaderConfiguration = ({ ref, ...props }) => {
borderRadius: 4, borderRadius: 4,
}} }}
> >
<div style={{ color: "#666", fontSize: 12, fontStyle: "italic" }}> <div
style={{ color: "#666", fontSize: 12, fontStyle: "italic" }}
>
{t("Always included origins: {{urls}}", { {t("Always included origins: {{urls}}", {
urls: DEV_URLS.join(", "), urls: DEV_URLS.join(", "),
})} })}
@@ -261,4 +267,5 @@ export const HeaderConfiguration = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; },
);

View File

@@ -1,9 +1,9 @@
import { styled, Typography } from "@mui/material"; import { styled, Typography } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
@@ -26,7 +26,7 @@ const HOTKEY_FUNC = [
"entry_lightweight_mode", "entry_lightweight_mode",
]; ];
export const HotkeyViewer = ({ ref, ...props }) => { export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -117,4 +117,4 @@ export const HotkeyViewer = ({ ref, ...props }) => {
))} ))}
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -12,10 +12,10 @@ import { convertFileSrc } from "@tauri-apps/api/core";
import { join } from "@tauri-apps/api/path"; import { join } from "@tauri-apps/api/path";
import { open as openDialog } from "@tauri-apps/plugin-dialog"; import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { exists } from "@tauri-apps/plugin-fs"; import { exists } from "@tauri-apps/plugin-fs";
import { useEffect, useImperativeHandle, useState } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { TooltipIcon } from "@/components/base/base-tooltip-icon";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { copyIconFile, getAppDir } from "@/services/cmds"; import { copyIconFile, getAppDir } from "@/services/cmds";
@@ -38,7 +38,7 @@ const getIcons = async (icon_dir: string, name: string) => {
}; };
}; };
export const LayoutViewer = ({ ref, ...props }) => { export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { verge, patchVerge, mutateVerge } = useVerge(); const { verge, patchVerge, mutateVerge } = useVerge();
@@ -387,7 +387,7 @@ export const LayoutViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });
const Item = styled(ListItem)(() => ({ const Item = styled(ListItem)(() => ({
padding: "5px 2px", padding: "5px 2px",

View File

@@ -7,16 +7,16 @@ import {
InputAdornment, InputAdornment,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { TooltipIcon } from "@/components/base/base-tooltip-icon";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { entry_lightweight_mode } from "@/services/cmds"; import { entry_lightweight_mode } from "@/services/cmds";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
export const LiteModeViewer = ({ ref, ...props }) => { export const LiteModeViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { verge, patchVerge } = useVerge(); const { verge, patchVerge } = useVerge();
@@ -143,4 +143,4 @@ export const LiteModeViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -8,15 +8,15 @@ import {
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { TooltipIcon } from "@/components/base/base-tooltip-icon";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
export const MiscViewer = ({ ref, ...props }) => { export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { verge, patchVerge } = useVerge(); const { verge, patchVerge } = useVerge();
@@ -319,4 +319,4 @@ export const MiscViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -1,15 +1,15 @@
import { ContentCopyRounded } from "@mui/icons-material"; import { ContentCopyRounded } from "@mui/icons-material";
import { alpha, Box, Button, IconButton } from "@mui/material"; import { alpha, Box, Button, IconButton } from "@mui/material";
import { writeText } from "@tauri-apps/plugin-clipboard-manager"; import { writeText } from "@tauri-apps/plugin-clipboard-manager";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import useSWR from "swr"; import useSWR from "swr";
import { BaseDialog } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { getNetworkInterfacesInfo } from "@/services/cmds"; import { getNetworkInterfacesInfo } from "@/services/cmds";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
export const NetworkInterfaceViewer = ({ ref, ...props }) => { export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [isV4, setIsV4] = useState(true); const [isV4, setIsV4] = useState(true);
@@ -99,7 +99,7 @@ export const NetworkInterfaceViewer = ({ ref, ...props }) => {
))} ))}
</BaseDialog> </BaseDialog>
); );
}; });
const AddressDisplay = (props: { label: string; content: string }) => { const AddressDisplay = (props: { label: string; content: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -11,19 +11,25 @@ import {
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useEffect, useImperativeHandle, useMemo, useState } from "react"; import {
forwardRef,
useEffect,
useImperativeHandle,
useMemo,
useState,
} from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { BaseFieldset } from "@/components/base/base-fieldset"; import { BaseFieldset } from "@/components/base/base-fieldset";
import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { TooltipIcon } from "@/components/base/base-tooltip-icon";
import { EditorViewer } from "@/components/profile/editor-viewer"; import { EditorViewer } from "@/components/profile/editor-viewer";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { useAppData } from "@/providers/app-data-provider"; import { useAppData } from "@/providers/app-data-provider";
import { getClashConfig } from "@/services/cmds";
import { import {
getAutotemProxy, getAutotemProxy,
getClashConfig,
getNetworkInterfacesInfo, getNetworkInterfacesInfo,
getSystemHostname, getSystemHostname,
getSystemProxy, getSystemProxy,
@@ -69,7 +75,7 @@ const getValidReg = (isWindows: boolean) => {
return new RegExp(rValid); return new RegExp(rValid);
}; };
export const SysproxyViewer = ({ ref, ...props }) => { export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const isWindows = getSystem() === "windows"; const isWindows = getSystem() === "windows";
const validReg = useMemo(() => getValidReg(isWindows), [isWindows]); const validReg = useMemo(() => getValidReg(isWindows), [isWindows]);
@@ -613,7 +619,7 @@ export const SysproxyViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });
const FlexBox = styled("div")` const FlexBox = styled("div")`
display: flex; display: flex;

View File

@@ -9,16 +9,16 @@ import {
useTheme, useTheme,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { EditorViewer } from "@/components/profile/editor-viewer"; import { EditorViewer } from "@/components/profile/editor-viewer";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme"; import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
export const ThemeViewer = ({ ref, ...props }) => { export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -144,7 +144,7 @@ export const ThemeViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });
const Item = styled(ListItem)(() => ({ const Item = styled(ListItem)(() => ({
padding: "5px 2px", padding: "5px 2px",

View File

@@ -8,10 +8,10 @@ import {
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Switch } from "@/components/base";
import { useClash } from "@/hooks/use-clash"; import { useClash } from "@/hooks/use-clash";
import { enhanceProfiles } from "@/services/cmds"; import { enhanceProfiles } from "@/services/cmds";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
@@ -21,7 +21,7 @@ import { StackModeSwitch } from "./stack-mode-switch";
const OS = getSystem(); const OS = getSystem();
export const TunViewer = ({ ref, ...props }) => { export const TunViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { clash, mutateClash, patchClash } = useClash(); const { clash, mutateClash, patchClash } = useClash();
@@ -238,4 +238,4 @@ export const TunViewer = ({ ref, ...props }) => {
</List> </List>
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -4,18 +4,24 @@ import { relaunch } from "@tauri-apps/plugin-process";
import { open as openUrl } from "@tauri-apps/plugin-shell"; import { open as openUrl } from "@tauri-apps/plugin-shell";
import { check as checkUpdate } from "@tauri-apps/plugin-updater"; import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState, useMemo, useEffect } from "react"; import {
forwardRef,
useImperativeHandle,
useState,
useMemo,
useEffect,
} from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import useSWR from "swr"; import useSWR from "swr";
import { BaseDialog } from "@/components/base"; import { BaseDialog, DialogRef } from "@/components/base";
import { useListen } from "@/hooks/use-listen"; import { useListen } from "@/hooks/use-listen";
import { portableFlag } from "@/pages/_layout"; import { portableFlag } from "@/pages/_layout";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
import { useUpdateState, useSetUpdateState } from "@/services/states"; import { useUpdateState, useSetUpdateState } from "@/services/states";
export const UpdateViewer = ({ ref, ...props }) => { export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -161,4 +167,4 @@ export const UpdateViewer = ({ ref, ...props }) => {
)} )}
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -1,9 +1,9 @@
import { Button, Box, Typography } from "@mui/material"; import { Button, Box, Typography } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BaseDialog, BaseEmpty } from "@/components/base"; import { BaseDialog, BaseEmpty, DialogRef } from "@/components/base";
import { useClashInfo } from "@/hooks/use-clash"; import { useClashInfo } from "@/hooks/use-clash";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { openWebUrl } from "@/services/cmds"; import { openWebUrl } from "@/services/cmds";
@@ -11,7 +11,7 @@ import { showNotice } from "@/services/noticeService";
import { WebUIItem } from "./web-ui-item"; import { WebUIItem } from "./web-ui-item";
export const WebUIViewer = ({ ref, ...props }) => { export const WebUIViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { clashInfo } = useClashInfo(); const { clashInfo } = useClashInfo();
@@ -139,4 +139,4 @@ export const WebUIViewer = ({ ref, ...props }) => {
)} )}
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -1,7 +1,7 @@
import { TextField } from "@mui/material"; import { TextField } from "@mui/material";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { useImperativeHandle, useState } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -19,10 +19,7 @@ export interface TestViewerRef {
} }
// create or edit the test item // create or edit the test item
export const TestViewer = ({ export const TestViewer = forwardRef<TestViewerRef, Props>((props, ref) => {
ref,
...props
}: Props & { ref?: React.RefObject<TestViewerRef | null> }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [openType, setOpenType] = useState<"new" | "edit">("new"); const [openType, setOpenType] = useState<"new" | "edit">("new");
@@ -176,4 +173,4 @@ export const TestViewer = ({
/> />
</BaseDialog> </BaseDialog>
); );
}; });

View File

@@ -1,5 +1,11 @@
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import React, { createContext, use, useEffect, useMemo, useRef } from "react"; import React, {
createContext,
useContext,
useEffect,
useMemo,
useRef,
} from "react";
import useSWR from "swr"; import useSWR from "swr";
import { useClashInfo } from "@/hooks/use-clash"; import { useClashInfo } from "@/hooks/use-clash";
@@ -583,12 +589,14 @@ export const AppDataProvider = ({
refreshAll, refreshAll,
]); ]);
return <AppDataContext value={value}>{children}</AppDataContext>; return (
<AppDataContext.Provider value={value}>{children}</AppDataContext.Provider>
);
}; };
// 自定义Hook访问全局数据 // 自定义Hook访问全局数据
export const useAppData = () => { export const useAppData = () => {
const context = use(AppDataContext); const context = useContext(AppDataContext);
if (!context) { if (!context) {
throw new Error("useAppData必须在AppDataProvider内使用"); throw new Error("useAppData必须在AppDataProvider内使用");

View File

@@ -1,4 +1,4 @@
import React, { createContext, useCallback, use, useState } from "react"; import React, { createContext, useCallback, useContext, useState } from "react";
interface ChainProxyContextType { interface ChainProxyContextType {
isChainMode: boolean; isChainMode: boolean;
@@ -26,7 +26,7 @@ export const ChainProxyProvider = ({
}, []); }, []);
return ( return (
<ChainProxyContext <ChainProxyContext.Provider
value={{ value={{
isChainMode, isChainMode,
setChainMode, setChainMode,
@@ -35,12 +35,12 @@ export const ChainProxyProvider = ({
}} }}
> >
{children} {children}
</ChainProxyContext> </ChainProxyContext.Provider>
); );
}; };
export const useChainProxy = () => { export const useChainProxy = () => {
const context = use(ChainProxyContext); const context = useContext(ChainProxyContext);
if (!context) { if (!context) {
throw new Error("useChainProxy must be used within a ChainProxyProvider"); throw new Error("useChainProxy must be used within a ChainProxyProvider");
} }