From 8df7f6772ce17d8740bcdcef1ce0905992d286b3 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Fri, 31 Jan 2025 11:23:51 +0000 Subject: [PATCH] [web] close connection permanently when opening out-of-sync modal (#23148) * [web] close connection permanently when opening out-of-sync modal * [web] disable generic forceDisconnected behavior for out-of-sync modal * [web] hide "disconnected" banner when displaying out of sync modal GitOrigin-RevId: 133b3e24d94279917298ce7fd5d0a76da2265512 --- .../ide-react/components/alerts/alerts.tsx | 4 +++- .../components/modals/force-disconnected.tsx | 8 +++++-- .../connection/connection-manager.ts | 2 +- .../ide-react/context/connection-context.tsx | 21 ++++++++++++------- .../context/editor-manager-context.tsx | 6 +++--- .../web/frontend/stories/decorators/scope.tsx | 2 +- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/services/web/frontend/js/features/ide-react/components/alerts/alerts.tsx b/services/web/frontend/js/features/ide-react/components/alerts/alerts.tsx index 507ecf45e5..3e7bd02e74 100644 --- a/services/web/frontend/js/features/ide-react/components/alerts/alerts.tsx +++ b/services/web/frontend/js/features/ide-react/components/alerts/alerts.tsx @@ -27,7 +27,9 @@ export function Alerts() { return createPortal( <> - {connectionState.forceDisconnected ? ( + {connectionState.forceDisconnected && + // hide "disconnected" banner when displaying out of sync modal + connectionState.error !== 'out-of-sync' ? ( {t('disconnected')}} diff --git a/services/web/frontend/js/features/ide-react/components/modals/force-disconnected.tsx b/services/web/frontend/js/features/ide-react/components/modals/force-disconnected.tsx index 11f6e0e86a..5b34675352 100644 --- a/services/web/frontend/js/features/ide-react/components/modals/force-disconnected.tsx +++ b/services/web/frontend/js/features/ide-react/components/modals/force-disconnected.tsx @@ -15,10 +15,14 @@ function ForceDisconnected() { const [show, setShow] = useState(false) useEffect(() => { - if (connectionState.forceDisconnected) { + if ( + connectionState.forceDisconnected && + // out of sync has its own modal + connectionState.error !== 'out-of-sync' + ) { setShow(true) } - }, [connectionState.forceDisconnected]) + }, [connectionState.forceDisconnected, connectionState.error]) useEffect(() => { if (connectionState.forceDisconnected) { diff --git a/services/web/frontend/js/features/ide-react/connection/connection-manager.ts b/services/web/frontend/js/features/ide-react/connection/connection-manager.ts index ea251c0b22..7e27f6004d 100644 --- a/services/web/frontend/js/features/ide-react/connection/connection-manager.ts +++ b/services/web/frontend/js/features/ide-react/connection/connection-manager.ts @@ -347,7 +347,7 @@ export class ConnectionManager extends EventTarget { return true } - disconnect() { + private disconnect() { this.changeState({ ...this.state, readyState: WebSocket.CLOSED, diff --git a/services/web/frontend/js/features/ide-react/context/connection-context.tsx b/services/web/frontend/js/features/ide-react/context/connection-context.tsx index 3ab5dc4a3e..8cffc2485e 100644 --- a/services/web/frontend/js/features/ide-react/context/connection-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/connection-context.tsx @@ -8,6 +8,7 @@ import { useMemo, } from 'react' import { + ConnectionError, ConnectionState, SocketDebuggingInfo, } from '../connection/types/connection-state' @@ -27,7 +28,7 @@ type ConnectionContextValue = { secondsUntilReconnect: () => number tryReconnectNow: () => void registerUserActivity: () => void - disconnect: () => void + closeConnection: (err: ConnectionError) => void getSocketDebuggingInfo: () => SocketDebuggingInfo } @@ -75,9 +76,10 @@ export const ConnectionProvider: FC = ({ children }) => { [connectionManager] ) - const disconnect = useCallback(() => { - connectionManager.disconnect() - }, [connectionManager]) + const closeConnection = useCallback( + (err: ConnectionError) => connectionManager.close(err), + [connectionManager] + ) const getSocketDebuggingInfo = useCallback( () => connectionManager.getSocketDebuggingInfo(), @@ -87,7 +89,11 @@ export const ConnectionProvider: FC = ({ children }) => { // Reload the page on force disconnect. Doing this in React-land means that we // can use useLocation(), which provides mockable location methods useEffect(() => { - if (connectionState.forceDisconnected) { + if ( + connectionState.forceDisconnected && + // keep editor open when out of sync + connectionState.error !== 'out-of-sync' + ) { const timer = window.setTimeout( () => location.reload(), connectionState.forcedDisconnectDelay * 1000 @@ -99,6 +105,7 @@ export const ConnectionProvider: FC = ({ children }) => { }, [ connectionState.forceDisconnected, connectionState.forcedDisconnectDelay, + connectionState.error, location, ]) @@ -111,7 +118,7 @@ export const ConnectionProvider: FC = ({ children }) => { secondsUntilReconnect, tryReconnectNow, registerUserActivity, - disconnect, + closeConnection, getSocketDebuggingInfo, }), [ @@ -122,7 +129,7 @@ export const ConnectionProvider: FC = ({ children }) => { registerUserActivity, secondsUntilReconnect, tryReconnectNow, - disconnect, + closeConnection, getSocketDebuggingInfo, ] ) diff --git a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx index 3ab33ff7fc..b2a6c4374e 100644 --- a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx @@ -98,7 +98,7 @@ export const EditorManagerProvider: FC = ({ children }) => { const { projectId } = useIdeReactContext() const { reportError, eventEmitter, eventLog } = useIdeReactContext() const { setOutOfSync } = useEditorContext() - const { socket, disconnect, connectionState } = useConnectionContext() + const { socket, closeConnection, connectionState } = useConnectionContext() const { view, setView } = useLayoutContext() const { showGenericMessageModal, genericModalVisible, showOutOfSyncModal } = useModalsContext() @@ -580,7 +580,7 @@ export const EditorManagerProvider: FC = ({ children }) => { // Do not re-join after re-connecting. document.leaveAndCleanUp() - disconnect() + closeConnection('out-of-sync') reportError(error, meta) // Tell the user about the error state. @@ -605,7 +605,7 @@ export const EditorManagerProvider: FC = ({ children }) => { } } }, [ - disconnect, + closeConnection, docError, docTooLongErrorShown, eventEmitter, diff --git a/services/web/frontend/stories/decorators/scope.tsx b/services/web/frontend/stories/decorators/scope.tsx index 4986cbea24..3aa56a0466 100644 --- a/services/web/frontend/stories/decorators/scope.tsx +++ b/services/web/frontend/stories/decorators/scope.tsx @@ -184,7 +184,7 @@ const ConnectionProvider: FC = ({ children }) => { secondsUntilReconnect: () => 0, tryReconnectNow: () => {}, registerUserActivity: () => {}, - disconnect: () => {}, + closeConnection: () => {}, getSocketDebuggingInfo: () => ({ client_id: 'fakeClientId', transport: 'fakeTransport',