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 6f80e85cdb..e8ad0e60c0 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
@@ -7,6 +7,7 @@ import { createPortal } from 'react-dom'
import { useGlobalAlertsContainer } from '@/features/ide-react/context/global-alerts-context'
import Notification from '@/shared/components/notification'
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
const rollingBuildsUpdatedAlert: Array<{
import: { default: ElementType }
@@ -23,6 +24,9 @@ export function Alerts() {
secondsUntilReconnect,
} = useConnectionContext()
const globalAlertsContainer = useGlobalAlertsContainer()
+ const improvedFlakyConnections = useFeatureFlag(
+ 'intermittent-connection-improvements'
+ )
if (!globalAlertsContainer) {
return null
@@ -36,56 +40,60 @@ export function Alerts() {
)
)}
- {connectionState.forceDisconnected &&
- // hide "disconnected" banner when displaying out of sync modal
- connectionState.error !== 'out-of-sync' ? (
-
- {t('disconnected')}}
- />
-
- ) : null}
+ {!improvedFlakyConnections && (
+ <>
+ {connectionState.forceDisconnected &&
+ // hide "disconnected" banner when displaying out of sync modal
+ connectionState.error !== 'out-of-sync' ? (
+
+ {t('disconnected')}}
+ />
+
+ ) : null}
- {connectionState.reconnectAt ? (
-
- ) : null}
+ {connectionState.reconnectAt ? (
+
+ ) : null}
- {isStillReconnecting ? (
-
- {t('reconnecting')}…}
- />
-
- ) : null}
+ {isStillReconnecting ? (
+
+ {t('reconnecting')}…}
+ />
+
+ ) : null}
- {connectionState.inactiveDisconnect ||
- (connectionState.readyState === WebSocket.CLOSED &&
- (connectionState.error === 'rate-limited' ||
- connectionState.error === 'unable-to-connect') &&
- !secondsUntilReconnect()) ? (
-
- {t('editor_disconected_click_to_reconnect')}
- }
- />
-
- ) : null}
+ {connectionState.inactiveDisconnect ||
+ (connectionState.readyState === WebSocket.CLOSED &&
+ (connectionState.error === 'rate-limited' ||
+ connectionState.error === 'unable-to-connect') &&
+ !secondsUntilReconnect()) ? (
+
+ {t('editor_disconected_click_to_reconnect')}
+ }
+ />
+
+ ) : null}
- {debugging ? (
-
- Connected: {isConnected.toString()}}
- />
-
- ) : null}
+ {debugging ? (
+
+ Connected: {isConnected.toString()}}
+ />
+
+ ) : null}
+ >
+ )}
>,
globalAlertsContainer
)
diff --git a/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs-locked-alert.tsx b/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs-locked-alert.tsx
index 88dab5f33e..499c02bdbd 100644
--- a/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs-locked-alert.tsx
+++ b/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs-locked-alert.tsx
@@ -3,11 +3,15 @@ import { useTranslation } from 'react-i18next'
import Notification from '@/shared/components/notification'
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
export const UnsavedDocsLockedAlert: FC = () => {
const { t } = useTranslation()
const { openDocs } = useEditorManagerContext()
const { reportError } = useIdeReactContext()
+ const improvedFlakyConnections = useFeatureFlag(
+ 'intermittent-connection-improvements'
+ )
useEffect(() => {
const { pendingOpsLength, inflightOpsLength } = openDocs.getUnsavedOpsSize()
@@ -17,6 +21,10 @@ export const UnsavedDocsLockedAlert: FC = () => {
})
}, [reportError, openDocs])
+ if (improvedFlakyConnections) {
+ return null
+ }
+
return (
0,
+ tryReconnectNow: () => {},
+ registerUserActivity: () => {},
+ closeConnection: () => {},
+ getSocketDebuggingInfo: () => ({ id: '' }),
+} as any
+
+const projectContextValue = {
+ project: { imageName: 'texlive-full:2023.1' },
+} as any
+
+const mount = () => {
+ cy.mount(
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const enableFlag = () => {
+ cy.window().then(win => {
+ win.metaAttributesCache.set('ol-splitTestVariants', {
+ 'intermittent-connection-improvements': 'enabled',
+ })
+ })
+}
+
+describe('', function () {
+ it('shows the connection alert when flag is disabled', function () {
+ mount()
+ cy.findByRole('alert').should('exist')
+ })
+
+ it('suppresses the connection alert when flag is enabled', function () {
+ enableFlag()
+ mount()
+ cy.findByRole('alert').should('not.exist')
+ })
+})
diff --git a/services/web/test/frontend/features/ide-react/components/unsaved-docs/unsaved-docs.spec.tsx b/services/web/test/frontend/features/ide-react/components/unsaved-docs/unsaved-docs.spec.tsx
index a01bec2a01..1fc485a42a 100644
--- a/services/web/test/frontend/features/ide-react/components/unsaved-docs/unsaved-docs.spec.tsx
+++ b/services/web/test/frontend/features/ide-react/components/unsaved-docs/unsaved-docs.spec.tsx
@@ -4,6 +4,8 @@ import { GlobalAlertsProvider } from '@/features/ide-react/context/global-alerts
import { FileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
import { ConnectionContext } from '@/features/ide-react/context/connection-context'
import { SplitTestProvider } from '@/shared/context/split-test-context'
+import { EditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
+import { IdeReactContext } from '@/features/ide-react/context/ide-react-context'
const defaultUnsavedDocsContextValue = {
unsavedDocs: new Map([['doc1', 16]]),
@@ -37,16 +39,37 @@ const mockFileTreePathContextValue = {
previewByPath: () => null,
}
-const mount = () => {
+const mockEditorManagerContextValue = {
+ openDocs: {
+ getUnsavedOpsSize: () => ({ pendingOpsLength: 2, inflightOpsLength: 1 }),
+ },
+} as any
+
+const mount = ({
+ unsavedDocsContextValue = defaultUnsavedDocsContextValue,
+ reportError = cy.stub(),
+}: {
+ unsavedDocsContextValue?: typeof defaultUnsavedDocsContextValue
+ reportError?: any
+} = {}) => {
+ const ideReactContextValue = { reportError } as any
cy.mount(
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -72,4 +95,34 @@ describe('', function () {
mount()
cy.findByRole('alert').should('not.exist')
})
+
+ describe('when locked', function () {
+ const lockedContextValue = {
+ unsavedDocs: new Map(),
+ isLocked: true,
+ }
+
+ it('shows the locked alert and reports the error when flag is disabled', function () {
+ const reportError = cy.stub().as('reportError')
+ mount({ unsavedDocsContextValue: lockedContextValue, reportError })
+ cy.findByRole('alert').should('exist')
+ cy.get('@reportError').should(
+ 'have.been.calledOnceWith',
+ 'connection-lost-with-unsaved-changes',
+ { pendingOpsLength: 2, inflightOpsLength: 1 }
+ )
+ })
+
+ it('hides the locked alert but still reports the error when flag is enabled', function () {
+ const reportError = cy.stub().as('reportError')
+ enableFlag()
+ mount({ unsavedDocsContextValue: lockedContextValue, reportError })
+ cy.findByRole('alert').should('not.exist')
+ cy.get('@reportError').should(
+ 'have.been.calledOnceWith',
+ 'connection-lost-with-unsaved-changes',
+ { pendingOpsLength: 2, inflightOpsLength: 1 }
+ )
+ })
+ })
})