diff --git a/services/web/app/views/project/ide-react.pug b/services/web/app/views/project/ide-react.pug index 3d7b5213e6..283629f294 100644 --- a/services/web/app/views/project/ide-react.pug +++ b/services/web/app/views/project/ide-react.pug @@ -9,6 +9,10 @@ block vars block entrypointVar - entrypoint = 'pages/ide' +block css + each file in entrypointStyles('ide') + link(rel='stylesheet', href=file) + block content main#ide-root .loading-screen diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index c7c9e72960..6fa147eb0a 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -260,6 +260,7 @@ "disable_sso": "", "disable_stop_on_first_error": "", "disabling": "", + "disconnected": "", "discount_of": "", "dismiss": "", "dismiss_error_popup": "", @@ -307,6 +308,7 @@ "editing": "", "editing_captions": "", "editor_and_pdf": "&", + "editor_disconected_click_to_reconnect": "", "editor_only_hide_pdf": "", "editor_theme": "", "educational_discount_for_groups_of_x_or_more": "", @@ -631,6 +633,7 @@ "logs_and_output_files": "", "looking_multiple_licenses": "", "looks_like_youre_at": "", + "lost_connection": "", "main_document": "", "main_file_not_found": "", "make_a_copy": "", @@ -678,6 +681,7 @@ "month": "", "more": "", "more_actions": "", + "more_info": "", "more_options_for_border_settings_coming_soon": "", "my_library": "", "n_items": "", @@ -887,6 +891,8 @@ "recompile_from_scratch": "", "recompile_pdf": "", "reconnect": "", + "reconnecting": "", + "reconnecting_in_x_secs": "", "recurly_email_update_needed": "", "recurly_email_updated": "", "redirect_to_editor": "", @@ -1102,6 +1108,7 @@ "sync_project_to_github_explanation": "", "sync_to_dropbox": "", "sync_to_github": "", + "synctex_failed": "", "syntax_validation": "", "tab_connecting": "", "tab_no_longer_connected": "", @@ -1222,6 +1229,7 @@ "tried_to_register_with_email": "", "try_again": "", "try_it_for_free": "", + "try_now": "", "try_premium_for_free": "", "try_recompile_project_or_troubleshoot": "", "try_relinking_provider": "", 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 new file mode 100644 index 0000000000..f76544df50 --- /dev/null +++ b/services/web/frontend/js/features/ide-react/components/alerts/alerts.tsx @@ -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 ( +
+ {connectionState.forceDisconnected ? ( + + {t('disconnected')} + + ) : null} + + {connectionState.reconnectAt ? ( + + ) : null} + + {isStillReconnecting ? ( + + {t('reconnecting')}… + + ) : null} + + {synctexError ? ( + + {t('synctex_failed')} + + {t('more_info')} + + + ) : 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} +
+ ) +} diff --git a/services/web/frontend/js/features/ide-react/components/alerts/lost-connection-alert.tsx b/services/web/frontend/js/features/ide-react/components/alerts/lost-connection-alert.tsx new file mode 100644 index 0000000000..6592491bab --- /dev/null +++ b/services/web/frontend/js/features/ide-react/components/alerts/lost-connection-alert.tsx @@ -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 ( + + {t('lost_connection')}{' '} + {t('reconnecting_in_x_secs', { seconds: secondsUntilReconnect })}. + + + ) +} diff --git a/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx b/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx index 795def99d6..90c5185acb 100644 --- a/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx +++ b/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx @@ -2,6 +2,7 @@ import LayoutWithPlaceholders from '@/features/ide-react/components/layout/layou import { useConnectionContext } from '@/features/ide-react/context/connection-context' import useEventListener from '@/shared/hooks/use-event-listener' 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 // away from Angular @@ -23,7 +24,8 @@ export default function IdePage() { return ( <> - {/* TODO: Alerts and left menu will go here */} + + {/* TODO: Left menu will go here */} ) diff --git a/services/web/frontend/stylesheets/app/editor/ide-react.less b/services/web/frontend/stylesheets/app/editor/ide-react.less index 51d611fa01..9ad548f92b 100644 --- a/services/web/frontend/stylesheets/app/editor/ide-react.less +++ b/services/web/frontend/stylesheets/app/editor/ide-react.less @@ -1,5 +1,12 @@ #ide-root { height: 100vh; + + .global-alerts { + position: absolute; + top: 0; + left: 0; + right: 0; + } } .ide-react-main { diff --git a/services/web/frontend/stylesheets/core/mixins.less b/services/web/frontend/stylesheets/core/mixins.less index 998d394a0a..ca219ddd76 100755 --- a/services/web/frontend/stylesheets/core/mixins.less +++ b/services/web/frontend/stylesheets/core/mixins.less @@ -543,6 +543,7 @@ color: darken(@text-color, 10%); } + button, .alert-link-as-btn { display: inline-block; font-weight: bold; @@ -557,6 +558,10 @@ } } + button { + border-width: 0; + } + small, .small { color: @text-color;