- Extract modal manager, app settings, deep-link handling, favorites, and auth onboarding into dedicated hooks - Rewire App.tsx to consume the new hooks and reduce component-level state/effect complexity - Preserve favorite-toggle error toast behavior in the updated favorites wiring - Add hook-level test coverage for modal manager, app settings, deep links, favorites, and auth onboarding flows
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import { useCallback, useEffect, useState } from "react";
|
|
import { fetchConfig, fetchSettings, updateSettings } from "../api";
|
|
|
|
/**
|
|
* Settings state and actions consumed by the dashboard App shell.
|
|
*/
|
|
export interface UseAppSettingsResult {
|
|
maxConcurrent: number;
|
|
rootDir: string;
|
|
autoMerge: boolean;
|
|
globalPaused: boolean;
|
|
enginePaused: boolean;
|
|
taskStuckTimeoutMs: number | undefined;
|
|
githubTokenConfigured: boolean;
|
|
toggleAutoMerge: () => Promise<void>;
|
|
toggleGlobalPause: () => Promise<void>;
|
|
toggleEnginePause: () => Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* Loads per-project dashboard settings and exposes optimistic toggle handlers.
|
|
*/
|
|
export function useAppSettings(projectId?: string): UseAppSettingsResult {
|
|
const [maxConcurrent, setMaxConcurrent] = useState(2);
|
|
const [rootDir, setRootDir] = useState<string>(".");
|
|
const [autoMerge, setAutoMerge] = useState(true);
|
|
const [globalPaused, setGlobalPaused] = useState(false);
|
|
const [enginePaused, setEnginePaused] = useState(false);
|
|
const [taskStuckTimeoutMs, setTaskStuckTimeoutMs] = useState<number | undefined>(undefined);
|
|
const [githubTokenConfigured, setGithubTokenConfigured] = useState(false);
|
|
|
|
useEffect(() => {
|
|
fetchConfig(projectId)
|
|
.then((cfg) => {
|
|
setMaxConcurrent(cfg.maxConcurrent);
|
|
setRootDir(cfg.rootDir);
|
|
})
|
|
.catch(() => {
|
|
// Keep defaults on fetch failure.
|
|
});
|
|
|
|
fetchSettings(projectId)
|
|
.then((settings) => {
|
|
setAutoMerge(Boolean(settings.autoMerge));
|
|
setGlobalPaused(Boolean(settings.globalPause));
|
|
setEnginePaused(Boolean(settings.enginePaused));
|
|
setGithubTokenConfigured(Boolean(settings.githubTokenConfigured));
|
|
setTaskStuckTimeoutMs(settings.taskStuckTimeoutMs);
|
|
})
|
|
.catch(() => {
|
|
// Keep defaults on fetch failure.
|
|
});
|
|
}, [projectId]);
|
|
|
|
const toggleAutoMerge = useCallback(async () => {
|
|
const next = !autoMerge;
|
|
setAutoMerge(next);
|
|
|
|
try {
|
|
await updateSettings({ autoMerge: next }, projectId);
|
|
} catch {
|
|
setAutoMerge(!next);
|
|
}
|
|
}, [autoMerge, projectId]);
|
|
|
|
const toggleGlobalPause = useCallback(async () => {
|
|
const next = !globalPaused;
|
|
setGlobalPaused(next);
|
|
|
|
try {
|
|
await updateSettings({ globalPause: next }, projectId);
|
|
} catch {
|
|
setGlobalPaused(!next);
|
|
}
|
|
}, [globalPaused, projectId]);
|
|
|
|
const toggleEnginePause = useCallback(async () => {
|
|
const next = !enginePaused;
|
|
setEnginePaused(next);
|
|
|
|
try {
|
|
await updateSettings({ enginePaused: next }, projectId);
|
|
} catch {
|
|
setEnginePaused(!next);
|
|
}
|
|
}, [enginePaused, projectId]);
|
|
|
|
return {
|
|
maxConcurrent,
|
|
rootDir,
|
|
autoMerge,
|
|
globalPaused,
|
|
enginePaused,
|
|
taskStuckTimeoutMs,
|
|
githubTokenConfigured,
|
|
toggleAutoMerge,
|
|
toggleGlobalPause,
|
|
toggleEnginePause,
|
|
};
|
|
}
|