Louis/hooks (#599)

* Update useDevServer to use tanstack (vibe-kanban a5e7cb20)

We are migrating the frontend to use Tanstack query. We should make radical changes in order to improve readability and performance.

See an example in frontend/src/hooks/useRebase.ts

Please plan how to modernise the frontend/src/hooks/useDevServer.ts hook

* Deprecate useExecutionProcesses → Replace with useAttemptExecution (vibe-kanban 5123ff1e)
This commit is contained in:
Louis Knight-Webb
2025-09-01 17:17:48 +01:00
committed by GitHub
parent 13fceaf77e
commit a10d3f0854
6 changed files with 79 additions and 79 deletions

View File

@@ -104,7 +104,9 @@ export function AttemptHeaderCard({
Open in IDE
</DropdownMenuItem>
<DropdownMenuItem
onClick={runningDevServer ? stopDevServer : startDevServer}
onClick={() =>
runningDevServer ? stopDevServer() : startDevServer()
}
disabled={!selectedAttempt}
className={runningDevServer ? 'text-destructive' : ''}
>

View File

@@ -512,7 +512,9 @@ function CurrentAttempt({
<Button
variant={runningDevServer ? 'destructive' : 'outline'}
size="xs"
onClick={runningDevServer ? stopDevServer : startDevServer}
onClick={() =>
runningDevServer ? stopDevServer() : startDevServer()
}
disabled={isStartingDevServer || !projectHasDevScript}
className="gap-1 flex-1"
>

View File

@@ -1,4 +1,3 @@
export { useExecutionProcesses } from './useExecutionProcesses';
export { useBranchStatus } from './useBranchStatus';
export { useAttemptExecution } from './useAttemptExecution';
export { useOpenInEditor } from './useOpenInEditor';

View File

@@ -1,30 +1,39 @@
import { useCallback } from 'react';
import { attemptsApi } from '@/lib/api';
import type { CreateGitHubPrRequest } from 'shared/types';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { attemptsApi, type Result } from '@/lib/api';
import type { CreateGitHubPrRequest, GitHubServiceError } from 'shared/types';
export function useCreatePR(
attemptId: string | undefined,
onSuccess?: (prUrl?: string) => void,
onError?: (err: unknown) => void
) {
return useCallback(
async (prData: CreateGitHubPrRequest) => {
if (!attemptId) return;
const queryClient = useQueryClient();
try {
const result = await attemptsApi.createPR(attemptId, prData);
if (result.success) {
onSuccess?.(result.data);
return result.data;
} else {
throw result.error || new Error(result.message);
}
} catch (err) {
console.error('Failed to create PR:', err);
onError?.(err);
return useMutation<
Result<string, GitHubServiceError>,
Error,
CreateGitHubPrRequest
>({
mutationFn: async (prData: CreateGitHubPrRequest) => {
if (!attemptId)
return { success: false, error: undefined, message: 'No attempt ID' };
return attemptsApi.createPR(attemptId, prData);
},
onSuccess: (result) => {
if (result.success) {
queryClient.invalidateQueries({
queryKey: ['branchStatus', attemptId],
});
onSuccess?.(result.data);
} else {
throw (
result.error || new Error(result.message || 'Failed to create PR')
);
}
},
[attemptId, onSuccess, onError]
);
onError: (err) => {
console.error('Failed to create PR:', err);
onError?.(err);
},
});
}

View File

@@ -1,4 +1,5 @@
import { useCallback, useMemo, useState } from 'react';
import { useMemo } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { attemptsApi, executionProcessesApi } from '@/lib/api';
import { useAttemptExecution } from '@/hooks/useAttemptExecution';
import type { ExecutionProcess } from 'shared/types';
@@ -14,12 +15,11 @@ export function useDevServer(
attemptId: string | undefined,
options?: UseDevServerOptions
) {
const queryClient = useQueryClient();
const { attemptData } = useAttemptExecution(attemptId);
const [isStarting, setIsStarting] = useState(false);
const [isStopping, setIsStopping] = useState(false);
// Find running dev server process
const runningDevServer = useMemo((): ExecutionProcess | undefined => {
const runningDevServer = useMemo<ExecutionProcess | undefined>(() => {
return attemptData.processes.find(
(process) =>
process.run_reason === 'devserver' && process.status === 'running'
@@ -27,7 +27,7 @@ export function useDevServer(
}, [attemptData.processes]);
// Find latest dev server process (for logs viewing)
const latestDevServerProcess = useMemo((): ExecutionProcess | undefined => {
const latestDevServerProcess = useMemo<ExecutionProcess | undefined>(() => {
return [...attemptData.processes]
.filter((process) => process.run_reason === 'devserver')
.sort(
@@ -36,41 +36,56 @@ export function useDevServer(
)[0];
}, [attemptData.processes]);
const start = useCallback(async () => {
if (!attemptId) return;
setIsStarting(true);
try {
// Start mutation
const startMutation = useMutation({
mutationKey: ['startDevServer', attemptId],
mutationFn: async () => {
if (!attemptId) return;
await attemptsApi.startDevServer(attemptId);
},
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: ['executionProcesses', attemptId],
});
options?.onStartSuccess?.();
} catch (err) {
},
onError: (err) => {
console.error('Failed to start dev server:', err);
options?.onStartError?.(err);
} finally {
setIsStarting(false);
}
}, [attemptId, options?.onStartSuccess, options?.onStartError]);
},
});
const stop = useCallback(async () => {
if (!runningDevServer) return;
setIsStopping(true);
try {
// Stop mutation
const stopMutation = useMutation({
mutationKey: ['stopDevServer', runningDevServer?.id],
mutationFn: async () => {
if (!runningDevServer) return;
await executionProcessesApi.stopExecutionProcess(runningDevServer.id);
},
onSuccess: async () => {
await Promise.all([
queryClient.invalidateQueries({
queryKey: ['executionProcesses', attemptId],
}),
runningDevServer
? queryClient.invalidateQueries({
queryKey: ['processDetails', runningDevServer.id],
})
: Promise.resolve(),
]);
options?.onStopSuccess?.();
} catch (err) {
},
onError: (err) => {
console.error('Failed to stop dev server:', err);
options?.onStopError?.(err);
} finally {
setIsStopping(false);
}
}, [runningDevServer, options?.onStopSuccess, options?.onStopError]);
},
});
return {
start,
stop,
isStarting,
isStopping,
start: startMutation.mutate,
stop: stopMutation.mutate,
isStarting: startMutation.isPending,
isStopping: stopMutation.isPending,
runningDevServer,
latestDevServerProcess,
};

View File

@@ -1,27 +0,0 @@
import { useQuery } from '@tanstack/react-query';
import { executionProcessesApi } from '@/lib/api';
import type { ExecutionProcess } from 'shared/types';
export function useExecutionProcesses(attemptId?: string) {
const query = useQuery({
queryKey: ['executionProcesses', attemptId],
queryFn: () => executionProcessesApi.getExecutionProcesses(attemptId!),
enabled: !!attemptId,
refetchInterval: () => {
// Always poll every 5 seconds when enabled - we'll control this via enabled
return 5000;
},
select: (data) => ({
processes: data,
isAttemptRunning: data.some(
(process: ExecutionProcess) =>
(process.run_reason === 'codingagent' ||
process.run_reason === 'setupscript' ||
process.run_reason === 'cleanupscript') &&
process.status === 'running'
),
}),
});
return query;
}