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
This commit is contained in:
YoVinchen
2026-01-26 15:55:40 +08:00
parent cfab768f95
commit 0ecf6891c0
3 changed files with 48 additions and 12 deletions
@@ -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<void>>(Promise.resolve());
const enqueueSave = useCallback((saveFn: () => Promise<void>) => {
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) {
@@ -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<void>>(Promise.resolve());
const enqueueSave = useCallback((saveFn: () => Promise<void>) => {
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);
@@ -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<void>>(Promise.resolve());
const enqueueSave = useCallback((saveFn: () => Promise<void>) => {
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(