Clear SSH reset prompt during render (#3389)

This commit is contained in:
Neil
2026-05-30 14:49:39 -04:00
committed by GitHub
parent 3193ea5f16
commit c03a6a6db2
3 changed files with 77 additions and 10 deletions
@@ -1,9 +1,13 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useRef, useState } from 'react'
import type { ReactNode } from 'react'
import type { SshConnectionState } from '../../../../shared/ssh-types'
import { useMountedRef } from '@/hooks/useMountedRef'
import { SshDestructiveActionDialog } from './SshDestructiveActionDialog'
import { isSshTargetConnecting, type SshTargetBusyAction } from './ssh-target-action-state'
import {
isSshTargetConnecting,
shouldClearPendingSshReset,
type SshTargetBusyAction
} from './ssh-target-action-state'
type PendingTargetAction = { id: string; label: string }
@@ -102,12 +106,17 @@ export function SshTargetDestructiveActions({
pendingReset !== null && isSshTargetConnecting(pendingResetStatus)
const pendingTerminateIsBusy =
pendingTerminate !== null && targetActionsInFlight.get(pendingTerminate.id) === 'terminate'
useEffect(() => {
if (pendingResetBlockedByConnection && !pendingResetIsBusy) {
setPendingReset(null)
}
}, [pendingResetBlockedByConnection, pendingResetIsBusy])
const shouldClearReset = shouldClearPendingSshReset({
pendingTargetId: pendingReset?.id ?? null,
pendingResetIsBusy,
connectionStatus: pendingResetStatus
})
if (shouldClearReset) {
// Why: a reconnecting target cannot safely reset its relay; clear the
// pending dialog before it paints stale destructive UI.
setPendingReset(null)
}
const dialogPendingReset = shouldClearReset ? null : pendingReset
const confirmResetRelay = async (): Promise<void> => {
if (!pendingReset) {
@@ -169,10 +178,10 @@ export function SshTargetDestructiveActions({
/>
<SshDestructiveActionDialog
open={!!pendingReset && (!pendingResetBlockedByConnection || pendingResetIsBusy)}
open={!!dialogPendingReset && (!pendingResetBlockedByConnection || pendingResetIsBusy)}
title="Reset Remote Relay?"
description="This force-stops the remote relay for this SSH target. Active remote terminals and port forwards for this target will end."
targetLabel={pendingReset?.label}
targetLabel={dialogPendingReset?.label}
actionLabel="Reset Relay"
busyLabel="Resetting"
isBusy={pendingResetIsBusy}
@@ -0,0 +1,46 @@
import { describe, expect, it } from 'vitest'
import { isSshTargetConnecting, shouldClearPendingSshReset } from './ssh-target-action-state'
describe('ssh target action state', () => {
it('classifies connecting statuses as busy connection states', () => {
expect(isSshTargetConnecting('connecting')).toBe(true)
expect(isSshTargetConnecting('deploying-relay')).toBe(true)
expect(isSshTargetConnecting('reconnecting')).toBe(true)
expect(isSshTargetConnecting('connected')).toBe(false)
expect(isSshTargetConnecting('disconnected')).toBe(false)
})
it('clears pending reset only when a non-busy target starts connecting', () => {
expect(
shouldClearPendingSshReset({
pendingTargetId: 'target-1',
pendingResetIsBusy: false,
connectionStatus: 'reconnecting'
})
).toBe(true)
expect(
shouldClearPendingSshReset({
pendingTargetId: 'target-1',
pendingResetIsBusy: true,
connectionStatus: 'reconnecting'
})
).toBe(false)
expect(
shouldClearPendingSshReset({
pendingTargetId: null,
pendingResetIsBusy: false,
connectionStatus: 'reconnecting'
})
).toBe(false)
expect(
shouldClearPendingSshReset({
pendingTargetId: 'target-1',
pendingResetIsBusy: false,
connectionStatus: 'connected'
})
).toBe(false)
})
})
@@ -11,3 +11,15 @@ const SSH_TARGET_CONNECTING_STATUSES: ReadonlySet<SshConnectionStatus> = new Set
export function isSshTargetConnecting(status: SshConnectionStatus): boolean {
return SSH_TARGET_CONNECTING_STATUSES.has(status)
}
export function shouldClearPendingSshReset({
pendingTargetId,
pendingResetIsBusy,
connectionStatus
}: {
pendingTargetId: string | null
pendingResetIsBusy: boolean
connectionStatus: SshConnectionStatus
}): boolean {
return pendingTargetId !== null && !pendingResetIsBusy && isSshTargetConnecting(connectionStatus)
}