Reduce API calls on FE (#258)

* remove unnecessary /branches call

* remove unnecessary dependency on active tab and user selected tab

* remove duplicate fetch attempt data and execution state
This commit is contained in:
Anastasiia Solop
2025-07-19 16:02:03 +02:00
committed by GitHub
parent 975fc901e1
commit 0e91ecd016
4 changed files with 9 additions and 69 deletions

View File

@@ -38,19 +38,13 @@ const TaskDetailsProvider: FC<{
task: TaskWithAttemptStatus; task: TaskWithAttemptStatus;
projectId: string; projectId: string;
children: ReactNode; children: ReactNode;
activeTab: 'logs' | 'diffs' | 'related';
setActiveTab: Dispatch<SetStateAction<'logs' | 'diffs' | 'related'>>;
setShowEditorDialog: Dispatch<SetStateAction<boolean>>; setShowEditorDialog: Dispatch<SetStateAction<boolean>>;
userSelectedTab: boolean;
projectHasDevScript?: boolean; projectHasDevScript?: boolean;
}> = ({ }> = ({
task, task,
projectId, projectId,
children, children,
activeTab,
setActiveTab,
setShowEditorDialog, setShowEditorDialog,
userSelectedTab,
projectHasDevScript, projectHasDevScript,
}) => { }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -84,7 +78,6 @@ const TaskDetailsProvider: FC<{
allLogs: [], // new field for all logs allLogs: [], // new field for all logs
}); });
const diffLoadingRef = useRef(false);
const relatedTasksLoadingRef = useRef(false); const relatedTasksLoadingRef = useRef(false);
const fetchRelatedTasks = useCallback(async () => { const fetchRelatedTasks = useCallback(async () => {
@@ -127,12 +120,6 @@ const TaskDetailsProvider: FC<{
return; return;
} }
// Prevent multiple concurrent requests
if (diffLoadingRef.current) {
return;
}
diffLoadingRef.current = true;
if (isBackgroundRefresh) { if (isBackgroundRefresh) {
setIsBackgroundRefreshing(true); setIsBackgroundRefreshing(true);
} else { } else {
@@ -154,7 +141,6 @@ const TaskDetailsProvider: FC<{
console.error('Failed to load diff:', err); console.error('Failed to load diff:', err);
setDiffError('Failed to load diff'); setDiffError('Failed to load diff');
} finally { } finally {
diffLoadingRef.current = false;
if (isBackgroundRefresh) { if (isBackgroundRefresh) {
setIsBackgroundRefreshing(false); setIsBackgroundRefreshing(false);
} else { } else {
@@ -165,10 +151,6 @@ const TaskDetailsProvider: FC<{
[projectId, selectedAttempt?.id, selectedAttempt?.task_id] [projectId, selectedAttempt?.id, selectedAttempt?.task_id]
); );
useEffect(() => {
fetchDiff();
}, [fetchDiff]);
useEffect(() => { useEffect(() => {
if (selectedAttempt && task) { if (selectedAttempt && task) {
fetchRelatedTasks(); fetchRelatedTasks();
@@ -318,7 +300,7 @@ const TaskDetailsProvider: FC<{
fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id); fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id);
fetchExecutionState(selectedAttempt.id, selectedAttempt.task_id); fetchExecutionState(selectedAttempt.id, selectedAttempt.task_id);
} }
}, 2000); }, 5000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [ }, [
@@ -355,32 +337,12 @@ const TaskDetailsProvider: FC<{
useEffect(() => { useEffect(() => {
if (!executionState?.execution_state || !selectedAttempt) return; if (!executionState?.execution_state || !selectedAttempt) return;
const isCodingAgentComplete = fetchDiff();
executionState.execution_state === 'CodingAgentComplete';
const isCodingAgentFailed =
executionState.execution_state === 'CodingAgentFailed';
const isComplete = executionState.execution_state === 'Complete';
const hasChanges = executionState.has_changes;
// Fetch diff when coding agent completes, fails, or task is complete and has changes
if (
(isCodingAgentComplete || isCodingAgentFailed || isComplete) &&
hasChanges
) {
fetchDiff();
// Auto-switch to diffs tab when changes are detected, but only if user hasn't manually selected a tab
if (activeTab === 'logs' && !userSelectedTab) {
setActiveTab('diffs');
}
}
}, [ }, [
executionState?.execution_state, executionState?.execution_state,
executionState?.has_changes, executionState?.has_changes,
selectedAttempt, selectedAttempt,
fetchDiff, fetchDiff,
activeTab,
userSelectedTab,
setActiveTab,
]); ]);
const value = useMemo( const value = useMemo(

View File

@@ -8,10 +8,9 @@ import {
type Props = { type Props = {
activeTab: 'logs' | 'diffs' | 'related'; activeTab: 'logs' | 'diffs' | 'related';
setActiveTab: (tab: 'logs' | 'diffs' | 'related') => void; setActiveTab: (tab: 'logs' | 'diffs' | 'related') => void;
setUserSelectedTab: (tab: boolean) => void;
}; };
function TabNavigation({ activeTab, setActiveTab, setUserSelectedTab }: Props) { function TabNavigation({ activeTab, setActiveTab }: Props) {
const { diff } = useContext(TaskDiffContext); const { diff } = useContext(TaskDiffContext);
const { totalRelatedCount } = useContext(TaskRelatedTasksContext); const { totalRelatedCount } = useContext(TaskRelatedTasksContext);
return ( return (
@@ -19,9 +18,7 @@ function TabNavigation({ activeTab, setActiveTab, setUserSelectedTab }: Props) {
<div className="flex px-4"> <div className="flex px-4">
<button <button
onClick={() => { onClick={() => {
console.log('Logs tab clicked - setting activeTab to logs');
setActiveTab('logs'); setActiveTab('logs');
setUserSelectedTab(true);
}} }}
className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${ className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'logs' activeTab === 'logs'
@@ -34,9 +31,7 @@ function TabNavigation({ activeTab, setActiveTab, setUserSelectedTab }: Props) {
</button> </button>
<button <button
onClick={() => { onClick={() => {
console.log('Diffs tab clicked - setting activeTab to diffs');
setActiveTab('diffs'); setActiveTab('diffs');
setUserSelectedTab(true);
}} }}
className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${ className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'diffs' activeTab === 'diffs'
@@ -54,11 +49,7 @@ function TabNavigation({ activeTab, setActiveTab, setUserSelectedTab }: Props) {
</button> </button>
<button <button
onClick={() => { onClick={() => {
console.log(
'Related Tasks tab clicked - setting activeTab to related'
);
setActiveTab('related'); setActiveTab('related');
setUserSelectedTab(true);
}} }}
className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${ className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'related' activeTab === 'related'

View File

@@ -40,13 +40,11 @@ export function TaskDetailsPanel({
const [activeTab, setActiveTab] = useState<'logs' | 'diffs' | 'related'>( const [activeTab, setActiveTab] = useState<'logs' | 'diffs' | 'related'>(
'logs' 'logs'
); );
const [userSelectedTab, setUserSelectedTab] = useState<boolean>(false);
// Reset to logs tab when task changes // Reset to logs tab when task changes
useEffect(() => { useEffect(() => {
if (task?.id) { if (task?.id) {
setActiveTab('logs'); setActiveTab('logs');
setUserSelectedTab(true); // Treat this as a user selection to prevent auto-switching
} }
}, [task?.id]); }, [task?.id]);
@@ -74,9 +72,6 @@ export function TaskDetailsPanel({
task={task} task={task}
projectId={projectId} projectId={projectId}
setShowEditorDialog={setShowEditorDialog} setShowEditorDialog={setShowEditorDialog}
activeTab={activeTab}
setActiveTab={setActiveTab}
userSelectedTab={userSelectedTab}
projectHasDevScript={projectHasDevScript} projectHasDevScript={projectHasDevScript}
> >
{/* Backdrop - only on smaller screens (overlay mode) */} {/* Backdrop - only on smaller screens (overlay mode) */}
@@ -96,7 +91,6 @@ export function TaskDetailsPanel({
<TabNavigation <TabNavigation
activeTab={activeTab} activeTab={activeTab}
setActiveTab={setActiveTab} setActiveTab={setActiveTab}
setUserSelectedTab={setUserSelectedTab}
/> />
{/* Tab Content */} {/* Tab Content */}

View File

@@ -5,13 +5,12 @@ import { Button } from '@/components/ui/button';
import { useConfig } from '@/components/config-provider'; import { useConfig } from '@/components/config-provider';
import { attemptsApi, projectsApi } from '@/lib/api'; import { attemptsApi, projectsApi } from '@/lib/api';
import type { GitBranch, TaskAttempt } from 'shared/types'; import type { GitBranch, TaskAttempt } from 'shared/types';
import { EXECUTOR_TYPES, EXECUTOR_LABELS } from 'shared/types'; import { EXECUTOR_LABELS, EXECUTOR_TYPES } from 'shared/types';
import { import {
TaskAttemptDataContext, TaskAttemptDataContext,
TaskAttemptLoadingContext, TaskAttemptLoadingContext,
TaskAttemptStoppingContext, TaskAttemptStoppingContext,
TaskDetailsContext, TaskDetailsContext,
TaskExecutionStateContext,
TaskSelectedAttemptContext, TaskSelectedAttemptContext,
} from '@/components/context/taskDetailsContext.ts'; } from '@/components/context/taskDetailsContext.ts';
import CreatePRDialog from '@/components/tasks/Toolbar/CreatePRDialog.tsx'; import CreatePRDialog from '@/components/tasks/Toolbar/CreatePRDialog.tsx';
@@ -31,10 +30,9 @@ function TaskDetailsToolbar() {
); );
const { isStopping } = useContext(TaskAttemptStoppingContext); const { isStopping } = useContext(TaskAttemptStoppingContext);
const { fetchAttemptData, setAttemptData, isAttemptRunning } = useContext( const { setAttemptData, isAttemptRunning } = useContext(
TaskAttemptDataContext TaskAttemptDataContext
); );
const { fetchExecutionState } = useContext(TaskExecutionStateContext);
const [taskAttempts, setTaskAttempts] = useState<TaskAttempt[]>([]); const [taskAttempts, setTaskAttempts] = useState<TaskAttempt[]>([]);
const location = useLocation(); const location = useLocation();
@@ -67,10 +65,10 @@ function TaskDetailsToolbar() {
setBranches(result); setBranches(result);
// Set current branch as default // Set current branch as default
const currentBranch = result.find((b) => b.is_current); const currentBranch = result.find((b) => b.is_current);
if (currentBranch && !selectedBranch) { if (currentBranch) {
setSelectedBranch(currentBranch.name); setSelectedBranch((prev) => (!prev ? currentBranch.name : prev));
} }
}, [projectId, selectedBranch]); }, [projectId]);
useEffect(() => { useEffect(() => {
fetchProjectBranches(); fetchProjectBranches();
@@ -163,11 +161,6 @@ function TaskDetailsToolbar() {
return prev; return prev;
return selectedAttemptToUse; return selectedAttemptToUse;
}); });
fetchAttemptData(selectedAttemptToUse.id, selectedAttemptToUse.task_id);
fetchExecutionState(
selectedAttemptToUse.id,
selectedAttemptToUse.task_id
);
} else { } else {
setSelectedAttempt(null); setSelectedAttempt(null);
setAttemptData({ setAttemptData({
@@ -181,7 +174,7 @@ function TaskDetailsToolbar() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [task, projectId, fetchAttemptData, fetchExecutionState, location.search]); }, [task, projectId, location.search]);
useEffect(() => { useEffect(() => {
fetchTaskAttempts(); fetchTaskAttempts();