feat(proxy-groups, current-proxy-card): auto-refresh delay sorting

- proxy-groups: recalculate active group head and reapply delay sort after tests so list reorders automatically when "按延迟排序" is active.
- current-proxy-card: add delaySortRefresh trigger after auto/manual latency checks to immediately refresh selector and proxy list ordering.
- current-proxy-card: listen for delaySortRefresh to keep displayed delay chips and option ordering aligned with latest measurements.
This commit is contained in:
Slinetrac
2025-10-14 20:13:04 +08:00
Unverified
parent 7c71d07ad2
commit 2e3174baa7
3 changed files with 60 additions and 40 deletions

View File

@@ -10,6 +10,7 @@
- 主界面“当前节点”卡片新增“延迟测试”按钮
- 新增批量选择配置文件功能
- 新增本地备份功能
- 主界面“当前节点”卡片新增自动延迟检测开关(默认关闭)
### 🚀 优化改进
@@ -28,8 +29,8 @@
- 改进 Windows 和 Unix 的 服务连接方式以及权限,避免无法连接服务或内核
- 修改内核默认日志级别为 Info
- 支持通过桌面快捷方式重新打开应用
- 主界面“当前节点”卡片新增自动延迟检测开关(默认关闭)
- 支持订阅界面输入链接后回车导入
- 选择按延迟排序时每次延迟测试自动刷新节点顺序
### 🐞 修复问题

View File

@@ -124,6 +124,7 @@ export const CurrentProxyCard = () => {
const savedSortType = localStorage.getItem(STORAGE_KEY_SORT_TYPE);
return savedSortType ? (Number(savedSortType) as ProxySortType) : 0;
});
const [delaySortRefresh, setDelaySortRefresh] = useState(0);
// 定义状态类型
type ProxyState = {
@@ -443,12 +444,17 @@ export const CurrentProxyCard = () => {
} finally {
autoCheckInProgressRef.current = false;
refreshProxy();
if (sortType === 1) {
setDelaySortRefresh((prev) => prev + 1);
}
}
}, [
isDirectMode,
refreshProxy,
state.selection.group,
state.selection.proxy,
sortType,
setDelaySortRefresh,
]);
useEffect(() => {
@@ -487,28 +493,25 @@ export const CurrentProxyCard = () => {
]);
// 自定义渲染选择框中的值
const renderProxyValue = useCallback(
(selected: string) => {
if (!selected || !state.proxyData.records[selected]) return selected;
const renderProxyValue = (selected: string) => {
if (!selected || !state.proxyData.records[selected]) return selected;
const delayValue = delayManager.getDelayFix(
state.proxyData.records[selected],
state.selection.group,
);
const delayValue = delayManager.getDelayFix(
state.proxyData.records[selected],
state.selection.group,
);
return (
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<Typography noWrap>{selected}</Typography>
<Chip
size="small"
label={delayManager.formatDelay(delayValue)}
color={convertDelayColor(delayValue)}
/>
</Box>
);
},
[state.proxyData.records, state.selection.group],
);
return (
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<Typography noWrap>{selected}</Typography>
<Chip
size="small"
label={delayManager.formatDelay(delayValue)}
color={convertDelayColor(delayValue)}
/>
</Box>
);
};
// 排序类型变更
const handleSortTypeChange = useCallback(() => {
@@ -594,24 +597,28 @@ export const CurrentProxyCard = () => {
}
refreshProxy();
if (sortType === 1) {
setDelaySortRefresh((prev) => prev + 1);
}
});
// 排序代理函数(增加非空校验)
const sortProxies = useCallback(
(proxies: ProxyOption[]) => {
if (!proxies || sortType === 0) return proxies;
// 计算要显示的代理选项(增加非空校验)
const proxyOptions = useMemo(() => {
const sortWithLatency = (proxiesToSort: ProxyOption[]) => {
if (!proxiesToSort || sortType === 0) return proxiesToSort;
// 确保数据存在
if (!state.proxyData.records || !state.selection.group) return proxies;
if (!state.proxyData.records || !state.selection.group) {
return proxiesToSort;
}
const list = [...proxies];
const list = [...proxiesToSort];
if (sortType === 1) {
const refreshTick = delaySortRefresh;
list.sort((a, b) => {
const recordA = state.proxyData.records[a.name];
const recordB = state.proxyData.records[b.name];
// 处理 record 不存在的情况
if (!recordA) return 1;
if (!recordB) return -1;
@@ -621,19 +628,16 @@ export const CurrentProxyCard = () => {
if (ad === -1 || ad === -2) return 1;
if (bd === -1 || bd === -2) return -1;
return ad - bd;
if (ad !== bd) return ad - bd;
return refreshTick >= 0 ? a.name.localeCompare(b.name) : 0;
});
} 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" }];
}
@@ -647,7 +651,7 @@ export const CurrentProxyCard = () => {
name: typeof p === "string" ? p : p.name,
}));
return sortProxies(options);
return sortWithLatency(options);
}
// 规则模式
@@ -657,7 +661,7 @@ export const CurrentProxyCard = () => {
if (group) {
const options = group.all.map((name) => ({ name }));
return sortProxies(options);
return sortWithLatency(options);
}
return [];
@@ -667,7 +671,8 @@ export const CurrentProxyCard = () => {
proxies,
state.proxyData,
state.selection.group,
sortProxies,
sortType,
delaySortRefresh,
]);
// 获取排序图标

View File

@@ -80,6 +80,16 @@ export const ProxyGroups = (props: Props) => {
selectedGroup,
);
const getGroupHeadState = useCallback(
(groupName: string) => {
const headItem = renderList.find(
(item) => item.type === 1 && item.group?.name === groupName,
);
return headItem?.headState;
},
[renderList],
);
// 统代理选择
const { handleProxyGroupChange } = useProxySelection({
onSuccess: () => {
@@ -297,9 +307,13 @@ export const ProxyGroups = (props: Props) => {
console.log(`[ProxyGroups] 延迟测试完成,组: ${groupName}`);
} catch (error) {
console.error(`[ProxyGroups] 延迟测试出错,组: ${groupName}`, error);
} finally {
const headState = getGroupHeadState(groupName);
if (headState?.sortType === 1) {
onHeadState(groupName, { sortType: headState.sortType });
}
onProxies();
}
onProxies();
});
// 滚到对应的节点