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 <noreply@anthropic.com>
This commit is contained in:
@@ -57,14 +57,27 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveUrl() async {
|
Future<void> _saveUrl() async {
|
||||||
final url = _controller.text.trim();
|
final url = _controller.text.trim().replaceAll(RegExp(r'/+$'), '');
|
||||||
if (url.isEmpty) {
|
if (url.isEmpty) {
|
||||||
_showSnackBar('请输入 API 地址', isError: true);
|
_showSnackBar('请输入 API 地址', isError: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(() => _saving = true);
|
setState(() => _saving = true);
|
||||||
|
// Load config once, apply all changes, then save once to avoid race conditions
|
||||||
final configService = AppConfigService();
|
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);
|
setState(() => _saving = false);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_showSnackBar('设置已保存');
|
_showSnackBar('设置已保存');
|
||||||
@@ -91,18 +104,6 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
);
|
);
|
||||||
if (result != null && result.files.single.path != null) {
|
if (result != null && result.files.single.path != null) {
|
||||||
final path = result.files.single.path!;
|
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(() {
|
setState(() {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'success':
|
case 'success':
|
||||||
@@ -121,18 +122,6 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _clearSound(String key) async {
|
Future<void> _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(() {
|
setState(() {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'success':
|
case 'success':
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ class AppConfigService {
|
|||||||
Future<void> setString(String key, String value) async {
|
Future<void> setString(String key, String value) async {
|
||||||
final config = await loadConfig();
|
final config = await loadConfig();
|
||||||
config[key.toString()] = value.toString();
|
config[key.toString()] = value.toString();
|
||||||
await _saveConfig(config);
|
await saveConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 完整写入 JSON 文件
|
/// 完整写入 JSON 文件
|
||||||
Future<void> _saveConfig(Map<String, dynamic> config) async {
|
Future<void> saveConfig(Map<String, dynamic> config) async {
|
||||||
try {
|
try {
|
||||||
final file = File(await _filePath);
|
final file = File(await _filePath);
|
||||||
await file.writeAsString(jsonEncode(config));
|
await file.writeAsString(jsonEncode(config));
|
||||||
|
|||||||
Reference in New Issue
Block a user