mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 21:23:45 +01:00
[web] hide online collaborators when offline (#35403)
GitOrigin-RevId: bea77d919c63740e0049467327720036876f64ee
This commit is contained in:
+5
-17
@@ -1,8 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import OLTooltip from '@/shared/components/ol/ol-tooltip'
|
||||
import { useUnsavedDocsContext } from '@/features/ide-react/context/unsaved-docs-context'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
|
||||
export function OfflineIndicatorContent() {
|
||||
const { t } = useTranslation()
|
||||
@@ -20,22 +18,12 @@ export function OfflineIndicatorContent() {
|
||||
)
|
||||
}
|
||||
|
||||
export default function OfflineIndicator() {
|
||||
export default function OfflineIndicator({
|
||||
isOffline,
|
||||
}: {
|
||||
isOffline: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { unsavedDocs } = useUnsavedDocsContext()
|
||||
const improvedFlakyConnections = useFeatureFlag(
|
||||
'intermittent-connection-improvements'
|
||||
)
|
||||
|
||||
if (!improvedFlakyConnections) {
|
||||
return null
|
||||
}
|
||||
|
||||
// TODO: use shared hook for this when merged
|
||||
const MAX_UNSAVED_ALERT_SECONDS = 15
|
||||
const isOffline = [...unsavedDocs.values()].some(
|
||||
seconds => seconds >= MAX_UNSAVED_ALERT_SECONDS
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{isOffline && <OfflineIndicatorContent />}
|
||||
|
||||
@@ -16,6 +16,7 @@ import UpgradeButton from './upgrade-button'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
import useIsNetworkStalled from '@/features/ide-react/hooks/use-is-network-stalled'
|
||||
import OLIconButton from '@/shared/components/ol/ol-icon-button'
|
||||
import OLTooltip from '@/shared/components/ol/ol-tooltip'
|
||||
import SplitTestBadge from '@/shared/components/split-test-badge'
|
||||
@@ -33,7 +34,12 @@ export const Toolbar = () => {
|
||||
const upgradeButtonRelocation = useFeatureFlag(
|
||||
'editor-upgrade-button-relocation'
|
||||
)
|
||||
const improvedFlakyConnections = useFeatureFlag(
|
||||
'intermittent-connection-improvements'
|
||||
)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isOfflineDueToNetworkStall = useIsNetworkStalled()
|
||||
const shouldDisplaySubmitButton =
|
||||
(permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') &&
|
||||
SubmitProjectButton
|
||||
@@ -130,9 +136,11 @@ export const Toolbar = () => {
|
||||
</div>
|
||||
<ToolbarProjectTitle />
|
||||
<div className="ide-redesign-toolbar-actions-wrapper">
|
||||
<OfflineIndicator />
|
||||
{improvedFlakyConnections && (
|
||||
<OfflineIndicator isOffline={isOfflineDueToNetworkStall} />
|
||||
)}
|
||||
<div className="ide-redesign-toolbar-actions">
|
||||
<OnlineUsers />
|
||||
{!isOfflineDueToNetworkStall && <OnlineUsers />}
|
||||
{!isRestrictedTokenMember && <ShowHistoryButton />}
|
||||
<ChangeLayoutButton />
|
||||
{shouldDisplaySubmitButton && cobranding && (
|
||||
|
||||
+5
-65
@@ -1,79 +1,19 @@
|
||||
import OfflineIndicator from '@/features/ide-react/components/toolbar/offline-indicator'
|
||||
import { UnsavedDocsContext } from '@/features/ide-react/context/unsaved-docs-context'
|
||||
import { IdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||
import { IdeEventEmitter } from '@/features/ide-react/create-ide-event-emitter'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
|
||||
const defaultIdeReactContextValue = {
|
||||
projectId: 'test-project',
|
||||
eventEmitter: new IdeEventEmitter(),
|
||||
startedFreeTrial: false,
|
||||
setStartedFreeTrial: () => {},
|
||||
reportError: () => {},
|
||||
projectJoined: true,
|
||||
permissionsLevel: 'owner' as const,
|
||||
setPermissionsLevel: () => {},
|
||||
outOfSync: false,
|
||||
setOutOfSync: () => {},
|
||||
}
|
||||
|
||||
const defaultUnsavedDocsContextValue = {
|
||||
unsavedDocs: new Map<string, number>(),
|
||||
isLocked: false,
|
||||
isSavingStalled: false,
|
||||
}
|
||||
|
||||
const mount = ({
|
||||
unsavedDocs = new Map<string, number>(),
|
||||
}: {
|
||||
unsavedDocs?: Map<string, number>
|
||||
} = {}) => {
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<IdeReactContext.Provider value={defaultIdeReactContextValue}>
|
||||
<UnsavedDocsContext.Provider
|
||||
value={{ ...defaultUnsavedDocsContextValue, unsavedDocs }}
|
||||
>
|
||||
<OfflineIndicator />
|
||||
</UnsavedDocsContext.Provider>
|
||||
</IdeReactContext.Provider>
|
||||
</SplitTestProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const enableFlag = () => {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-splitTestVariants', {
|
||||
'intermittent-connection-improvements': 'enabled',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const unsavedDoc = () => new Map<string, number>([['doc-1', 20]])
|
||||
|
||||
describe('<OfflineIndicator />', function () {
|
||||
it('is hidden when the feature flag is disabled', function () {
|
||||
mount({ unsavedDocs: unsavedDoc() })
|
||||
cy.findByText('You’re offline').should('not.exist')
|
||||
cy.findByRole('status').should('not.exist')
|
||||
})
|
||||
|
||||
it('is hidden when the flag is enabled but there are no unsaved docs', function () {
|
||||
enableFlag()
|
||||
mount()
|
||||
it('renders an empty live region when online', function () {
|
||||
cy.mount(<OfflineIndicator isOffline={false} />)
|
||||
cy.findByText('You’re offline').should('not.exist')
|
||||
cy.findByRole('status').should('be.empty')
|
||||
})
|
||||
|
||||
it('is shown when the flag is enabled and there are unsaved docs', function () {
|
||||
enableFlag()
|
||||
mount({ unsavedDocs: unsavedDoc() })
|
||||
it('shows the offline indicator when offline', function () {
|
||||
cy.mount(<OfflineIndicator isOffline />)
|
||||
cy.findAllByText('You’re offline').first().should('be.visible')
|
||||
})
|
||||
|
||||
it('announces the offline status to screen readers via a live region', function () {
|
||||
enableFlag()
|
||||
mount({ unsavedDocs: unsavedDoc() })
|
||||
cy.mount(<OfflineIndicator isOffline />)
|
||||
cy.findByRole('status').within(() => {
|
||||
cy.findByText('You’re offline').should('exist')
|
||||
cy.findByText(
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { OnlineUsers } from '@/features/ide-react/components/toolbar/online-users'
|
||||
import { EditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||
import { OnlineUsersContext } from '@/features/ide-react/context/online-users-context'
|
||||
import type { OnlineUser } from '@/features/ide-react/context/online-users-context'
|
||||
|
||||
const onlineUser: OnlineUser = {
|
||||
id: 'client-1',
|
||||
user_id: 'user-1',
|
||||
email: 'alice@example.com',
|
||||
name: 'alice',
|
||||
initial: 'a',
|
||||
}
|
||||
|
||||
const mount = ({
|
||||
onlineUsersArray = [onlineUser],
|
||||
}: {
|
||||
onlineUsersArray?: OnlineUser[]
|
||||
} = {}) => {
|
||||
const mockOpenDoc = cy.stub().as('openDoc')
|
||||
cy.mount(
|
||||
<EditorManagerContext.Provider value={{ openDoc: mockOpenDoc } as any}>
|
||||
<OnlineUsersContext.Provider
|
||||
value={{
|
||||
onlineUsers: {},
|
||||
onlineUserCursorHighlights: {},
|
||||
onlineUsersArray,
|
||||
onlineUsersCount: onlineUsersArray.length,
|
||||
}}
|
||||
>
|
||||
<OnlineUsers />
|
||||
</OnlineUsersContext.Provider>
|
||||
</EditorManagerContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
describe('<OnlineUsers />', function () {
|
||||
it('renders nothing when there are no online users', function () {
|
||||
mount({ onlineUsersArray: [] })
|
||||
cy.get('.ide-redesign-online-users').should('not.exist')
|
||||
})
|
||||
|
||||
it('renders the widget when there are online users', function () {
|
||||
mount()
|
||||
cy.get('.ide-redesign-online-users').should('exist')
|
||||
cy.findByText('a').should('be.visible')
|
||||
})
|
||||
})
|
||||
@@ -4,6 +4,11 @@ import {
|
||||
makeEditorProvider,
|
||||
} from '../../../helpers/editor-providers'
|
||||
import { Cobranding } from '@ol-types/cobranding'
|
||||
import {
|
||||
OnlineUsersContext,
|
||||
type OnlineUser,
|
||||
} from '@/features/ide-react/context/online-users-context'
|
||||
import { UnsavedDocsContext } from '@/features/ide-react/context/unsaved-docs-context'
|
||||
import partnerLogoUrl from './cobranding-logo.png'
|
||||
|
||||
describe('<Toolbar />', function () {
|
||||
@@ -245,4 +250,66 @@ describe('<Toolbar />', function () {
|
||||
|
||||
// TODO: Test all the dynamic items
|
||||
})
|
||||
|
||||
describe('offline gating', function () {
|
||||
const onlineUser: OnlineUser = {
|
||||
id: 'client-1',
|
||||
user_id: 'user-1',
|
||||
email: 'alice@example.com',
|
||||
name: 'alice',
|
||||
initial: 'a',
|
||||
}
|
||||
|
||||
const mountToolbar = ({ offline }: { offline: boolean }) => {
|
||||
const unsavedDocs = offline ? new Map([['doc-1', 20]]) : new Map()
|
||||
cy.mount(
|
||||
<EditorProviders>
|
||||
<OnlineUsersContext.Provider
|
||||
value={{
|
||||
onlineUsers: {},
|
||||
onlineUserCursorHighlights: {},
|
||||
onlineUsersArray: [onlineUser],
|
||||
onlineUsersCount: 1,
|
||||
}}
|
||||
>
|
||||
<UnsavedDocsContext.Provider
|
||||
value={{ unsavedDocs, isLocked: false, isSavingStalled: offline }}
|
||||
>
|
||||
<Toolbar />
|
||||
</UnsavedDocsContext.Provider>
|
||||
</OnlineUsersContext.Provider>
|
||||
</EditorProviders>
|
||||
)
|
||||
}
|
||||
|
||||
describe('with intermittent-connection-improvements enabled', function () {
|
||||
beforeEach(function () {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-splitTestVariants', {
|
||||
'intermittent-connection-improvements': 'enabled',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('shows online users and no offline indicator when online', function () {
|
||||
mountToolbar({ offline: false })
|
||||
cy.get('.ide-redesign-online-users').should('exist')
|
||||
cy.findByText('You’re offline').should('not.exist')
|
||||
})
|
||||
|
||||
it('hides online users and shows the offline indicator when offline', function () {
|
||||
mountToolbar({ offline: true })
|
||||
cy.get('.ide-redesign-online-users').should('not.exist')
|
||||
cy.findAllByText('You’re offline').first().should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('with intermittent-connection-improvements disabled', function () {
|
||||
it('always shows online users and never the offline indicator', function () {
|
||||
mountToolbar({ offline: true })
|
||||
cy.get('.ide-redesign-online-users').should('exist')
|
||||
cy.findByText('You’re offline').should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user