Merge pull request #16335 from overleaf/ae-real-time-down

[ide-react] Improve handling of lost connection

GitOrigin-RevId: 89b641b2beca4f9de65551e6873b3c8c11bb1695
This commit is contained in:
Alf Eaton
2024-01-04 09:03:05 +00:00
committed by Copybot
parent ecfa15cf57
commit eb3e5037f8
9 changed files with 88 additions and 12 deletions
@@ -4,6 +4,8 @@ import { useConnectionContext } from '@/features/ide-react/context/connection-co
import { debugging } from '@/utils/debugging'
import { Alert } from 'react-bootstrap'
import useScopeValue from '@/shared/hooks/use-scope-value'
import { createPortal } from 'react-dom'
import { useGlobalAlertsContainer } from '@/features/ide-react/context/global-alerts-context'
export function Alerts() {
const { t } = useTranslation()
@@ -14,11 +16,16 @@ export function Alerts() {
tryReconnectNow,
secondsUntilReconnect,
} = useConnectionContext()
const globalAlertsContainer = useGlobalAlertsContainer()
const [synctexError] = useScopeValue('sync_tex_error')
return (
<div className="global-alerts">
if (!globalAlertsContainer) {
return null
}
return createPortal(
<>
{connectionState.forceDisconnected ? (
<Alert bsStyle="danger" className="small">
<strong>{t('disconnected')}</strong>
@@ -67,6 +74,7 @@ export function Alerts() {
<strong>Connected: {isConnected.toString()}</strong>
</Alert>
) : null}
</div>
</>,
globalAlertsContainer
)
}
@@ -11,7 +11,7 @@ export function FileTree() {
const user = useUserContext()
const { indexAllReferences } = useReferencesContext()
const { setStartedFreeTrial } = useIdeReactContext()
const { isConnected } = useConnectionContext()
const { isConnected, connectionState } = useConnectionContext()
const { handleFileTreeInit, handleFileTreeSelect, handleFileTreeDelete } =
useFileTreeOpenContext()
@@ -37,7 +37,7 @@ export function FileTree() {
reindexReferences={reindexReferences}
setRefProviderEnabled={setRefProviderEnabled}
setStartedFreeTrial={setStartedFreeTrial}
isConnected={isConnected}
isConnected={isConnected || connectionState.reconnectAt !== null}
onInit={handleFileTreeInit}
onSelect={handleFileTreeSelect}
onDelete={handleFileTreeDelete}
@@ -8,6 +8,7 @@ import { useEditingSessionHeartbeat } from '@/features/ide-react/hooks/use-editi
import { useRegisterUserActivity } from '@/features/ide-react/hooks/use-register-user-activity'
import { useHasLintingError } from '@/features/ide-react/hooks/use-has-linting-error'
import { Modals } from '@/features/ide-react/components/modals/modals'
import { GlobalAlertsProvider } from '@/features/ide-react/context/global-alerts-context'
export default function IdePage() {
useLayoutEventTracking() // sent event when the layout changes
@@ -18,11 +19,11 @@ export default function IdePage() {
useOpenFile() // create ide.binaryFilesManager (TODO: move to the history file restore component)
return (
<>
<GlobalAlertsProvider>
<Alerts />
<Modals />
<EditorLeftMenu />
<MainLayout />
</>
</GlobalAlertsProvider>
)
}
@@ -6,14 +6,14 @@ import { useTranslation } from 'react-i18next'
export const UnsavedDocsAlert: FC<{ unsavedDocs: Map<string, number> }> = ({
unsavedDocs,
}) => (
<div className="global-alerts">
<>
{[...unsavedDocs.entries()].map(
([docId, seconds]) =>
seconds > 8 && (
<UnsavedDocAlert key={docId} docId={docId} seconds={seconds} />
)
)}
</div>
</>
)
const UnsavedDocAlert: FC<{ docId: string; seconds: number }> = ({
@@ -8,6 +8,7 @@ export const UnsavedDocsLockedModal: FC = () => {
return (
<AccessibleModal
show
onHide={() => {}} // It's not possible to hide this modal, but it's a required prop
className="lock-editor-modal"
backdrop={false}
@@ -5,6 +5,8 @@ import { PermissionsLevel } from '@/features/ide-react/types/permissions'
import { UnsavedDocsLockedModal } from '@/features/ide-react/components/unsaved-docs/unsaved-docs-locked-modal'
import { UnsavedDocsAlert } from '@/features/ide-react/components/unsaved-docs/unsaved-docs-alert'
import useEventListener from '@/shared/hooks/use-event-listener'
import { createPortal } from 'react-dom'
import { useGlobalAlertsContainer } from '@/features/ide-react/context/global-alerts-context'
const MAX_UNSAVED_SECONDS = 15 // lock the editor after this time if unsaved
@@ -13,6 +15,7 @@ export const UnsavedDocs: FC = () => {
const { permissionsLevel, setPermissionsLevel } = useEditorContext()
const [isLocked, setIsLocked] = useState(false)
const [unsavedDocs, setUnsavedDocs] = useState(new Map<string, number>())
const globalAlertsContainer = useGlobalAlertsContainer()
// always contains the latest value
const previousUnsavedDocsRef = useRef(unsavedDocs)
@@ -99,7 +102,12 @@ export const UnsavedDocs: FC = () => {
return (
<>
{isLocked && <UnsavedDocsLockedModal />}
{unsavedDocs.size > 0 && <UnsavedDocsAlert unsavedDocs={unsavedDocs} />}
{unsavedDocs.size > 0 &&
globalAlertsContainer &&
createPortal(
<UnsavedDocsAlert unsavedDocs={unsavedDocs} />,
globalAlertsContainer
)}
</>
)
}