From 077a1ab1b93cc4b769f97201c71cc4b82e6f8f26 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Wed, 13 May 2026 16:58:39 +0800 Subject: [PATCH] fix(settings): save all config atomically and strip trailing slash from API URL - Save API URL and sound paths in a single load-modify-write cycle to prevent race conditions that wiped previously saved config - Strip trailing slashes from API URL to avoid double-slash 404 errors - Make AppConfigService.saveConfig public for batch config writes Co-Authored-By: Claude Opus 4.6 --- lib/pages/settings_page.dart | 41 ++++++++++------------------ lib/services/app_config_service.dart | 4 +-- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index 659d78a..44fdd80 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -57,14 +57,27 @@ class _SettingsPageState extends State { } Future _saveUrl() async { - final url = _controller.text.trim(); + final url = _controller.text.trim().replaceAll(RegExp(r'/+$'), ''); if (url.isEmpty) { _showSnackBar('请输入 API 地址', isError: true); return; } setState(() => _saving = true); + // Load config once, apply all changes, then save once to avoid race conditions final configService = AppConfigService(); - await configService.setString('api_url', url); + final config = await configService.loadConfig(); + config['api_url'] = url; + if (_successPath != null) config['sound_success'] = _successPath; + else config.remove('sound_success'); + if (_failurePath != null) config['sound_failure'] = _failurePath; + else config.remove('sound_failure'); + if (_beepPath != null) config['sound_beep'] = _beepPath; + else config.remove('sound_beep'); + if (_errorPath != null) config['sound_error'] = _errorPath; + else config.remove('sound_error'); + if (_alertPath != null) config['sound_alert'] = _alertPath; + else config.remove('sound_alert'); + await configService.saveConfig(config); setState(() => _saving = false); if (mounted) { _showSnackBar('设置已保存'); @@ -91,18 +104,6 @@ class _SettingsPageState extends State { ); if (result != null && result.files.single.path != null) { final path = result.files.single.path!; - switch (key) { - case 'success': - await _soundService.setSuccessPath(path); - case 'failure': - await _soundService.setFailurePath(path); - case 'beep': - await _soundService.setBeepPath(path); - case 'error': - await _soundService.setErrorPath(path); - case 'alert': - await _soundService.setAlertPath(path); - } setState(() { switch (key) { case 'success': @@ -121,18 +122,6 @@ class _SettingsPageState extends State { } Future _clearSound(String key) async { - switch (key) { - case 'success': - await _soundService.setSuccessPath(null); - case 'failure': - await _soundService.setFailurePath(null); - case 'beep': - await _soundService.setBeepPath(null); - case 'error': - await _soundService.setErrorPath(null); - case 'alert': - await _soundService.setAlertPath(null); - } setState(() { switch (key) { case 'success': diff --git a/lib/services/app_config_service.dart b/lib/services/app_config_service.dart index 9f49ead..bc67f8d 100644 --- a/lib/services/app_config_service.dart +++ b/lib/services/app_config_service.dart @@ -38,11 +38,11 @@ class AppConfigService { Future setString(String key, String value) async { final config = await loadConfig(); config[key.toString()] = value.toString(); - await _saveConfig(config); + await saveConfig(config); } /// 完整写入 JSON 文件 - Future _saveConfig(Map config) async { + Future saveConfig(Map config) async { try { final file = File(await _filePath); await file.writeAsString(jsonEncode(config));