Просмотр исходного кода

React IDE page: add connection and SyncTex alerts (#15273)

Add connection and SyncTex alerts

GitOrigin-RevId: 5004a0d356d0a0355d125516a18db1f57e617a7f
Tim Down 2 лет назад
Родитель
Сommit
87199c80fe

+ 4 - 0
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

+ 8 - 0
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": "",

+ 73 - 0
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 (
+    <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>
+  )
+}

+ 40 - 0
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 (
+    <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>
+  )
+}

+ 3 - 1
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 */}
+      <Alerts />
+      {/* TODO: Left menu will go here */}
       <LayoutWithPlaceholders shouldPersistLayout />
     </>
   )

+ 7 - 0
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 {

+ 5 - 0
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;