feat(settings): add autoSaveSettings for lightweight auto-save
Add optimized auto-save function for General tab settings. - Add autoSaveSettings method for non-destructive auto-save - Only trigger system APIs when values actually change - Avoid unnecessary auto-launch and plugin config updates - Update tests to cover new functionality
This commit is contained in:
@@ -37,6 +37,9 @@ export interface UseSettingsResult {
|
||||
overrides?: Partial<SettingsFormState>,
|
||||
options?: { silent?: boolean },
|
||||
) => Promise<SaveResult | null>;
|
||||
autoSaveSettings: (
|
||||
updates: Partial<SettingsFormState>,
|
||||
) => Promise<SaveResult | null>;
|
||||
resetSettings: () => void;
|
||||
acknowledgeRestart: () => void;
|
||||
}
|
||||
@@ -117,7 +120,82 @@ export function useSettings(): UseSettingsResult {
|
||||
setRequiresRestart,
|
||||
]);
|
||||
|
||||
// 保存设置
|
||||
// 即时保存设置(用于 General 标签页的实时更新)
|
||||
// 保存基础配置 + 独立的系统 API 调用(开机自启)
|
||||
const autoSaveSettings = useCallback(
|
||||
async (updates: Partial<SettingsFormState>): Promise<SaveResult | null> => {
|
||||
const mergedSettings = settings ? { ...settings, ...updates } : null;
|
||||
if (!mergedSettings) return null;
|
||||
|
||||
try {
|
||||
const sanitizedClaudeDir = sanitizeDir(mergedSettings.claudeConfigDir);
|
||||
const sanitizedCodexDir = sanitizeDir(mergedSettings.codexConfigDir);
|
||||
const sanitizedGeminiDir = sanitizeDir(mergedSettings.geminiConfigDir);
|
||||
|
||||
const payload: Settings = {
|
||||
...mergedSettings,
|
||||
claudeConfigDir: sanitizedClaudeDir,
|
||||
codexConfigDir: sanitizedCodexDir,
|
||||
geminiConfigDir: sanitizedGeminiDir,
|
||||
language: mergedSettings.language,
|
||||
};
|
||||
|
||||
// 保存到配置文件
|
||||
await saveMutation.mutateAsync(payload);
|
||||
|
||||
// 如果开机自启状态改变,调用系统 API
|
||||
if (
|
||||
payload.launchOnStartup !== undefined &&
|
||||
payload.launchOnStartup !== data?.launchOnStartup
|
||||
) {
|
||||
try {
|
||||
await settingsApi.setAutoLaunch(payload.launchOnStartup);
|
||||
} catch (error) {
|
||||
console.error("Failed to update auto-launch:", error);
|
||||
toast.error(
|
||||
t("settings.autoLaunchFailed", {
|
||||
defaultValue: "设置开机自启失败",
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 持久化语言偏好
|
||||
try {
|
||||
if (typeof window !== "undefined" && updates.language) {
|
||||
window.localStorage.setItem("language", updates.language);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[useSettings] Failed to persist language preference",
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
// 更新托盘菜单
|
||||
try {
|
||||
await providersApi.updateTrayMenu();
|
||||
} catch (error) {
|
||||
console.warn("[useSettings] Failed to refresh tray menu", error);
|
||||
}
|
||||
|
||||
return { requiresRestart: false };
|
||||
} catch (error) {
|
||||
console.error("[useSettings] Failed to auto-save settings", error);
|
||||
toast.error(
|
||||
t("notifications.settingsSaveFailed", {
|
||||
defaultValue: "保存设置失败: {{error}}",
|
||||
error: (error as Error)?.message ?? String(error),
|
||||
}),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[data, saveMutation, settings, t],
|
||||
);
|
||||
|
||||
// 完整保存设置(用于 Advanced 标签页的手动保存)
|
||||
// 包含所有系统 API 调用和完整的验证流程
|
||||
const saveSettings = useCallback(
|
||||
async (
|
||||
overrides?: Partial<SettingsFormState>,
|
||||
@@ -147,8 +225,11 @@ export function useSettings(): UseSettingsResult {
|
||||
|
||||
await settingsApi.setAppConfigDirOverride(sanitizedAppDir ?? null);
|
||||
|
||||
// 如果开机自启状态改变,调用系统 API
|
||||
if (payload.launchOnStartup !== undefined) {
|
||||
// 只在开机自启状态真正改变时调用系统 API
|
||||
if (
|
||||
payload.launchOnStartup !== undefined &&
|
||||
payload.launchOnStartup !== data?.launchOnStartup
|
||||
) {
|
||||
try {
|
||||
await settingsApi.setAutoLaunch(payload.launchOnStartup);
|
||||
} catch (error) {
|
||||
@@ -161,22 +242,29 @@ export function useSettings(): UseSettingsResult {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (payload.enableClaudePluginIntegration) {
|
||||
await settingsApi.applyClaudePluginConfig({ official: false });
|
||||
} else {
|
||||
await settingsApi.applyClaudePluginConfig({ official: true });
|
||||
// 只在 Claude 插件集成状态真正改变时调用系统 API
|
||||
if (
|
||||
payload.enableClaudePluginIntegration !== undefined &&
|
||||
payload.enableClaudePluginIntegration !==
|
||||
data?.enableClaudePluginIntegration
|
||||
) {
|
||||
try {
|
||||
if (payload.enableClaudePluginIntegration) {
|
||||
await settingsApi.applyClaudePluginConfig({ official: false });
|
||||
} else {
|
||||
await settingsApi.applyClaudePluginConfig({ official: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[useSettings] Failed to sync Claude plugin config",
|
||||
error,
|
||||
);
|
||||
toast.error(
|
||||
t("notifications.syncClaudePluginFailed", {
|
||||
defaultValue: "同步 Claude 插件失败",
|
||||
}),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[useSettings] Failed to sync Claude plugin config",
|
||||
error,
|
||||
);
|
||||
toast.error(
|
||||
t("notifications.syncClaudePluginFailed", {
|
||||
defaultValue: "同步 Claude 插件失败",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -268,6 +356,7 @@ export function useSettings(): UseSettingsResult {
|
||||
resetDirectory,
|
||||
resetAppConfigDir,
|
||||
saveSettings,
|
||||
autoSaveSettings,
|
||||
resetSettings,
|
||||
acknowledgeRestart,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user