@@ -49,6 +49,7 @@ interface SocketContextType {
4949 socket : Socket | null
5050 isConnected : boolean
5151 isConnecting : boolean
52+ isReconnecting : boolean
5253 authFailed : boolean
5354 currentWorkflowId : string | null
5455 currentSocketId : string | null
@@ -88,6 +89,7 @@ const SocketContext = createContext<SocketContextType>({
8889 socket : null ,
8990 isConnected : false ,
9091 isConnecting : false ,
92+ isReconnecting : false ,
9193 authFailed : false ,
9294 currentWorkflowId : null ,
9395 currentSocketId : null ,
@@ -122,6 +124,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
122124 const [ socket , setSocket ] = useState < Socket | null > ( null )
123125 const [ isConnected , setIsConnected ] = useState ( false )
124126 const [ isConnecting , setIsConnecting ] = useState ( false )
127+ const [ isReconnecting , setIsReconnecting ] = useState ( false )
125128 const [ currentWorkflowId , setCurrentWorkflowId ] = useState < string | null > ( null )
126129 const [ currentSocketId , setCurrentSocketId ] = useState < string | null > ( null )
127130 const [ presenceUsers , setPresenceUsers ] = useState < PresenceUser [ ] > ( [ ] )
@@ -236,20 +239,19 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
236239 setCurrentWorkflowId ( null )
237240 setPresenceUsers ( [ ] )
238241
239- logger . info ( 'Socket disconnected' , {
240- reason,
241- } )
242+ // socket.active indicates if auto-reconnect will happen
243+ if ( socketInstance . active ) {
244+ setIsReconnecting ( true )
245+ logger . info ( 'Socket disconnected, will auto-reconnect' , { reason } )
246+ } else {
247+ setIsReconnecting ( false )
248+ logger . info ( 'Socket disconnected, no auto-reconnect' , { reason } )
249+ }
242250 } )
243251
244- socketInstance . on ( 'connect_error' , ( error : any ) => {
252+ socketInstance . on ( 'connect_error' , ( error : Error ) => {
245253 setIsConnecting ( false )
246- logger . error ( 'Socket connection error:' , {
247- message : error . message ,
248- stack : error . stack ,
249- description : error . description ,
250- type : error . type ,
251- transport : error . transport ,
252- } )
254+ logger . error ( 'Socket connection error:' , { message : error . message } )
253255
254256 // Check if this is an authentication failure
255257 const isAuthError =
@@ -261,43 +263,41 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
261263 logger . warn (
262264 'Authentication failed - stopping reconnection attempts. User may need to refresh/re-login.'
263265 )
264- // Stop reconnection attempts to prevent infinite loop
265266 socketInstance . disconnect ( )
266- // Reset state to allow re-initialization when session is restored
267267 setSocket ( null )
268268 setAuthFailed ( true )
269+ setIsReconnecting ( false )
269270 initializedRef . current = false
271+ } else if ( socketInstance . active ) {
272+ // Temporary failure, will auto-reconnect
273+ setIsReconnecting ( true )
270274 }
271275 } )
272276
273- socketInstance . on ( 'reconnect' , ( attemptNumber ) => {
277+ // Reconnection events are on the Manager (socket.io), not the socket itself
278+ socketInstance . io . on ( 'reconnect' , ( attemptNumber ) => {
274279 setIsConnected ( true )
280+ setIsReconnecting ( false )
275281 setCurrentSocketId ( socketInstance . id ?? null )
276282 logger . info ( 'Socket reconnected successfully' , {
277283 attemptNumber,
278284 socketId : socketInstance . id ,
279285 transport : socketInstance . io . engine ?. transport ?. name ,
280286 } )
281- // Note: join-workflow is handled by the useEffect watching isConnected
282287 } )
283288
284- socketInstance . on ( 'reconnect_attempt' , ( attemptNumber ) => {
285- logger . info ( 'Socket reconnection attempt (fresh token will be generated)' , {
286- attemptNumber,
287- timestamp : new Date ( ) . toISOString ( ) ,
288- } )
289+ socketInstance . io . on ( 'reconnect_attempt' , ( attemptNumber ) => {
290+ setIsReconnecting ( true )
291+ logger . info ( 'Socket reconnection attempt' , { attemptNumber } )
289292 } )
290293
291- socketInstance . on ( 'reconnect_error' , ( error : any ) => {
292- logger . error ( 'Socket reconnection error:' , {
293- message : error . message ,
294- attemptNumber : error . attemptNumber ,
295- type : error . type ,
296- } )
294+ socketInstance . io . on ( 'reconnect_error' , ( error : Error ) => {
295+ logger . error ( 'Socket reconnection error:' , { message : error . message } )
297296 } )
298297
299- socketInstance . on ( 'reconnect_failed' , ( ) => {
298+ socketInstance . io . on ( 'reconnect_failed' , ( ) => {
300299 logger . error ( 'Socket reconnection failed - all attempts exhausted' )
300+ setIsReconnecting ( false )
301301 setIsConnecting ( false )
302302 } )
303303
@@ -629,6 +629,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
629629
630630 if ( commit ) {
631631 socket . emit ( 'workflow-operation' , {
632+ workflowId : currentWorkflowId ,
632633 operation,
633634 target,
634635 payload,
@@ -645,6 +646,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
645646 }
646647
647648 pendingPositionUpdates . current . set ( blockId , {
649+ workflowId : currentWorkflowId ,
648650 operation,
649651 target,
650652 payload,
@@ -666,6 +668,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
666668 }
667669 } else {
668670 socket . emit ( 'workflow-operation' , {
671+ workflowId : currentWorkflowId ,
669672 operation,
670673 target,
671674 payload,
@@ -678,47 +681,53 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
678681 )
679682
680683 const emitSubblockUpdate = useCallback (
681- ( blockId : string , subblockId : string , value : any , operationId ?: string ) => {
682- if ( socket && currentWorkflowId ) {
683- socket . emit ( 'subblock-update' , {
684- blockId,
685- subblockId,
686- value,
687- timestamp : Date . now ( ) ,
688- operationId,
689- } )
690- } else {
691- logger . warn ( 'Cannot emit subblock update: no socket connection or workflow room' , {
692- hasSocket : ! ! socket ,
693- currentWorkflowId,
694- blockId,
695- subblockId,
696- } )
684+ (
685+ blockId : string ,
686+ subblockId : string ,
687+ value : any ,
688+ operationId ?: string ,
689+ workflowId ?: string
690+ ) => {
691+ if ( ! workflowId ) {
692+ logger . error ( 'emitSubblockUpdate called without workflowId' , { blockId, subblockId } )
693+ return
694+ }
695+ if ( ! socket ) {
696+ logger . warn ( 'Cannot emit subblock update: no socket connection' , { workflowId, blockId } )
697+ return
697698 }
699+ socket . emit ( 'subblock-update' , {
700+ workflowId,
701+ blockId,
702+ subblockId,
703+ value,
704+ timestamp : Date . now ( ) ,
705+ operationId,
706+ } )
698707 } ,
699- [ socket , currentWorkflowId ]
708+ [ socket ]
700709 )
701710
702711 const emitVariableUpdate = useCallback (
703- ( variableId : string , field : string , value : any , operationId ?: string ) => {
704- if ( socket && currentWorkflowId ) {
705- socket . emit ( 'variable-update' , {
706- variableId,
707- field,
708- value,
709- timestamp : Date . now ( ) ,
710- operationId,
711- } )
712- } else {
713- logger . warn ( 'Cannot emit variable update: no socket connection or workflow room' , {
714- hasSocket : ! ! socket ,
715- currentWorkflowId,
716- variableId,
717- field,
718- } )
712+ ( variableId : string , field : string , value : any , operationId ?: string , workflowId ?: string ) => {
713+ if ( ! workflowId ) {
714+ logger . error ( 'emitVariableUpdate called without workflowId' , { variableId, field } )
715+ return
716+ }
717+ if ( ! socket ) {
718+ logger . warn ( 'Cannot emit variable update: no socket connection' , { workflowId, variableId } )
719+ return
719720 }
721+ socket . emit ( 'variable-update' , {
722+ workflowId,
723+ variableId,
724+ field,
725+ value,
726+ timestamp : Date . now ( ) ,
727+ operationId,
728+ } )
720729 } ,
721- [ socket , currentWorkflowId ]
730+ [ socket ]
722731 )
723732
724733 const lastCursorEmit = useRef ( 0 )
@@ -794,6 +803,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
794803 socket,
795804 isConnected,
796805 isConnecting,
806+ isReconnecting,
797807 authFailed,
798808 currentWorkflowId,
799809 currentSocketId,
@@ -820,6 +830,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
820830 socket ,
821831 isConnected ,
822832 isConnecting ,
833+ isReconnecting ,
823834 authFailed ,
824835 currentWorkflowId ,
825836 currentSocketId ,
0 commit comments