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
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 的 服务连接方式以及权限,避免无法连接服务或内核 - 改进 Windows 和 Unix 的 服务连接方式以及权限,避免无法连接服务或内核
- 修改内核默认日志级别为 Info - 修改内核默认日志级别为 Info
- 支持通过桌面快捷方式重新打开应用 - 支持通过桌面快捷方式重新打开应用
- 主界面“当前节点”卡片新增自动延迟检测开关(默认关闭)
- 支持订阅界面输入链接后回车导入 - 支持订阅界面输入链接后回车导入
- 选择按延迟排序时每次延迟测试自动刷新节点顺序
### 🐞 修复问题 ### 🐞 修复问题

View File

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

View File

@@ -80,6 +80,16 @@ export const ProxyGroups = (props: Props) => {
selectedGroup, 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({ const { handleProxyGroupChange } = useProxySelection({
onSuccess: () => { onSuccess: () => {
@@ -297,9 +307,13 @@ export const ProxyGroups = (props: Props) => {
console.log(`[ProxyGroups] 延迟测试完成,组: ${groupName}`); console.log(`[ProxyGroups] 延迟测试完成,组: ${groupName}`);
} catch (error) { } catch (error) {
console.error(`[ProxyGroups] 延迟测试出错,组: ${groupName}`, error); console.error(`[ProxyGroups] 延迟测试出错,组: ${groupName}`, error);
} finally {
const headState = getGroupHeadState(groupName);
if (headState?.sortType === 1) {
onHeadState(groupName, { sortType: headState.sortType });
}
onProxies();
} }
onProxies();
}); });
// 滚到对应的节点 // 滚到对应的节点