From 0ecf6891c0231a42ba8fa0a4ffaa957b8c82a227 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Mon, 26 Jan 2026 15:55:40 +0800 Subject: [PATCH] refactor(hooks): improve error handling and user feedback Address code audit findings #3, #5, #9: Save queue improvements: - Add error logging in enqueueSave() catch handlers - Use console.error with [SaveQueue] prefix for debugging - Prevent silent failures in async save operations Extract operation improvements: - Add toast.success() notification after extract completes - Notify user that custom config was automatically updated - Add error logging for failed settingsConfig updates I18n improvements: - Replace MCP i18n keys with dedicated Codex keys: - codexConfig.tomlFormatError - codexConfig.tomlSyntaxError - codexConfig.extractedTomlInvalid - Avoid namespace pollution between modules --- .../forms/hooks/useCodexCommonConfig.ts | 29 ++++++++++++++----- .../forms/hooks/useCommonConfigSnippet.ts | 19 ++++++++++-- .../forms/hooks/useGeminiCommonConfig.ts | 12 +++++++- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/components/providers/forms/hooks/useCodexCommonConfig.ts b/src/components/providers/forms/hooks/useCodexCommonConfig.ts index 837bec5af..63f043a03 100644 --- a/src/components/providers/forms/hooks/useCodexCommonConfig.ts +++ b/src/components/providers/forms/hooks/useCodexCommonConfig.ts @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; import TOML from "smol-toml"; import { configApi } from "@/lib/api"; import { @@ -123,7 +124,10 @@ export function useCodexCommonConfig({ const saveQueueRef = useRef>(Promise.resolve()); const enqueueSave = useCallback((saveFn: () => Promise) => { const next = saveQueueRef.current.then(saveFn); - saveQueueRef.current = next.catch(() => {}); + // Log errors to help with debugging, but don't block subsequent saves + saveQueueRef.current = next.catch((e) => { + console.error("[SaveQueue] Codex common config save failed:", e); + }); return next; }, []); @@ -255,7 +259,7 @@ export function useCodexCommonConfig({ const tomlError = validateTomlFormat(commonConfigSnippet); if (tomlError) { setCommonConfigError( - t("mcp.error.tomlInvalid", { defaultValue: "TOML 格式错误" }), + t("codexConfig.tomlFormatError", { defaultValue: "TOML 格式错误" }), ); setUseCommonConfig(false); return; @@ -322,7 +326,7 @@ export function useCodexCommonConfig({ const tomlError = validateTomlFormat(commonConfigSnippet); if (tomlError) { setCommonConfigError( - t("mcp.error.tomlInvalid", { defaultValue: "TOML 格式错误" }), + t("codexConfig.tomlFormatError", { defaultValue: "TOML 格式错误" }), ); setUseCommonConfig(false); return; @@ -361,7 +365,7 @@ export function useCodexCommonConfig({ const tomlError = validateTomlFormat(value); if (tomlError) { setCommonConfigError( - t("mcp.error.tomlInvalid", { + t("codexConfig.tomlSyntaxError", { defaultValue: "TOML 格式错误,请检查语法", }), ); @@ -409,8 +413,8 @@ export function useCodexCommonConfig({ const tomlError = validateTomlFormat(extracted); if (tomlError) { setCommonConfigError( - t("mcp.error.tomlInvalid", { - defaultValue: "TOML 格式错误,请检查语法", + t("codexConfig.extractedTomlInvalid", { + defaultValue: "提取的配置 TOML 格式错误", }), ); return; @@ -431,8 +435,17 @@ export function useCodexCommonConfig({ const parsed = JSON.parse(codexConfig); parsed.config = diffResult.customToml; onConfigChange(JSON.stringify(parsed, null, 2)); - } catch { - // 忽略解析错误 + // Notify user that config was modified + toast.success( + t("codexConfig.extractSuccess", { + defaultValue: "已提取通用配置,自定义配置已自动更新", + }), + ); + } catch (parseError) { + console.warn( + "[Extract] Failed to update codexConfig after extract:", + parseError, + ); } } } catch (error) { diff --git a/src/components/providers/forms/hooks/useCommonConfigSnippet.ts b/src/components/providers/forms/hooks/useCommonConfigSnippet.ts index c753fba09..72b25ce39 100644 --- a/src/components/providers/forms/hooks/useCommonConfigSnippet.ts +++ b/src/components/providers/forms/hooks/useCommonConfigSnippet.ts @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; import { validateJsonConfig } from "@/utils/providerConfigUtils"; import { configApi } from "@/lib/api"; import { @@ -101,7 +102,10 @@ export function useCommonConfigSnippet({ const saveQueueRef = useRef>(Promise.resolve()); const enqueueSave = useCallback((saveFn: () => Promise) => { const next = saveQueueRef.current.then(saveFn); - saveQueueRef.current = next.catch(() => {}); + // Log errors to help with debugging, but don't block subsequent saves + saveQueueRef.current = next.catch((e) => { + console.error("[SaveQueue] Claude common config save failed:", e); + }); return next; }, []); @@ -424,9 +428,18 @@ export function useCommonConfigSnippet({ if (isPlainObject(customParsed) && isPlainObject(extractedParsed)) { const diffResult = extractDifference(customParsed, extractedParsed); onConfigChange(JSON.stringify(diffResult.customConfig, null, 2)); + // Notify user that config was modified + toast.success( + t("claudeConfig.extractSuccess", { + defaultValue: "已提取通用配置,自定义配置已自动更新", + }), + ); } - } catch { - // 忽略解析错误 + } catch (parseError) { + console.warn( + "[Extract] Failed to update settingsConfig after extract:", + parseError, + ); } } catch (error) { console.error("提取通用配置失败:", error); diff --git a/src/components/providers/forms/hooks/useGeminiCommonConfig.ts b/src/components/providers/forms/hooks/useGeminiCommonConfig.ts index 22a00a8df..494e76892 100644 --- a/src/components/providers/forms/hooks/useGeminiCommonConfig.ts +++ b/src/components/providers/forms/hooks/useGeminiCommonConfig.ts @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; import { configApi } from "@/lib/api"; import { GEMINI_COMMON_ENV_FORBIDDEN_KEYS, @@ -106,7 +107,10 @@ export function useGeminiCommonConfig({ const saveQueueRef = useRef>(Promise.resolve()); const enqueueSave = useCallback((saveFn: () => Promise) => { const next = saveQueueRef.current.then(saveFn); - saveQueueRef.current = next.catch(() => {}); + // Log errors to help with debugging, but don't block subsequent saves + saveQueueRef.current = next.catch((e) => { + console.error("[SaveQueue] Gemini common config save failed:", e); + }); return next; }, []); @@ -436,6 +440,12 @@ export function useGeminiCommonConfig({ } } onEnvChange(envObjToString(newCustomEnv)); + // Notify user that config was modified + toast.success( + t("geminiConfig.extractSuccess", { + defaultValue: "已提取通用配置,自定义配置已自动更新", + }), + ); } catch (error) { console.error("提取 Gemini 通用配置失败:", error); setCommonConfigError(