fix(settings): move the data-dir baseline after a successful save

The settings page no longer unmounts after saving, but the saved
app-config-dir baseline was only captured on mount. After saving a new
data directory and choosing "restart later" the save button stayed,
resetting to the old value could not be saved, and every later save
reported "restart required" again. Keep the baseline in state and commit
it to the just-saved value once the override is persisted.
This commit is contained in:
Jason committed 2026-10-03 23:29:00 +08:00
1 parent 485a3b69de
commit 92e6f3b975
4 files changed
+44 -2

No files matched your search

+14 -2
View File
@@ -102,6 +102,8 @@ export interface UseDirectorySettingsResult {
resolvedDirs: ResolvedDirectories;
isLoading: boolean;
initialAppConfigDir?: string;
/** 保存成功后把已保存的数据目录换成刚存的值 */
commitAppConfigDir: (value: string | undefined) => void;
updateDirectory: (app: DirectoryAppId, value?: string) => void;
updateAppConfigDir: (value?: string) => void;
browseDirectory: (app: DirectoryAppId) => Promise<void>;
@@ -157,7 +159,16 @@ export function useDirectorySettings({
hermes: "",
pi: "",
});
// 已保存的数据目录(保存按钮和「需要重启」都和它比)。ref 给回调用,state 让比较它的
// 组件在保存后重渲染:保存成功要把它更新成刚存的值,否则保存按钮一直在、改回去存不了
const initialAppConfigDirRef = useRef<string | undefined>(undefined);
const [initialAppConfigDir, setInitialAppConfigDir] = useState<
string | undefined
>(undefined);
const commitAppConfigDir = useCallback((value: string | undefined) => {
initialAppConfigDirRef.current = value;
setInitialAppConfigDir(value);
}, []);
// 加载目录信息
useEffect(() => {
@@ -223,7 +234,7 @@ export function useDirectorySettings({
};
setAppConfigDir(normalizedOverride);
initialAppConfigDirRef.current = normalizedOverride;
commitAppConfigDir(normalizedOverride);
setResolvedDirs({
appConfig: normalizedOverride ?? defaultsRef.current.appConfig,
@@ -387,7 +398,8 @@ export function useDirectorySettings({
appConfigDir,
resolvedDirs,
isLoading,
initialAppConfigDir: initialAppConfigDirRef.current,
initialAppConfigDir,
commitAppConfigDir,
updateDirectory,
updateAppConfigDir,
browseDirectory,
+4
View File
@@ -87,6 +87,7 @@ export function useSettings(): UseSettingsResult {
resolvedDirs,
isLoading: isDirectoryLoading,
initialAppConfigDir,
commitAppConfigDir,
updateDirectory,
updateAppConfigDir,
browseDirectory,
@@ -373,6 +374,8 @@ export function useSettings(): UseSettingsResult {
await saveMutation.mutateAsync(payload);
await settingsApi.setAppConfigDirOverride(sanitizedAppDir ?? null);
// 基准值换成刚存的:设置页不卸载,下次比较和「需要重启」都只跟真正没保存的改动走
commitAppConfigDir(sanitizedAppDir);
// 只在开机自启状态真正改变时调用系统 API
if (
@@ -496,6 +499,7 @@ export function useSettings(): UseSettingsResult {
},
[
appConfigDir,
commitAppConfigDir,
data,
initialAppConfigDir,
queryClient,
+21
View File
@@ -170,6 +170,27 @@ describe("useDirectorySettings", () => {
});
});
it("moves the saved baseline when a new app config dir is committed", async () => {
getAppConfigDirOverrideMock.mockResolvedValue("/override/app");
const { result } = renderHook(() =>
useDirectorySettings({
settings: createSettings(),
onUpdateSettings: vi.fn(),
}),
);
await waitFor(() =>
expect(result.current.initialAppConfigDir).toBe("/override/app"),
);
act(() => result.current.commitAppConfigDir("/saved/app"));
expect(result.current.initialAppConfigDir).toBe("/saved/app");
// 重置回的也是新基准
act(() => result.current.updateAppConfigDir("/typed/app"));
act(() => result.current.resetAllDirectories());
expect(result.current.appConfigDir).toBe("/saved/app");
});
it("updates app config directory via browseAppConfigDir", async () => {
selectConfigDirectoryMock.mockResolvedValue(" /new/app ");
+5
View File
@@ -123,6 +123,7 @@ const createDirectorySettingsMock = (
},
isLoading: false,
initialAppConfigDir: undefined,
commitAppConfigDir: vi.fn(),
updateDirectory: vi.fn(),
updateAppConfigDir: vi.fn(),
browseDirectory: vi.fn(),
@@ -297,6 +298,10 @@ describe("useSettings hook", () => {
});
expect(saveResult).toEqual({ requiresRestart: true });
// 存完把基准值换成刚存的,保存按钮和下一次「需要重启」只跟新改动走
expect(directorySettingsMock.commitAppConfigDir).toHaveBeenCalledWith(
"/override/app",
);
expect(mutateAsyncMock).toHaveBeenCalledTimes(1);
const payload = mutateAsyncMock.mock.calls[0][0] as Settings;
expect(payload.claudeConfigDir).toBe("/custom/claude");