mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-04 23:23:45 +08:00
feat: add sorting functionality to current node list on homepage
This commit is contained in:
parent
ff5a2c6ca4
commit
cfe8328f9e
@ -33,6 +33,7 @@
|
||||
- 订阅卡片点击时间可切换下次自动更新时间,自动更新触发后页面有明确的成功与否提示
|
||||
- 添加网络管理器以优化网络请求处理,防止资源竞争导致的启动时 UI 卡死
|
||||
- 更新依赖
|
||||
- 首页当前节点增加排序功能
|
||||
|
||||
#### 优化了:
|
||||
- 系统代理 Bypass 设置
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
InputLabel,
|
||||
SelectChangeEvent,
|
||||
Tooltip,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
import { useEffect, useState, useMemo, useCallback } from "react";
|
||||
import {
|
||||
@ -22,6 +23,9 @@ import {
|
||||
SignalWifi0Bar as SignalNone,
|
||||
WifiOff as SignalError,
|
||||
ChevronRight,
|
||||
SortRounded,
|
||||
AccessTimeRounded,
|
||||
SortByAlphaRounded,
|
||||
} from "@mui/icons-material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { EnhancedCard } from "@/components/home/enhanced-card";
|
||||
@ -33,18 +37,20 @@ import { useAppData } from "@/providers/app-data-provider";
|
||||
// 本地存储的键名
|
||||
const STORAGE_KEY_GROUP = "clash-verge-selected-proxy-group";
|
||||
const STORAGE_KEY_PROXY = "clash-verge-selected-proxy";
|
||||
const STORAGE_KEY_SORT_TYPE = "clash-verge-proxy-sort-type";
|
||||
|
||||
// 代理节点信息接口
|
||||
interface ProxyOption {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// 将delayManager返回的颜色格式转换为MUI Chip组件需要的格式
|
||||
// 排序类型: 默认 | 按延迟 | 按字母
|
||||
export type ProxySortType = 0 | 1 | 2;
|
||||
|
||||
function convertDelayColor(delayValue: number) {
|
||||
const colorStr = delayManager.formatDelayColor(delayValue);
|
||||
if (!colorStr) return "default";
|
||||
|
||||
// 从"error.main"这样的格式转为"error"
|
||||
const mainColor = colorStr.split(".")[0];
|
||||
|
||||
switch (mainColor) {
|
||||
@ -61,7 +67,6 @@ function convertDelayColor(delayValue: number) {
|
||||
}
|
||||
}
|
||||
|
||||
// 根据延迟值获取合适的WiFi信号图标
|
||||
function getSignalIcon(delay: number) {
|
||||
if (delay < 0)
|
||||
return { icon: <SignalNone />, text: "未测试", color: "text.secondary" };
|
||||
@ -97,6 +102,12 @@ export const CurrentProxyCard = () => {
|
||||
const isGlobalMode = mode === "global";
|
||||
const isDirectMode = mode === "direct";
|
||||
|
||||
// 添加排序类型状态
|
||||
const [sortType, setSortType] = useState<ProxySortType>(() => {
|
||||
const savedSortType = localStorage.getItem(STORAGE_KEY_SORT_TYPE);
|
||||
return savedSortType ? Number(savedSortType) as ProxySortType : 0;
|
||||
});
|
||||
|
||||
// 定义状态类型
|
||||
type ProxyState = {
|
||||
proxyData: {
|
||||
@ -112,7 +123,6 @@ export const CurrentProxyCard = () => {
|
||||
displayProxy: any;
|
||||
};
|
||||
|
||||
// 合并状态,减少状态更新次数
|
||||
const [state, setState] = useState<ProxyState>({
|
||||
proxyData: {
|
||||
groups: [],
|
||||
@ -131,11 +141,9 @@ export const CurrentProxyCard = () => {
|
||||
useEffect(() => {
|
||||
if (!proxies) return;
|
||||
|
||||
// 提取primaryGroupName
|
||||
const getPrimaryGroupName = () => {
|
||||
if (!proxies?.groups?.length) return "";
|
||||
|
||||
// 查找主要的代理组(优先级:包含关键词 > 第一个非GLOBAL组)
|
||||
const primaryKeywords = [
|
||||
"auto",
|
||||
"select",
|
||||
@ -188,9 +196,7 @@ export const CurrentProxyCard = () => {
|
||||
useEffect(() => {
|
||||
if (!proxies) return;
|
||||
|
||||
// 使用函数式更新确保状态更新的原子性
|
||||
setState((prev) => {
|
||||
// 过滤和格式化组
|
||||
const filteredGroups = proxies.groups
|
||||
.filter((g: { name: string }) => g.name !== "DIRECT" && g.name !== "REJECT")
|
||||
.map((g: { name: string; now: string; all: Array<{ name: string }> }) => ({
|
||||
@ -213,7 +219,6 @@ export const CurrentProxyCard = () => {
|
||||
newProxy = proxies.global.now || "";
|
||||
newDisplayProxy = proxies.records?.[newProxy] || null;
|
||||
} else {
|
||||
// 普通模式 - 检查当前选择的组是否存在
|
||||
const currentGroup = filteredGroups.find(
|
||||
(g: { name: string }) => g.name === prev.selection.group,
|
||||
);
|
||||
@ -225,7 +230,6 @@ export const CurrentProxyCard = () => {
|
||||
newProxy = firstGroup.now;
|
||||
newDisplayProxy = proxies.records?.[newProxy] || null;
|
||||
|
||||
// 保存到本地存储
|
||||
if (!isGlobalMode && !isDirectMode) {
|
||||
localStorage.setItem(STORAGE_KEY_GROUP, newGroup);
|
||||
if (newProxy) {
|
||||
@ -233,7 +237,6 @@ export const CurrentProxyCard = () => {
|
||||
}
|
||||
}
|
||||
} else if (currentGroup) {
|
||||
// 使用当前组的代理
|
||||
newProxy = currentGroup.now;
|
||||
newDisplayProxy = proxies.records?.[newProxy] || null;
|
||||
}
|
||||
@ -256,7 +259,7 @@ export const CurrentProxyCard = () => {
|
||||
});
|
||||
}, [proxies, isGlobalMode, isDirectMode]);
|
||||
|
||||
// 使用防抖包装状态更新,避免快速连续更新,增加防抖时间
|
||||
// 使用防抖包装状态更新
|
||||
const debouncedSetState = useCallback(
|
||||
debounce((updateFn: (prev: ProxyState) => ProxyState) => {
|
||||
setState(updateFn);
|
||||
@ -271,10 +274,8 @@ export const CurrentProxyCard = () => {
|
||||
|
||||
const newGroup = event.target.value;
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem(STORAGE_KEY_GROUP, newGroup);
|
||||
|
||||
// 获取该组当前选中的代理
|
||||
setState((prev) => {
|
||||
const group = prev.proxyData.groups.find((g: { name: string }) => g.name === newGroup);
|
||||
if (group) {
|
||||
@ -308,7 +309,6 @@ export const CurrentProxyCard = () => {
|
||||
const currentGroup = state.selection.group;
|
||||
const previousProxy = state.selection.proxy;
|
||||
|
||||
// 立即更新UI,优化体验
|
||||
debouncedSetState((prev: ProxyState) => ({
|
||||
...prev,
|
||||
selection: {
|
||||
@ -318,13 +318,11 @@ export const CurrentProxyCard = () => {
|
||||
displayProxy: prev.proxyData.records[newProxy] || null,
|
||||
}));
|
||||
|
||||
// 非特殊模式下保存到本地存储
|
||||
if (!isGlobalMode && !isDirectMode) {
|
||||
localStorage.setItem(STORAGE_KEY_PROXY, newProxy);
|
||||
}
|
||||
|
||||
try {
|
||||
// 更新代理设置
|
||||
await updateProxy(currentGroup, newProxy);
|
||||
|
||||
// 自动关闭连接设置
|
||||
@ -363,7 +361,6 @@ export const CurrentProxyCard = () => {
|
||||
|
||||
// 获取要显示的代理节点
|
||||
const currentProxy = useMemo(() => {
|
||||
// 从state中获取当前代理信息
|
||||
return state.displayProxy;
|
||||
}, [state.displayProxy]);
|
||||
|
||||
@ -372,7 +369,6 @@ export const CurrentProxyCard = () => {
|
||||
? delayManager.getDelayFix(currentProxy, state.selection.group)
|
||||
: -1;
|
||||
|
||||
// 获取信号图标
|
||||
const signalInfo = getSignalIcon(currentDelay);
|
||||
|
||||
// 自定义渲染选择框中的值
|
||||
@ -399,27 +395,99 @@ export const CurrentProxyCard = () => {
|
||||
[state.proxyData.records, state.selection.group],
|
||||
);
|
||||
|
||||
// 计算要显示的代理选项 - 使用 useMemo 优化
|
||||
// 排序类型变更
|
||||
const handleSortTypeChange = useCallback(() => {
|
||||
const newSortType = ((sortType + 1) % 3) as ProxySortType;
|
||||
setSortType(newSortType);
|
||||
localStorage.setItem(STORAGE_KEY_SORT_TYPE, newSortType.toString());
|
||||
}, [sortType]);
|
||||
|
||||
// 排序代理函数
|
||||
const sortProxies = useCallback(
|
||||
(proxies: ProxyOption[]) => {
|
||||
if (!proxies || sortType === 0) return proxies;
|
||||
|
||||
const list = [...proxies];
|
||||
|
||||
if (sortType === 1) {
|
||||
list.sort((a, b) => {
|
||||
const ad = delayManager.getDelayFix(
|
||||
state.proxyData.records[a.name],
|
||||
state.selection.group
|
||||
);
|
||||
const bd = delayManager.getDelayFix(
|
||||
state.proxyData.records[b.name],
|
||||
state.selection.group
|
||||
);
|
||||
|
||||
if (ad === -1 || ad === -2) return 1;
|
||||
if (bd === -1 || bd === -2) return -1;
|
||||
|
||||
return ad - bd;
|
||||
});
|
||||
} else {
|
||||
list.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
return list;
|
||||
},
|
||||
[sortType, state?.proxyData?.records, state?.selection?.group]
|
||||
);
|
||||
|
||||
// 计算要显示的代理选项
|
||||
const proxyOptions = useMemo(() => {
|
||||
if (isDirectMode) {
|
||||
return [{ name: "DIRECT" }];
|
||||
}
|
||||
if (isGlobalMode && state.proxyData.records) {
|
||||
// 全局模式下的选项
|
||||
return Object.keys(state.proxyData.records)
|
||||
.filter((name) => name !== "DIRECT" && name !== "REJECT")
|
||||
.map((name) => ({ name }));
|
||||
if (isGlobalMode && proxies?.global) {
|
||||
const options = proxies.global.all
|
||||
.filter((p: any) => {
|
||||
const name = typeof p === 'string' ? p : p.name;
|
||||
return name !== "DIRECT" && name !== "REJECT";
|
||||
})
|
||||
.map((p: any) => ({
|
||||
name: typeof p === 'string' ? p : p.name
|
||||
}));
|
||||
|
||||
return sortProxies(options);
|
||||
}
|
||||
|
||||
// 普通模式
|
||||
// 规则模式
|
||||
const group = state.proxyData.groups.find(
|
||||
(g: { name: string }) => g.name === state.selection.group,
|
||||
);
|
||||
if (group) {
|
||||
return group.all.map((name) => ({ name }));
|
||||
const options = group.all.map((name) => ({ name }));
|
||||
return sortProxies(options);
|
||||
}
|
||||
return [];
|
||||
}, [isDirectMode, isGlobalMode, state.proxyData, state.selection.group]);
|
||||
}, [isDirectMode, isGlobalMode, proxies, state.proxyData, state.selection.group, sortProxies]);
|
||||
|
||||
// 获取排序图标
|
||||
const getSortIcon = () => {
|
||||
switch (sortType) {
|
||||
case 1:
|
||||
return <AccessTimeRounded fontSize="small" />;
|
||||
case 2:
|
||||
return <SortByAlphaRounded fontSize="small" />;
|
||||
default:
|
||||
return <SortRounded fontSize="small" />;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取排序提示文本
|
||||
const getSortTooltip = () => {
|
||||
switch (sortType) {
|
||||
case 0:
|
||||
return t("Sort by default");
|
||||
case 1:
|
||||
return t("Sort by delay");
|
||||
case 2:
|
||||
return t("Sort by name");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<EnhancedCard
|
||||
@ -439,6 +507,17 @@ export const CurrentProxyCard = () => {
|
||||
}
|
||||
iconColor={currentProxy ? "primary" : undefined}
|
||||
action={
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Tooltip title={getSortTooltip()}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={handleSortTypeChange}
|
||||
sx={{ mr: 1 }}
|
||||
>
|
||||
{getSortIcon()}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
@ -448,6 +527,7 @@ export const CurrentProxyCard = () => {
|
||||
>
|
||||
{t("Label-Proxies")}
|
||||
</Button>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
{currentProxy ? (
|
||||
|
Loading…
x
Reference in New Issue
Block a user