1- import {
2- memo ,
3- useState ,
4- useEffect ,
5- useCallback ,
6- useMemo ,
7- useRef ,
8- type CSSProperties ,
9- } from "react" ;
1+ import { memo , useState , useEffect , useCallback , useMemo , useRef , type CSSProperties } from "react" ;
102import {
113 CheckCircle2 ,
124 GitFork ,
@@ -5821,6 +5813,8 @@ export default function App() {
58215813 const [ editingTask , setEditingTask ] = useState < any > ( null ) ;
58225814 const [ forkingTask , setForkingTask ] = useState < any > ( null ) ;
58235815 const [ editingHeartbeat , setEditingHeartbeat ] = useState < any > ( null ) ;
5816+ const pollRequestIdRef = useRef ( 0 ) ;
5817+ const detailRequestIdRef = useRef ( 0 ) ;
58245818
58255819 // ─── Color mode ───
58265820 const [ colorMode , setColorMode ] = useState ( ( ) => localStorage . getItem ( "colorMode" ) || "system" ) ;
@@ -5869,8 +5863,10 @@ export default function App() {
58695863 } , [ ] ) ;
58705864
58715865 const poll = useCallback ( async ( ) => {
5866+ const requestId = ++ pollRequestIdRef . current ;
58725867 try {
58735868 const data = await fetchMainViewData ( activeView , API ) ;
5869+ if ( requestId !== pollRequestIdRef . current ) return ;
58745870 if ( data . tasks !== undefined ) {
58755871 setTasks ( data . tasks ) ;
58765872 setDetail ( ( current ) => {
@@ -5885,6 +5881,7 @@ export default function App() {
58855881 setConnected ( true ) ;
58865882 setApiError ( null ) ;
58875883 } catch ( err ) {
5884+ if ( requestId !== pollRequestIdRef . current ) return ;
58885885 setConnected ( false ) ;
58895886 setApiError ( `Failed to refresh ${ activeView } : ${ err . message } ` ) ;
58905887 }
@@ -5908,32 +5905,47 @@ export default function App() {
59085905 } , [ backendReady ] ) ;
59095906
59105907 const openTaskDetail = useCallback ( async ( task ) => {
5908+ const requestId = ++ detailRequestIdRef . current ;
59115909 try {
5912- setDetail ( await fetchTask ( task . id ) ) ;
5910+ const fullTask = await fetchTask ( task . id ) ;
5911+ if ( requestId === detailRequestIdRef . current ) setDetail ( fullTask ) ;
59135912 } catch ( e ) {
5914- setApiError ( `Failed to fetch task details: ${ e . message } ` ) ;
5913+ if ( requestId === detailRequestIdRef . current ) {
5914+ setApiError ( `Failed to fetch task details: ${ e . message } ` ) ;
5915+ }
59155916 }
59165917 } , [ ] ) ;
5918+ const closeTaskDetail = useCallback ( ( ) => {
5919+ detailRequestIdRef . current += 1 ;
5920+ setDetail ( null ) ;
5921+ } , [ ] ) ;
5922+ const switchActiveView = useCallback ( ( view : MainView ) => {
5923+ pollRequestIdRef . current += 1 ;
5924+ setActiveView ( view ) ;
5925+ } , [ ] ) ;
59175926
5918- const handleAction = useCallback ( async ( action , id ) => {
5919- try {
5920- if ( action === "cancel" ) await cancelTask ( id ) ;
5921- else if ( action === "retry" ) await retryTask ( id ) ;
5922- else if ( action === "delete" ) {
5923- await deleteTask ( id ) ;
5924- setDetail ( ( current ) => ( current ?. id === id ? null : current ) ) ;
5925- } else if ( action === "edit" ) {
5926- setEditingTask ( await fetchTask ( id ) ) ;
5927- return ;
5928- } else if ( action === "fork" ) {
5929- setForkingTask ( await fetchTask ( id ) ) ;
5930- return ;
5927+ const handleAction = useCallback (
5928+ async ( action , id ) => {
5929+ try {
5930+ if ( action === "cancel" ) await cancelTask ( id ) ;
5931+ else if ( action === "retry" ) await retryTask ( id ) ;
5932+ else if ( action === "delete" ) {
5933+ await deleteTask ( id ) ;
5934+ setDetail ( ( current ) => ( current ?. id === id ? null : current ) ) ;
5935+ } else if ( action === "edit" ) {
5936+ setEditingTask ( await fetchTask ( id ) ) ;
5937+ return ;
5938+ } else if ( action === "fork" ) {
5939+ setForkingTask ( await fetchTask ( id ) ) ;
5940+ return ;
5941+ }
5942+ poll ( ) ;
5943+ } catch ( e ) {
5944+ setApiError ( `${ action } failed: ${ e . message } ` ) ;
59315945 }
5932- poll ( ) ;
5933- } catch ( e ) {
5934- setApiError ( `${ action } failed: ${ e . message } ` ) ;
5935- }
5936- } , [ poll ] ) ;
5946+ } ,
5947+ [ poll ] ,
5948+ ) ;
59375949
59385950 const handleHeartbeatAction = async ( action , id ) => {
59395951 try {
@@ -6396,7 +6408,7 @@ export default function App() {
63966408 ] . map ( ( tab ) => (
63976409 < button
63986410 key = { tab . key }
6399- onClick = { ( ) => setActiveView ( tab . key as MainView ) }
6411+ onClick = { ( ) => switchActiveView ( tab . key as MainView ) }
64006412 style = { {
64016413 padding : "6px 9px" ,
64026414 borderRadius : 5 ,
@@ -6670,7 +6682,7 @@ export default function App() {
66706682 { detail && (
66716683 < DetailPanel
66726684 task = { detail }
6673- onClose = { ( ) => setDetail ( null ) }
6685+ onClose = { closeTaskDetail }
66746686 onRespond = { handleRespond }
66756687 onResume = { handleResume }
66766688 />
0 commit comments