fix: guard ssh action state resets (#3533)

This commit is contained in:
Neil
2026-05-30 00:49:56 -07:00
committed by GitHub
parent aa60da9cf1
commit e0d9c4c2b0
2 changed files with 33 additions and 7 deletions
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import {
CircleStop,
Loader2,
@@ -85,13 +85,27 @@ export function SshTargetCard({
const terminateInFlight = actionInFlight === 'terminate' || busyAction === 'terminate'
const resetInFlight = actionInFlight === 'reset' || busyAction === 'reset'
const removeInFlight = busyAction === 'remove'
const mountedRef = useRef(true)
useEffect(() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])
const clearActionInFlight = (): void => {
if (mountedRef.current) {
setActionInFlight(null)
}
}
const handleConnect = (): void => {
if (actionInFlight) {
return
}
setActionInFlight('connect')
Promise.resolve(onConnect(target.id)).finally(() => setActionInFlight(null))
void Promise.resolve(onConnect(target.id)).finally(clearActionInFlight)
}
const handleDisconnect = (): void => {
@@ -99,7 +113,7 @@ export function SshTargetCard({
return
}
setActionInFlight('disconnect')
Promise.resolve(onDisconnect(target.id)).finally(() => setActionInFlight(null))
void Promise.resolve(onDisconnect(target.id)).finally(clearActionInFlight)
}
const handleTerminateSessions = (): void => {
@@ -107,7 +121,7 @@ export function SshTargetCard({
return
}
setActionInFlight('terminate')
Promise.resolve(onTerminateSessions(target.id)).finally(() => setActionInFlight(null))
void Promise.resolve(onTerminateSessions(target.id)).finally(clearActionInFlight)
}
const handleResetRelay = (): void => {
@@ -115,7 +129,7 @@ export function SshTargetCard({
return
}
setActionInFlight('reset')
Promise.resolve(onResetRelay(target.id)).finally(() => setActionInFlight(null))
void Promise.resolve(onResetRelay(target.id)).finally(clearActionInFlight)
}
const renderEndRemoteTerminalsButton = (): React.JSX.Element => (
@@ -4,7 +4,7 @@
* Why extracted: keeps AddRepoSteps.tsx under the 400-line oxlint limit
* while isolating the inline-connect interaction logic.
*/
import React, { useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { Loader2 } from 'lucide-react'
import type { SshTarget, SshConnectionState } from '../../../../shared/ssh-types'
@@ -22,6 +22,7 @@ export function SshTargetRow({
onConnect
}: Props): React.JSX.Element {
const [connecting, setConnecting] = useState(false)
const mountedRef = useRef(true)
const status = target.state?.status ?? 'disconnected'
const isConnected = status === 'connected'
const isBusy =
@@ -49,9 +50,20 @@ export function SshTargetRow({
return
}
setConnecting(true)
void onConnect(target.id).finally(() => setConnecting(false))
void onConnect(target.id).finally(() => {
if (mountedRef.current) {
setConnecting(false)
}
})
}
useEffect(() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])
return (
<div
role={isConnected ? 'button' : undefined}