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:
@@ -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(
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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 */}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user