mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
React IDE page: add connection and SyncTex alerts (#15273)
Add connection and SyncTex alerts GitOrigin-RevId: 5004a0d356d0a0355d125516a18db1f57e617a7f
This commit is contained in:
parent
3be937c503
commit
87199c80fe
7 changed files with 140 additions and 1 deletions
|
@ -9,6 +9,10 @@ block vars
|
||||||
block entrypointVar
|
block entrypointVar
|
||||||
- entrypoint = 'pages/ide'
|
- entrypoint = 'pages/ide'
|
||||||
|
|
||||||
|
block css
|
||||||
|
each file in entrypointStyles('ide')
|
||||||
|
link(rel='stylesheet', href=file)
|
||||||
|
|
||||||
block content
|
block content
|
||||||
main#ide-root
|
main#ide-root
|
||||||
.loading-screen
|
.loading-screen
|
||||||
|
|
|
@ -260,6 +260,7 @@
|
||||||
"disable_sso": "",
|
"disable_sso": "",
|
||||||
"disable_stop_on_first_error": "",
|
"disable_stop_on_first_error": "",
|
||||||
"disabling": "",
|
"disabling": "",
|
||||||
|
"disconnected": "",
|
||||||
"discount_of": "",
|
"discount_of": "",
|
||||||
"dismiss": "",
|
"dismiss": "",
|
||||||
"dismiss_error_popup": "",
|
"dismiss_error_popup": "",
|
||||||
|
@ -307,6 +308,7 @@
|
||||||
"editing": "",
|
"editing": "",
|
||||||
"editing_captions": "",
|
"editing_captions": "",
|
||||||
"editor_and_pdf": "&",
|
"editor_and_pdf": "&",
|
||||||
|
"editor_disconected_click_to_reconnect": "",
|
||||||
"editor_only_hide_pdf": "",
|
"editor_only_hide_pdf": "",
|
||||||
"editor_theme": "",
|
"editor_theme": "",
|
||||||
"educational_discount_for_groups_of_x_or_more": "",
|
"educational_discount_for_groups_of_x_or_more": "",
|
||||||
|
@ -631,6 +633,7 @@
|
||||||
"logs_and_output_files": "",
|
"logs_and_output_files": "",
|
||||||
"looking_multiple_licenses": "",
|
"looking_multiple_licenses": "",
|
||||||
"looks_like_youre_at": "",
|
"looks_like_youre_at": "",
|
||||||
|
"lost_connection": "",
|
||||||
"main_document": "",
|
"main_document": "",
|
||||||
"main_file_not_found": "",
|
"main_file_not_found": "",
|
||||||
"make_a_copy": "",
|
"make_a_copy": "",
|
||||||
|
@ -678,6 +681,7 @@
|
||||||
"month": "",
|
"month": "",
|
||||||
"more": "",
|
"more": "",
|
||||||
"more_actions": "",
|
"more_actions": "",
|
||||||
|
"more_info": "",
|
||||||
"more_options_for_border_settings_coming_soon": "",
|
"more_options_for_border_settings_coming_soon": "",
|
||||||
"my_library": "",
|
"my_library": "",
|
||||||
"n_items": "",
|
"n_items": "",
|
||||||
|
@ -887,6 +891,8 @@
|
||||||
"recompile_from_scratch": "",
|
"recompile_from_scratch": "",
|
||||||
"recompile_pdf": "",
|
"recompile_pdf": "",
|
||||||
"reconnect": "",
|
"reconnect": "",
|
||||||
|
"reconnecting": "",
|
||||||
|
"reconnecting_in_x_secs": "",
|
||||||
"recurly_email_update_needed": "",
|
"recurly_email_update_needed": "",
|
||||||
"recurly_email_updated": "",
|
"recurly_email_updated": "",
|
||||||
"redirect_to_editor": "",
|
"redirect_to_editor": "",
|
||||||
|
@ -1102,6 +1108,7 @@
|
||||||
"sync_project_to_github_explanation": "",
|
"sync_project_to_github_explanation": "",
|
||||||
"sync_to_dropbox": "",
|
"sync_to_dropbox": "",
|
||||||
"sync_to_github": "",
|
"sync_to_github": "",
|
||||||
|
"synctex_failed": "",
|
||||||
"syntax_validation": "",
|
"syntax_validation": "",
|
||||||
"tab_connecting": "",
|
"tab_connecting": "",
|
||||||
"tab_no_longer_connected": "",
|
"tab_no_longer_connected": "",
|
||||||
|
@ -1222,6 +1229,7 @@
|
||||||
"tried_to_register_with_email": "",
|
"tried_to_register_with_email": "",
|
||||||
"try_again": "",
|
"try_again": "",
|
||||||
"try_it_for_free": "",
|
"try_it_for_free": "",
|
||||||
|
"try_now": "",
|
||||||
"try_premium_for_free": "",
|
"try_premium_for_free": "",
|
||||||
"try_recompile_project_or_troubleshoot": "",
|
"try_recompile_project_or_troubleshoot": "",
|
||||||
"try_relinking_provider": "",
|
"try_relinking_provider": "",
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { LostConnectionAlert } from './lost-connection-alert'
|
||||||
|
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
|
||||||
|
import { debugging } from '@/utils/debugging'
|
||||||
|
import { Alert } from 'react-bootstrap'
|
||||||
|
|
||||||
|
// TODO SavingNotificationController, SystemMessagesController, out-of-sync modal
|
||||||
|
export function Alerts() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
connectionState,
|
||||||
|
isConnected,
|
||||||
|
isStillReconnecting,
|
||||||
|
tryReconnectNow,
|
||||||
|
secondsUntilReconnect,
|
||||||
|
} = useConnectionContext()
|
||||||
|
|
||||||
|
// TODO: Get this from a context
|
||||||
|
const synctexError = false
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="global-alerts">
|
||||||
|
{connectionState.forceDisconnected ? (
|
||||||
|
<Alert bsStyle="warning" className="small">
|
||||||
|
<strong>{t('disconnected')}</strong>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{connectionState.reconnectAt ? (
|
||||||
|
<LostConnectionAlert
|
||||||
|
reconnectAt={connectionState.reconnectAt}
|
||||||
|
tryReconnectNow={tryReconnectNow}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{isStillReconnecting ? (
|
||||||
|
<Alert bsStyle="warning" className="small">
|
||||||
|
<strong>{t('reconnecting')}…</strong>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{synctexError ? (
|
||||||
|
<Alert bsStyle="warning" className="small">
|
||||||
|
<strong>{t('synctex_failed')}</strong>
|
||||||
|
<a
|
||||||
|
href="/learn/how-to/SyncTeX_Errors"
|
||||||
|
target="_blank"
|
||||||
|
id="synctex-more-info-button"
|
||||||
|
className="alert-link-as-btn pull-right"
|
||||||
|
>
|
||||||
|
{t('more_info')}
|
||||||
|
</a>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{connectionState.inactiveDisconnect ||
|
||||||
|
(connectionState.readyState === WebSocket.CLOSED &&
|
||||||
|
(connectionState.error === 'rate-limited' ||
|
||||||
|
connectionState.error === 'unable-to-connect') &&
|
||||||
|
!secondsUntilReconnect()) ? (
|
||||||
|
<Alert bsStyle="warning" className="small">
|
||||||
|
<strong>{t('editor_disconected_click_to_reconnect')}</strong>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{debugging ? (
|
||||||
|
<Alert bsStyle="warning" className="small">
|
||||||
|
<strong>Connected: {isConnected.toString()}</strong>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { secondsUntil } from '@/features/ide-react/connection/utils'
|
||||||
|
import { Alert } from 'react-bootstrap'
|
||||||
|
|
||||||
|
type LostConnectionAlertProps = {
|
||||||
|
reconnectAt: number
|
||||||
|
tryReconnectNow: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LostConnectionAlert({
|
||||||
|
reconnectAt,
|
||||||
|
tryReconnectNow,
|
||||||
|
}: LostConnectionAlertProps) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [secondsUntilReconnect, setSecondsUntilReconnect] = useState(
|
||||||
|
secondsUntil(reconnectAt)
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = window.setInterval(() => {
|
||||||
|
setSecondsUntilReconnect(secondsUntil(reconnectAt))
|
||||||
|
}, 1000)
|
||||||
|
return () => window.clearInterval(timer)
|
||||||
|
}, [reconnectAt])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert bsStyle="warning" className="small">
|
||||||
|
<strong>{t('lost_connection')}</strong>{' '}
|
||||||
|
{t('reconnecting_in_x_secs', { seconds: secondsUntilReconnect })}.
|
||||||
|
<button
|
||||||
|
id="try-reconnect-now-button"
|
||||||
|
className="pull-right"
|
||||||
|
onClick={() => tryReconnectNow()}
|
||||||
|
>
|
||||||
|
{t('try_now')}
|
||||||
|
</button>
|
||||||
|
</Alert>
|
||||||
|
)
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import LayoutWithPlaceholders from '@/features/ide-react/components/layout/layou
|
||||||
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
|
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
|
||||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
|
import { Alerts } from '@/features/ide-react/components/alerts/alerts'
|
||||||
|
|
||||||
// This is filled with placeholder content while the real content is migrated
|
// This is filled with placeholder content while the real content is migrated
|
||||||
// away from Angular
|
// away from Angular
|
||||||
|
@ -23,7 +24,8 @@ export default function IdePage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* TODO: Alerts and left menu will go here */}
|
<Alerts />
|
||||||
|
{/* TODO: Left menu will go here */}
|
||||||
<LayoutWithPlaceholders shouldPersistLayout />
|
<LayoutWithPlaceholders shouldPersistLayout />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
#ide-root {
|
#ide-root {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
|
.global-alerts {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ide-react-main {
|
.ide-react-main {
|
||||||
|
|
|
@ -543,6 +543,7 @@
|
||||||
color: darken(@text-color, 10%);
|
color: darken(@text-color, 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
.alert-link-as-btn {
|
.alert-link-as-btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -557,6 +558,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
small,
|
small,
|
||||||
.small {
|
.small {
|
||||||
color: @text-color;
|
color: @text-color;
|
||||||
|
|
Loading…
Reference in a new issue