mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-06 09:02:08 +08:00
fix(proxies): move an unpicked GLOBAL off DIRECT to its first group
Keep DIRECT and REJECT listed in global mode.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
<summary><strong> 🐞 修复问题 </strong></summary>
|
||||
|
||||
- 修复检测更新后又发布新版时,应用内更新失败
|
||||
- 修复全局模式默认停在 `DIRECT` 或 `REJECT` 导致流量未经代理,现自动切换到第一个策略组
|
||||
|
||||
</details>
|
||||
|
||||
@@ -37,5 +38,6 @@
|
||||
<details>
|
||||
<summary><strong> 🚀 优化改进 </strong></summary>
|
||||
|
||||
- 优化全局模式代理列表:仅显示 `GLOBAL` 节点,选中其中的策略组时才显示该组
|
||||
|
||||
</details>
|
||||
|
||||
@@ -614,6 +614,53 @@ async fn select_node_with_timeout(group_name: &String, node: &String) -> Result<
|
||||
.with_context(|| format!("failed to select node [{node}] for group [{group_name}]"))
|
||||
}
|
||||
|
||||
// mihomo lists these first in GLOBAL, so an unpicked GLOBAL starts on DIRECT.
|
||||
const GLOBAL_BUILTINS: [&str; 2] = ["DIRECT", "REJECT"];
|
||||
|
||||
/// GLOBAL's first visible group, else its first node, while GLOBAL is still on a built-in.
|
||||
fn global_exit_target(proxies: &Proxies) -> Option<&str> {
|
||||
let global = proxies.proxies.get("GLOBAL")?;
|
||||
if !global.now.as_deref().is_some_and(|now| GLOBAL_BUILTINS.contains(&now)) {
|
||||
return None;
|
||||
}
|
||||
let members = global.all.as_deref()?;
|
||||
let is_visible_group = |name: &str| {
|
||||
proxies.proxies.get(name).is_some_and(|proxy| {
|
||||
proxy.hidden != Some(true)
|
||||
&& matches!(
|
||||
proxy.proxy_type,
|
||||
ProxyType::Selector | ProxyType::URLTest | ProxyType::Fallback | ProxyType::LoadBalance
|
||||
)
|
||||
})
|
||||
};
|
||||
members
|
||||
.iter()
|
||||
.find(|name| is_visible_group(name))
|
||||
.or_else(|| members.iter().find(|name| !GLOBAL_BUILTINS.contains(&name.as_str())))
|
||||
.map(|name| name.as_str())
|
||||
}
|
||||
|
||||
/// Moves GLOBAL off a built-in unless the profile records any GLOBAL choice.
|
||||
pub(crate) async fn leave_default_global_exit() -> Result<()> {
|
||||
let profiles = Config::profiles().await.data_arc();
|
||||
let has_record = profiles
|
||||
.current
|
||||
.as_ref()
|
||||
.and_then(|current| profiles.get_item(current).ok())
|
||||
.and_then(|item| item.selected.as_deref())
|
||||
.is_some_and(|selected| selected.iter().any(|item| item.name.as_deref() == Some("GLOBAL")));
|
||||
if has_record {
|
||||
return Ok(());
|
||||
}
|
||||
let snapshot = fetch_proxies_with_timeout().await?;
|
||||
let Some(target) = global_exit_target(&snapshot) else {
|
||||
return Ok(());
|
||||
};
|
||||
select_node_with_timeout(&String::from("GLOBAL"), &String::from(target)).await?;
|
||||
logging!(info, Type::Config, "moved GLOBAL off its built-in default to {target}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remaining_activations(
|
||||
activations: &[(String, String)],
|
||||
completed: &HashMap<String, String>,
|
||||
@@ -1019,6 +1066,12 @@ fn activate_selected_nodes_with(repair: SelectionRepair) -> tokio::sync::oneshot
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
|
||||
if Config::clash().await.latest_arc().get_mode().as_deref() == Some("global")
|
||||
&& let Err(err) = leave_default_global_exit().await
|
||||
{
|
||||
logging!(warn, Type::Config, "failed to move GLOBAL off its default: {err:#}");
|
||||
}
|
||||
|
||||
if selected.is_empty() {
|
||||
if is_activation_current(generation) {
|
||||
announce(Refresh::Clash);
|
||||
|
||||
@@ -66,6 +66,12 @@ pub async fn change_clash_mode(mode: String) -> Result<(), String> {
|
||||
return Err(err.to_string().into());
|
||||
}
|
||||
|
||||
if mode.as_str() == "global"
|
||||
&& let Err(err) = crate::config::profiles::leave_default_global_exit().await
|
||||
{
|
||||
logging!(warn, Type::Core, "failed to move GLOBAL off its default: {err:#}");
|
||||
}
|
||||
|
||||
let clash = Config::clash().await;
|
||||
let save_result = {
|
||||
let _config_write = Config::lock_config_write().await;
|
||||
|
||||
@@ -79,18 +79,6 @@ const resolveOccurrences = (view: ProxyViewV1, group: ProxyGroupView) =>
|
||||
member: resolveMember(view, member),
|
||||
}))
|
||||
|
||||
// mihomo selects DIRECT in GLOBAL by default, so the selected built-in stays visible.
|
||||
const HIDDEN_GLOBAL_BUILTINS = new Set(['DIRECT', 'REJECT'])
|
||||
|
||||
const isHiddenGlobalBuiltin = (
|
||||
group: ProxyGroupView,
|
||||
{ member }: ResolvedMemberOccurrence,
|
||||
) =>
|
||||
member.kind === 'node' &&
|
||||
member.node.source.kind === 'core' &&
|
||||
HIDDEN_GLOBAL_BUILTINS.has(member.node.source.proxyName) &&
|
||||
member.ref.name !== group.now
|
||||
|
||||
const memberKey = (
|
||||
group: ProxyGroupView,
|
||||
occurrence: ResolvedMemberOccurrence,
|
||||
@@ -307,11 +295,8 @@ export const useRenderList = (
|
||||
]
|
||||
|
||||
if (headState.open || isFlat) {
|
||||
const resolved = resolveOccurrences(proxyView, group)
|
||||
const occurrences = filterSort(
|
||||
isFlat
|
||||
? resolved.filter((item) => !isHiddenGlobalBuiltin(group, item))
|
||||
: resolved,
|
||||
resolveOccurrences(proxyView, group),
|
||||
group.name,
|
||||
headState.filterText,
|
||||
headState.sortType,
|
||||
|
||||
Reference in New Issue
Block a user