From bb9d4cd2b81757a7d2c3718c107f4c6ce2f351b2 Mon Sep 17 00:00:00 2001 From: Decrabbit <99632363+Decrabbityyy@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:20:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BB=8E=E6=8C=89=E9=94=AE=E9=A1=B5?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=8F=AF=E6=89=93=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/keybindings.rs | 43 +++++++++++++++++++++-- src/celemod-ui/src/routes/KeyBindings.tsx | 26 +++++++++++--- src/celemod-ui/src/tauri/commands.ts | 2 +- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/keybindings.rs b/src-tauri/src/keybindings.rs index e48ce6d..197772f 100644 --- a/src-tauri/src/keybindings.rs +++ b/src-tauri/src/keybindings.rs @@ -5,6 +5,10 @@ use std::{ fs, io::Read, path::{Path, PathBuf}, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, }; use super::{ @@ -726,9 +730,35 @@ fn replace_mouse_action(content: &str, action: &str, values: &[String]) -> Resul } #[tauri::command] -pub(super) fn get_key_bindings( +pub(super) async fn get_key_bindings( game_path: String, language: String, + cancel: Option, +) -> Result { + let cancelled = Arc::new(AtomicBool::new(false)); + let worker_cancelled = Arc::clone(&cancelled); + let worker = tauri::async_runtime::spawn_blocking(move || { + get_key_bindings_impl(game_path, language, &worker_cancelled) + }); + // 默认允许取消:IPC 请求被取消(页面卸载、WebView 销毁)时 Tauri 会 drop + // 本 future,guard 随即通知 worker 在下一个检查点退出,结果随之丢弃。 + // 传 cancel: true 可显式禁用(如页面内的手动刷新)。 + struct CancelOnDrop(Arc); + impl Drop for CancelOnDrop { + fn drop(&mut self) { + self.0.store(true, Ordering::Relaxed); + } + } + let _guard = (!cancel.unwrap_or(false)).then(|| CancelOnDrop(cancelled)); + worker + .await + .map_err(|error| format!("读取按键配置失败:{error}"))? +} + +fn get_key_bindings_impl( + game_path: String, + language: String, + cancelled: &AtomicBool, ) -> Result { let normalized = normalize_game_path_impl(&game_path); let game_path = PathBuf::from(normalized); @@ -782,6 +812,9 @@ pub(super) fn get_key_bindings( config_files.sort_by_key(|entry| entry.file_name().to_string_lossy().to_ascii_lowercase()); for config in config_files { + if cancelled.load(Ordering::Relaxed) { + return Err("已取消读取按键配置".to_string()); + } let file_name = config.file_name().to_string_lossy().to_string(); let source = config_source_name(&file_name); let Ok(content) = fs::read_to_string(config.path()) else { @@ -973,8 +1006,12 @@ mod tests { if !path.join("Saves").join("settings.celeste").is_file() { return; } - let catalog = get_key_bindings(path.to_string_lossy().into_owned(), "zh-CN".to_string()) - .expect("local key bindings should be readable"); + let catalog = get_key_bindings_impl( + path.to_string_lossy().into_owned(), + "zh-CN".to_string(), + &AtomicBool::new(false), + ) + .expect("local key bindings should be readable"); assert!( catalog .entries diff --git a/src/celemod-ui/src/routes/KeyBindings.tsx b/src/celemod-ui/src/routes/KeyBindings.tsx index fd97a2f..2729c0e 100644 --- a/src/celemod-ui/src/routes/KeyBindings.tsx +++ b/src/celemod-ui/src/routes/KeyBindings.tsx @@ -236,9 +236,11 @@ export const KeyBindings = () => { const [selectedConflict, setSelectedConflict] = useState(""); const [showDisabled, setShowDisabled] = useState(false); const [saving, setSaving] = useState(""); + const requestId = useRef(0); const refresh = useCallback(() => { if (!gamePath) return; + const currentRequest = ++requestId.current; setLoading(true); setError(""); void callRemote( @@ -246,12 +248,28 @@ export const KeyBindings = () => { gamePath, currentLang, ) - .then(setCatalog) - .catch((reason) => setError(String(reason))) - .finally(() => setLoading(false)); + .then((data) => { + if (currentRequest !== requestId.current) return; + setCatalog(data); + }) + .catch((reason) => { + if (currentRequest !== requestId.current) return; + setError(String(reason)); + }) + .finally(() => { + if (currentRequest === requestId.current) setLoading(false); + }); }, [currentLang, gamePath]); - useEffect(refresh, [refresh]); + useEffect(() => { + const requestScope = requestId; + refresh(); + // 卸载时作废旧请求:页面切换中途到达的响应不再写入状态, + // 下次挂载会以新序号重新加载。 + return () => { + requestScope.current += 1; + }; + }, [refresh]); const consideredEntries = useMemo( () => diff --git a/src/celemod-ui/src/tauri/commands.ts b/src/celemod-ui/src/tauri/commands.ts index 108215c..20d640d 100644 --- a/src/celemod-ui/src/tauri/commands.ts +++ b/src/celemod-ui/src/tauri/commands.ts @@ -117,7 +117,7 @@ const parameterNames: Record = { get_mod_cache_status: [], get_database_path: [], start_miaonet_oauth: ["gamePath", "onEvent"], - get_key_bindings: ["gamePath", "language"], + get_key_bindings: ["gamePath", "language", "cancel"], update_key_binding: ["gamePath", "request"], };