Files
automaker/apps/ui/src/components/views/board-view/hooks/use-board-effects.ts
WebDevCody 98381441b9 feat: add GitHub issue fix command and release command
- Introduced a new command for fetching and validating GitHub issues, allowing users to address issues directly from the command line.
- Added a release command to bump the version of the application and build the Electron app, ensuring version consistency across UI and server packages.
- Updated package.json files for both UI and server to version 0.7.1, reflecting the latest changes.
- Implemented version utility in the server to read the version from package.json, enhancing version management across the application.
2025-12-31 23:24:01 -05:00

183 lines
5.6 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
interface UseBoardEffectsProps {
currentProject: { path: string; id: string } | null;
specCreatingForProject: string | null;
setSpecCreatingForProject: (path: string | null) => void;
setSuggestionsCount: (count: number) => void;
setFeatureSuggestions: (suggestions: any[]) => void;
setIsGeneratingSuggestions: (generating: boolean) => void;
checkContextExists: (featureId: string) => Promise<boolean>;
features: any[];
isLoading: boolean;
featuresWithContext: Set<string>;
setFeaturesWithContext: (set: Set<string>) => void;
}
export function useBoardEffects({
currentProject,
specCreatingForProject,
setSpecCreatingForProject,
setSuggestionsCount,
setFeatureSuggestions,
setIsGeneratingSuggestions,
checkContextExists,
features,
isLoading,
featuresWithContext,
setFeaturesWithContext,
}: UseBoardEffectsProps) {
// Keep a ref to the current featuresWithContext for use in event handlers
const featuresWithContextRef = useRef(featuresWithContext);
useEffect(() => {
featuresWithContextRef.current = featuresWithContext;
}, [featuresWithContext]);
// Make current project available globally for modal
useEffect(() => {
if (currentProject) {
(window as any).__currentProject = currentProject;
}
return () => {
(window as any).__currentProject = null;
};
}, [currentProject]);
// Listen for suggestions events to update count (persists even when dialog is closed)
useEffect(() => {
const api = getElectronAPI();
if (!api?.suggestions) return;
const unsubscribe = api.suggestions.onEvent((event) => {
if (event.type === 'suggestions_complete' && event.suggestions) {
setSuggestionsCount(event.suggestions.length);
setFeatureSuggestions(event.suggestions);
setIsGeneratingSuggestions(false);
} else if (event.type === 'suggestions_error') {
setIsGeneratingSuggestions(false);
}
});
return () => {
unsubscribe();
};
}, [setSuggestionsCount, setFeatureSuggestions, setIsGeneratingSuggestions]);
// Subscribe to spec regeneration events to clear creating state on completion
useEffect(() => {
const api = getElectronAPI();
if (!api.specRegeneration) return;
const unsubscribe = api.specRegeneration.onEvent((event) => {
console.log(
'[BoardView] Spec regeneration event:',
event.type,
'for project:',
event.projectPath
);
if (event.projectPath !== specCreatingForProject) {
return;
}
if (event.type === 'spec_regeneration_complete') {
setSpecCreatingForProject(null);
} else if (event.type === 'spec_regeneration_error') {
setSpecCreatingForProject(null);
}
});
return () => {
unsubscribe();
};
}, [specCreatingForProject, setSpecCreatingForProject]);
// Sync running tasks from electron backend on mount
useEffect(() => {
if (!currentProject) return;
const syncRunningTasks = async () => {
try {
const api = getElectronAPI();
if (!api?.autoMode?.status) return;
const status = await api.autoMode.status(currentProject.path);
if (status.success) {
const projectId = currentProject.id;
const { clearRunningTasks, addRunningTask } = useAppStore.getState();
if (status.runningFeatures) {
console.log('[Board] Syncing running tasks from backend:', status.runningFeatures);
clearRunningTasks(projectId);
status.runningFeatures.forEach((featureId: string) => {
addRunningTask(projectId, featureId);
});
}
}
} catch (error) {
console.error('[Board] Failed to sync running tasks:', error);
}
};
syncRunningTasks();
}, [currentProject]);
// Check which features have context files
useEffect(() => {
const checkAllContexts = async () => {
const featuresWithPotentialContext = features.filter(
(f) =>
f.status === 'in_progress' || f.status === 'waiting_approval' || f.status === 'verified'
);
const contextChecks = await Promise.all(
featuresWithPotentialContext.map(async (f) => ({
id: f.id,
hasContext: await checkContextExists(f.id),
}))
);
const newSet = new Set<string>();
contextChecks.forEach(({ id, hasContext }) => {
if (hasContext) {
newSet.add(id);
}
});
setFeaturesWithContext(newSet);
};
if (features.length > 0 && !isLoading) {
checkAllContexts();
}
}, [features, isLoading, checkContextExists, setFeaturesWithContext]);
// Re-check context when a feature stops, completes, or errors
// This ensures hasContext is updated even if the features array doesn't change
useEffect(() => {
const api = getElectronAPI();
if (!api?.autoMode) return;
const unsubscribe = api.autoMode.onEvent(async (event) => {
// When a feature stops (error/abort) or completes, re-check its context
if (
(event.type === 'auto_mode_error' || event.type === 'auto_mode_feature_complete') &&
event.featureId
) {
const hasContext = await checkContextExists(event.featureId);
if (hasContext) {
const newSet = new Set(featuresWithContextRef.current);
newSet.add(event.featureId);
setFeaturesWithContext(newSet);
}
}
});
return () => {
unsubscribe();
};
}, [checkContextExists, setFeaturesWithContext]);
}