mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
refactor(ui): migrate remaining components to React Query
- Migrate workspace-picker-modal to useWorkspaceDirectories query - Migrate session-manager to useSessions query - Migrate git-diff-panel to useGitDiffs query - Migrate prompt-list to useIdeationPrompts query - Migrate spec-view hooks to useSpecFile query and spec mutations - Migrate use-board-background-settings to useProjectSettings query - Migrate use-guided-prompts to useIdeationPrompts query - Migrate use-project-settings-loader to React Query - Complete React Query migration across all components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,36 +1,26 @@
|
||||
import { useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('BoardBackground');
|
||||
import { useUpdateProjectSettings } from '@/hooks/mutations';
|
||||
|
||||
/**
|
||||
* Hook for managing board background settings with automatic persistence to server
|
||||
* Hook for managing board background settings with automatic persistence to server.
|
||||
* Uses React Query mutation for server persistence with automatic error handling.
|
||||
*/
|
||||
export function useBoardBackgroundSettings() {
|
||||
const store = useAppStore();
|
||||
const httpClient = getHttpApiClient();
|
||||
|
||||
// Get the mutation without a fixed project path - we'll pass it with each call
|
||||
const updateProjectSettings = useUpdateProjectSettings();
|
||||
|
||||
// Helper to persist settings to server
|
||||
const persistSettings = useCallback(
|
||||
async (projectPath: string, settingsToUpdate: Record<string, unknown>) => {
|
||||
try {
|
||||
const result = await httpClient.settings.updateProject(projectPath, {
|
||||
boardBackground: settingsToUpdate,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
logger.error('Failed to persist settings:', result.error);
|
||||
toast.error('Failed to save settings');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to persist settings:', error);
|
||||
toast.error('Failed to save settings');
|
||||
}
|
||||
(projectPath: string, settingsToUpdate: Record<string, unknown>) => {
|
||||
updateProjectSettings.mutate({
|
||||
projectPath,
|
||||
settings: { boardBackground: settingsToUpdate },
|
||||
});
|
||||
},
|
||||
[httpClient]
|
||||
[updateProjectSettings]
|
||||
);
|
||||
|
||||
// Get current background settings for a project
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
* Hook for fetching guided prompts from the backend API
|
||||
*
|
||||
* This hook provides the single source of truth for guided prompts,
|
||||
* fetched from the backend /api/ideation/prompts endpoint.
|
||||
* with caching via React Query.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { IdeationPrompt, PromptCategory, IdeaCategory } from '@automaker/types';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { useIdeationPrompts } from '@/hooks/queries';
|
||||
|
||||
interface UseGuidedPromptsReturn {
|
||||
prompts: IdeationPrompt[];
|
||||
@@ -21,36 +21,10 @@ interface UseGuidedPromptsReturn {
|
||||
}
|
||||
|
||||
export function useGuidedPrompts(): UseGuidedPromptsReturn {
|
||||
const [prompts, setPrompts] = useState<IdeationPrompt[]>([]);
|
||||
const [categories, setCategories] = useState<PromptCategory[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { data, isLoading, error, refetch } = useIdeationPrompts();
|
||||
|
||||
const fetchPrompts = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.ideation?.getPrompts();
|
||||
|
||||
if (result?.success) {
|
||||
setPrompts(result.prompts || []);
|
||||
setCategories(result.categories || []);
|
||||
} else {
|
||||
setError(result?.error || 'Failed to fetch prompts');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch guided prompts:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch prompts');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPrompts();
|
||||
}, [fetchPrompts]);
|
||||
const prompts = data?.prompts ?? [];
|
||||
const categories = data?.categories ?? [];
|
||||
|
||||
const getPromptsByCategory = useCallback(
|
||||
(category: IdeaCategory): IdeationPrompt[] => {
|
||||
@@ -73,12 +47,23 @@ export function useGuidedPrompts(): UseGuidedPromptsReturn {
|
||||
[categories]
|
||||
);
|
||||
|
||||
// Convert async refetch to match the expected interface
|
||||
const handleRefetch = useCallback(async () => {
|
||||
await refetch();
|
||||
}, [refetch]);
|
||||
|
||||
// Convert error to string for backward compatibility
|
||||
const errorMessage = useMemo(() => {
|
||||
if (!error) return null;
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}, [error]);
|
||||
|
||||
return {
|
||||
prompts,
|
||||
categories,
|
||||
isLoading,
|
||||
error,
|
||||
refetch: fetchPrompts,
|
||||
error: errorMessage,
|
||||
refetch: handleRefetch,
|
||||
getPromptsByCategory,
|
||||
getPromptById,
|
||||
getCategoryById,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { useProjectSettings } from '@/hooks/queries';
|
||||
|
||||
/**
|
||||
* Hook that loads project settings from the server when the current project changes.
|
||||
* This ensures that settings like board backgrounds are properly restored when
|
||||
* switching between projects or restarting the app.
|
||||
*
|
||||
* Uses React Query for data fetching with automatic caching.
|
||||
*/
|
||||
export function useProjectSettingsLoader() {
|
||||
const currentProject = useAppStore((state) => state.currentProject);
|
||||
@@ -24,93 +26,84 @@ export function useProjectSettingsLoader() {
|
||||
(state) => state.setAutoDismissInitScriptIndicator
|
||||
);
|
||||
|
||||
const loadingRef = useRef<string | null>(null);
|
||||
const currentProjectRef = useRef<string | null>(null);
|
||||
const appliedProjectRef = useRef<string | null>(null);
|
||||
|
||||
// Fetch project settings with React Query
|
||||
const { data: settings } = useProjectSettings(currentProject?.path);
|
||||
|
||||
// Apply settings when data changes
|
||||
useEffect(() => {
|
||||
currentProjectRef.current = currentProject?.path ?? null;
|
||||
|
||||
if (!currentProject?.path) {
|
||||
if (!currentProject?.path || !settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent loading the same project multiple times
|
||||
if (loadingRef.current === currentProject.path) {
|
||||
// Prevent applying the same settings multiple times
|
||||
if (appliedProjectRef.current === currentProject.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingRef.current = currentProject.path;
|
||||
const requestedProjectPath = currentProject.path;
|
||||
appliedProjectRef.current = currentProject.path;
|
||||
const projectPath = currentProject.path;
|
||||
|
||||
const loadProjectSettings = async () => {
|
||||
try {
|
||||
const httpClient = getHttpApiClient();
|
||||
const result = await httpClient.settings.getProject(requestedProjectPath);
|
||||
const bg = settings.boardBackground;
|
||||
|
||||
// Race condition protection: ignore stale results if project changed
|
||||
if (currentProjectRef.current !== requestedProjectPath) {
|
||||
return;
|
||||
}
|
||||
// Apply boardBackground if present
|
||||
if (bg?.imagePath) {
|
||||
setBoardBackground(projectPath, bg.imagePath);
|
||||
}
|
||||
|
||||
if (result.success && result.settings) {
|
||||
const bg = result.settings.boardBackground;
|
||||
// Settings map for cleaner iteration
|
||||
const settingsMap = {
|
||||
cardOpacity: setCardOpacity,
|
||||
columnOpacity: setColumnOpacity,
|
||||
columnBorderEnabled: setColumnBorderEnabled,
|
||||
cardGlassmorphism: setCardGlassmorphism,
|
||||
cardBorderEnabled: setCardBorderEnabled,
|
||||
cardBorderOpacity: setCardBorderOpacity,
|
||||
hideScrollbar: setHideScrollbar,
|
||||
} as const;
|
||||
|
||||
// Apply boardBackground if present
|
||||
if (bg?.imagePath) {
|
||||
setBoardBackground(requestedProjectPath, bg.imagePath);
|
||||
}
|
||||
|
||||
// Settings map for cleaner iteration
|
||||
const settingsMap = {
|
||||
cardOpacity: setCardOpacity,
|
||||
columnOpacity: setColumnOpacity,
|
||||
columnBorderEnabled: setColumnBorderEnabled,
|
||||
cardGlassmorphism: setCardGlassmorphism,
|
||||
cardBorderEnabled: setCardBorderEnabled,
|
||||
cardBorderOpacity: setCardBorderOpacity,
|
||||
hideScrollbar: setHideScrollbar,
|
||||
} as const;
|
||||
|
||||
// Apply all settings that are defined
|
||||
for (const [key, setter] of Object.entries(settingsMap)) {
|
||||
const value = bg?.[key as keyof typeof bg];
|
||||
if (value !== undefined) {
|
||||
(setter as (path: string, val: typeof value) => void)(requestedProjectPath, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply worktreePanelVisible if present
|
||||
if (result.settings.worktreePanelVisible !== undefined) {
|
||||
setWorktreePanelVisible(requestedProjectPath, result.settings.worktreePanelVisible);
|
||||
}
|
||||
|
||||
// Apply showInitScriptIndicator if present
|
||||
if (result.settings.showInitScriptIndicator !== undefined) {
|
||||
setShowInitScriptIndicator(
|
||||
requestedProjectPath,
|
||||
result.settings.showInitScriptIndicator
|
||||
);
|
||||
}
|
||||
|
||||
// Apply defaultDeleteBranch if present
|
||||
if (result.settings.defaultDeleteBranch !== undefined) {
|
||||
setDefaultDeleteBranch(requestedProjectPath, result.settings.defaultDeleteBranch);
|
||||
}
|
||||
|
||||
// Apply autoDismissInitScriptIndicator if present
|
||||
if (result.settings.autoDismissInitScriptIndicator !== undefined) {
|
||||
setAutoDismissInitScriptIndicator(
|
||||
requestedProjectPath,
|
||||
result.settings.autoDismissInitScriptIndicator
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load project settings:', error);
|
||||
// Don't show error toast - just log it
|
||||
// Apply all settings that are defined
|
||||
for (const [key, setter] of Object.entries(settingsMap)) {
|
||||
const value = bg?.[key as keyof typeof bg];
|
||||
if (value !== undefined) {
|
||||
(setter as (path: string, val: typeof value) => void)(projectPath, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
loadProjectSettings();
|
||||
}, [currentProject?.path]);
|
||||
// Apply worktreePanelVisible if present
|
||||
if (settings.worktreePanelVisible !== undefined) {
|
||||
setWorktreePanelVisible(projectPath, settings.worktreePanelVisible);
|
||||
}
|
||||
|
||||
// Apply showInitScriptIndicator if present
|
||||
if (settings.showInitScriptIndicator !== undefined) {
|
||||
setShowInitScriptIndicator(projectPath, settings.showInitScriptIndicator);
|
||||
}
|
||||
|
||||
// Apply defaultDeleteBranchWithWorktree if present
|
||||
if (settings.defaultDeleteBranchWithWorktree !== undefined) {
|
||||
setDefaultDeleteBranch(projectPath, settings.defaultDeleteBranchWithWorktree);
|
||||
}
|
||||
|
||||
// Apply autoDismissInitScriptIndicator if present
|
||||
if (settings.autoDismissInitScriptIndicator !== undefined) {
|
||||
setAutoDismissInitScriptIndicator(projectPath, settings.autoDismissInitScriptIndicator);
|
||||
}
|
||||
}, [
|
||||
currentProject?.path,
|
||||
settings,
|
||||
setBoardBackground,
|
||||
setCardOpacity,
|
||||
setColumnOpacity,
|
||||
setColumnBorderEnabled,
|
||||
setCardGlassmorphism,
|
||||
setCardBorderEnabled,
|
||||
setCardBorderOpacity,
|
||||
setHideScrollbar,
|
||||
setWorktreePanelVisible,
|
||||
setShowInitScriptIndicator,
|
||||
setDefaultDeleteBranch,
|
||||
setAutoDismissInitScriptIndicator,
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user