[web] Add offline indicator to toolbar (#35368)

GitOrigin-RevId: 81127bff21afb6aadcf175ba1767a2629f01d747
This commit is contained in:
Kristina
2026-09-17 11:23:02 +00:00
committed by Copybot
parent d3920e76d3
commit 77ce277a8b
10 changed files with 261 additions and 15 deletions
@@ -0,0 +1,52 @@
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()
return (
<OLTooltip
id="tooltip-offline-indicator"
description={t('changes_saved_in_browser_sync_when_online')}
overlayProps={{ delay: 0, placement: 'bottom' }}
>
<div className="ide-redesign-toolbar-offline-indicator">
<MaterialIcon type="wifi_off" />
<span>{t('you_re_offline')}</span>
</div>
</OLTooltip>
)
}
export default function OfflineIndicator() {
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 />}
<div className="visually-hidden" role="status">
{isOffline && (
<>
<span>{t('you_re_offline')}</span>
<span>{t('changes_saved_in_browser_sync_when_online')}</span>
</>
)}
</div>
</>
)
}
@@ -19,6 +19,8 @@ export const OnlineUsers = () => {
[openDoc]
)
if (!onlineUsersArray.length) return null
return (
<div className="ide-redesign-online-users">
<OnlineUsersWidget onlineUsers={onlineUsersArray} goToUser={goToUser} />
@@ -19,6 +19,7 @@ import { useFeatureFlag } from '@/shared/context/split-test-context'
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'
import OfflineIndicator from './offline-indicator'
const [publishModalModules] = importOverleafModules('publishModalToolbarButton')
const SubmitProjectButton = publishModalModules?.import.default
@@ -128,15 +129,18 @@ export const Toolbar = () => {
{showUpgradePrompt && upgradeButtonRelocation && <UpgradeButton />}
</div>
<ToolbarProjectTitle />
<div className="ide-redesign-toolbar-actions">
<OnlineUsers />
{!isRestrictedTokenMember && <ShowHistoryButton />}
<ChangeLayoutButton />
{shouldDisplaySubmitButton && cobranding && (
<SubmitProjectButton cobranding={cobranding} />
)}
<ShareProjectButton />
{showUpgradePrompt && !upgradeButtonRelocation && <UpgradeButton />}
<div className="ide-redesign-toolbar-actions-wrapper">
<OfflineIndicator />
<div className="ide-redesign-toolbar-actions">
<OnlineUsers />
{!isRestrictedTokenMember && <ShowHistoryButton />}
<ChangeLayoutButton />
{shouldDisplaySubmitButton && cobranding && (
<SubmitProjectButton cobranding={cobranding} />
)}
<ShareProjectButton />
{showUpgradePrompt && !upgradeButtonRelocation && <UpgradeButton />}
</div>
</div>
</nav>
)
@@ -4,10 +4,14 @@ import { UnsavedDocsAlert } from '@/features/ide-react/components/unsaved-docs/u
import { createPortal } from 'react-dom'
import { useGlobalAlertsContainer } from '@/features/ide-react/context/global-alerts-context'
import { useUnsavedDocsContext } from '@/features/ide-react/context/unsaved-docs-context'
import { useFeatureFlag } from '@/shared/context/split-test-context'
export const UnsavedDocs: FC = () => {
const { unsavedDocs, isLocked } = useUnsavedDocsContext()
const globalAlertsContainer = useGlobalAlertsContainer()
const improvedFlakyConnections = useFeatureFlag(
'intermittent-connection-improvements'
)
if (!globalAlertsContainer) {
return null
@@ -18,7 +22,8 @@ export const UnsavedDocs: FC = () => {
{isLocked &&
createPortal(<UnsavedDocsLockedAlert />, globalAlertsContainer)}
{unsavedDocs.size > 0 &&
{!improvedFlakyConnections &&
unsavedDocs.size > 0 &&
createPortal(
<UnsavedDocsAlert unsavedDocs={unsavedDocs} />,
globalAlertsContainer
@@ -26,9 +26,9 @@ type UnsavedDocsContextValue = {
isLocked: boolean
}
const UnsavedDocsContext = createContext<UnsavedDocsContextValue | undefined>(
undefined
)
export const UnsavedDocsContext = createContext<
UnsavedDocsContextValue | undefined
>(undefined)
export const UnsavedDocsProvider: FC<React.PropsWithChildren> = ({
children,