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:
committed by
GitHub
parent
13fceaf77e
commit
a10d3f0854
@@ -104,7 +104,9 @@ export function AttemptHeaderCard({
|
|||||||
Open in IDE
|
Open in IDE
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={runningDevServer ? stopDevServer : startDevServer}
|
onClick={() =>
|
||||||
|
runningDevServer ? stopDevServer() : startDevServer()
|
||||||
|
}
|
||||||
disabled={!selectedAttempt}
|
disabled={!selectedAttempt}
|
||||||
className={runningDevServer ? 'text-destructive' : ''}
|
className={runningDevServer ? 'text-destructive' : ''}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -512,7 +512,9 @@ function CurrentAttempt({
|
|||||||
<Button
|
<Button
|
||||||
variant={runningDevServer ? 'destructive' : 'outline'}
|
variant={runningDevServer ? 'destructive' : 'outline'}
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={runningDevServer ? stopDevServer : startDevServer}
|
onClick={() =>
|
||||||
|
runningDevServer ? stopDevServer() : startDevServer()
|
||||||
|
}
|
||||||
disabled={isStartingDevServer || !projectHasDevScript}
|
disabled={isStartingDevServer || !projectHasDevScript}
|
||||||
className="gap-1 flex-1"
|
className="gap-1 flex-1"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export { useExecutionProcesses } from './useExecutionProcesses';
|
|
||||||
export { useBranchStatus } from './useBranchStatus';
|
export { useBranchStatus } from './useBranchStatus';
|
||||||
export { useAttemptExecution } from './useAttemptExecution';
|
export { useAttemptExecution } from './useAttemptExecution';
|
||||||
export { useOpenInEditor } from './useOpenInEditor';
|
export { useOpenInEditor } from './useOpenInEditor';
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
import { useCallback } from 'react';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { attemptsApi } from '@/lib/api';
|
import { attemptsApi, type Result } from '@/lib/api';
|
||||||
import type { CreateGitHubPrRequest } from 'shared/types';
|
import type { CreateGitHubPrRequest, GitHubServiceError } from 'shared/types';
|
||||||
|
|
||||||
export function useCreatePR(
|
export function useCreatePR(
|
||||||
attemptId: string | undefined,
|
attemptId: string | undefined,
|
||||||
onSuccess?: (prUrl?: string) => void,
|
onSuccess?: (prUrl?: string) => void,
|
||||||
onError?: (err: unknown) => void
|
onError?: (err: unknown) => void
|
||||||
) {
|
) {
|
||||||
return useCallback(
|
const queryClient = useQueryClient();
|
||||||
async (prData: CreateGitHubPrRequest) => {
|
|
||||||
if (!attemptId) return;
|
|
||||||
|
|
||||||
try {
|
return useMutation<
|
||||||
const result = await attemptsApi.createPR(attemptId, prData);
|
Result<string, GitHubServiceError>,
|
||||||
|
Error,
|
||||||
if (result.success) {
|
CreateGitHubPrRequest
|
||||||
onSuccess?.(result.data);
|
>({
|
||||||
return result.data;
|
mutationFn: async (prData: CreateGitHubPrRequest) => {
|
||||||
} else {
|
if (!attemptId)
|
||||||
throw result.error || new Error(result.message);
|
return { success: false, error: undefined, message: 'No attempt ID' };
|
||||||
}
|
return attemptsApi.createPR(attemptId, prData);
|
||||||
} catch (err) {
|
},
|
||||||
console.error('Failed to create PR:', err);
|
onSuccess: (result) => {
|
||||||
onError?.(err);
|
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);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { attemptsApi, executionProcessesApi } from '@/lib/api';
|
||||||
import { useAttemptExecution } from '@/hooks/useAttemptExecution';
|
import { useAttemptExecution } from '@/hooks/useAttemptExecution';
|
||||||
import type { ExecutionProcess } from 'shared/types';
|
import type { ExecutionProcess } from 'shared/types';
|
||||||
@@ -14,12 +15,11 @@ export function useDevServer(
|
|||||||
attemptId: string | undefined,
|
attemptId: string | undefined,
|
||||||
options?: UseDevServerOptions
|
options?: UseDevServerOptions
|
||||||
) {
|
) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const { attemptData } = useAttemptExecution(attemptId);
|
const { attemptData } = useAttemptExecution(attemptId);
|
||||||
const [isStarting, setIsStarting] = useState(false);
|
|
||||||
const [isStopping, setIsStopping] = useState(false);
|
|
||||||
|
|
||||||
// Find running dev server process
|
// Find running dev server process
|
||||||
const runningDevServer = useMemo((): ExecutionProcess | undefined => {
|
const runningDevServer = useMemo<ExecutionProcess | undefined>(() => {
|
||||||
return attemptData.processes.find(
|
return attemptData.processes.find(
|
||||||
(process) =>
|
(process) =>
|
||||||
process.run_reason === 'devserver' && process.status === 'running'
|
process.run_reason === 'devserver' && process.status === 'running'
|
||||||
@@ -27,7 +27,7 @@ export function useDevServer(
|
|||||||
}, [attemptData.processes]);
|
}, [attemptData.processes]);
|
||||||
|
|
||||||
// Find latest dev server process (for logs viewing)
|
// Find latest dev server process (for logs viewing)
|
||||||
const latestDevServerProcess = useMemo((): ExecutionProcess | undefined => {
|
const latestDevServerProcess = useMemo<ExecutionProcess | undefined>(() => {
|
||||||
return [...attemptData.processes]
|
return [...attemptData.processes]
|
||||||
.filter((process) => process.run_reason === 'devserver')
|
.filter((process) => process.run_reason === 'devserver')
|
||||||
.sort(
|
.sort(
|
||||||
@@ -36,41 +36,56 @@ export function useDevServer(
|
|||||||
)[0];
|
)[0];
|
||||||
}, [attemptData.processes]);
|
}, [attemptData.processes]);
|
||||||
|
|
||||||
const start = useCallback(async () => {
|
// Start mutation
|
||||||
if (!attemptId) return;
|
const startMutation = useMutation({
|
||||||
|
mutationKey: ['startDevServer', attemptId],
|
||||||
setIsStarting(true);
|
mutationFn: async () => {
|
||||||
try {
|
if (!attemptId) return;
|
||||||
await attemptsApi.startDevServer(attemptId);
|
await attemptsApi.startDevServer(attemptId);
|
||||||
|
},
|
||||||
|
onSuccess: async () => {
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ['executionProcesses', attemptId],
|
||||||
|
});
|
||||||
options?.onStartSuccess?.();
|
options?.onStartSuccess?.();
|
||||||
} catch (err) {
|
},
|
||||||
|
onError: (err) => {
|
||||||
console.error('Failed to start dev server:', err);
|
console.error('Failed to start dev server:', err);
|
||||||
options?.onStartError?.(err);
|
options?.onStartError?.(err);
|
||||||
} finally {
|
},
|
||||||
setIsStarting(false);
|
});
|
||||||
}
|
|
||||||
}, [attemptId, options?.onStartSuccess, options?.onStartError]);
|
|
||||||
|
|
||||||
const stop = useCallback(async () => {
|
// Stop mutation
|
||||||
if (!runningDevServer) return;
|
const stopMutation = useMutation({
|
||||||
|
mutationKey: ['stopDevServer', runningDevServer?.id],
|
||||||
setIsStopping(true);
|
mutationFn: async () => {
|
||||||
try {
|
if (!runningDevServer) return;
|
||||||
await executionProcessesApi.stopExecutionProcess(runningDevServer.id);
|
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?.();
|
options?.onStopSuccess?.();
|
||||||
} catch (err) {
|
},
|
||||||
|
onError: (err) => {
|
||||||
console.error('Failed to stop dev server:', err);
|
console.error('Failed to stop dev server:', err);
|
||||||
options?.onStopError?.(err);
|
options?.onStopError?.(err);
|
||||||
} finally {
|
},
|
||||||
setIsStopping(false);
|
});
|
||||||
}
|
|
||||||
}, [runningDevServer, options?.onStopSuccess, options?.onStopError]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start,
|
start: startMutation.mutate,
|
||||||
stop,
|
stop: stopMutation.mutate,
|
||||||
isStarting,
|
isStarting: startMutation.isPending,
|
||||||
isStopping,
|
isStopping: stopMutation.isPending,
|
||||||
runningDevServer,
|
runningDevServer,
|
||||||
latestDevServerProcess,
|
latestDevServerProcess,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user