mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-06 07:39:02 +02:00
Merge pull request #5201 from overleaf/msm-pdf-viewer-error-boundaries
Error boundaries for React PDF viewer GitOrigin-RevId: 90052fc183f7ece8125ecfb0410a529cf905c13b
This commit is contained in:
@@ -712,6 +712,7 @@ module.exports = {
|
||||
publishModal: [],
|
||||
tprLinkedFileInfo: [],
|
||||
tprLinkedFileRefreshError: [],
|
||||
contactUsModal: [],
|
||||
},
|
||||
|
||||
moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'],
|
||||
|
||||
@@ -54,7 +54,9 @@
|
||||
"compiling": "",
|
||||
"conflicting_paths_found": "",
|
||||
"connected_users": "",
|
||||
"contact_message_label": "",
|
||||
"continue_github_merge": "",
|
||||
"contact_us": "",
|
||||
"copy": "",
|
||||
"copy_project": "",
|
||||
"copying": "",
|
||||
@@ -179,6 +181,7 @@
|
||||
"log_hint_extra_info": "",
|
||||
"logs_pane_info_message": "",
|
||||
"logs_pane_info_message_popup": "",
|
||||
"log_viewer_error": "",
|
||||
"main_file_not_found": "",
|
||||
"make_private": "",
|
||||
"manage_files_from_your_dropbox_folder": "",
|
||||
@@ -231,7 +234,9 @@
|
||||
"pdf_compile_in_progress_error": "",
|
||||
"pdf_compile_rate_limit_hit": "",
|
||||
"pdf_compile_try_again": "",
|
||||
"pdf_preview_error": "",
|
||||
"pdf_rendering_error": "",
|
||||
"pdf_viewer_error": "",
|
||||
"please_compile_pdf_before_download": "",
|
||||
"please_refresh": "",
|
||||
"please_select_a_file": "",
|
||||
@@ -252,6 +257,7 @@
|
||||
"project_too_large": "",
|
||||
"project_too_large_please_reduce": "",
|
||||
"project_too_much_editable_text": "",
|
||||
"project_url" : "",
|
||||
"public": "",
|
||||
"pull_github_changes_into_sharelatex": "",
|
||||
"push_sharelatex_changes_to_github": "",
|
||||
@@ -307,6 +313,7 @@
|
||||
"stop_compile": "",
|
||||
"stop_on_validation_error": "",
|
||||
"store_your_work": "",
|
||||
"subject": "",
|
||||
"submit_title": "",
|
||||
"sure_you_want_to_delete": "",
|
||||
"sync_project_to_github_explanation": "",
|
||||
@@ -328,6 +335,8 @@
|
||||
"too_recently_compiled": "",
|
||||
"total_words": "",
|
||||
"try_it_for_free": "",
|
||||
"try_recompile_project": "",
|
||||
"try_refresh_page": "",
|
||||
"turn_off_link_sharing": "",
|
||||
"turn_on_link_sharing": "",
|
||||
"unlimited_projects": "",
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useUserContext } from '../../../shared/context/user-context'
|
||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import { FetchError } from '../../../infrastructure/fetch-json'
|
||||
import { useChatContext } from '../context/chat-context'
|
||||
import LoadingSpinner from '../../../shared/components/loading-spinner'
|
||||
|
||||
const ChatPane = React.memo(function ChatPane() {
|
||||
const { t } = useTranslation()
|
||||
@@ -87,16 +88,6 @@ const ChatPane = React.memo(function ChatPane() {
|
||||
)
|
||||
})
|
||||
|
||||
function LoadingSpinner() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="loading">
|
||||
<Icon type="fw" modifier="refresh" spin />
|
||||
{` ${t('loading')}…`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Placeholder() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Alert } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useState } from 'react'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
|
||||
const [contactUsModalModules] = importOverleafModules('contactUsModal')
|
||||
const ContactUsModal = contactUsModalModules?.import.default
|
||||
|
||||
function ErrorBoundaryFallback({ type }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [showContactUsModal, setShowContactUsModal] = useState(false)
|
||||
|
||||
function handleContactUsClick() {
|
||||
setShowContactUsModal(true)
|
||||
}
|
||||
|
||||
function handleContactUsModalHide() {
|
||||
setShowContactUsModal(false)
|
||||
}
|
||||
|
||||
if (!ContactUsModal) {
|
||||
return (
|
||||
<div className="pdf-error-alert">
|
||||
<Alert bsStyle="danger">
|
||||
{`${t('generic_something_went_wrong')}. ${t('please_refresh')}`}
|
||||
</Alert>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// we create each instance of `<Trans/>` individually so `i18next-scanner` can detect hardcoded `i18nKey` values
|
||||
let content
|
||||
if (type === 'pdf') {
|
||||
content = (
|
||||
<>
|
||||
<p>{t('pdf_viewer_error')}</p>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="try_recompile_project"
|
||||
components={[<a href="#" onClick={handleContactUsClick} />]} // eslint-disable-line react/jsx-key, jsx-a11y/anchor-has-content, jsx-a11y/anchor-is-valid
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
} else if (type === 'logs') {
|
||||
content = (
|
||||
<>
|
||||
<p>{t('log_viewer_error')}</p>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="try_recompile_project"
|
||||
components={[<a href="#" onClick={handleContactUsClick} />]} // eslint-disable-line react/jsx-key, jsx-a11y/anchor-has-content, jsx-a11y/anchor-is-valid
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
content = (
|
||||
<>
|
||||
<p>{t('pdf_preview_error')}</p>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="try_refresh_page"
|
||||
components={[<a href="#" onClick={handleContactUsClick} />]} // eslint-disable-line react/jsx-key, jsx-a11y/anchor-has-content, jsx-a11y/anchor-is-valid
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pdf-error-alert">
|
||||
<Alert bsStyle="danger">{content}</Alert>
|
||||
<ContactUsModal
|
||||
show={showContactUsModal}
|
||||
handleHide={handleContactUsModalHide}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ErrorBoundaryFallback.propTypes = {
|
||||
type: PropTypes.oneOf(['preview', 'pdf', 'logs']).isRequired,
|
||||
}
|
||||
|
||||
export default ErrorBoundaryFallback
|
||||
@@ -8,6 +8,8 @@ import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
||||
import useScopeValue from '../../../shared/context/util/scope-value-hook'
|
||||
import { buildHighlightElement } from '../util/highlights'
|
||||
import PDFJSWrapper from '../util/pdf-js-wrapper'
|
||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import ErrorBoundaryFallback from './error-boundary-fallback'
|
||||
|
||||
function PdfJsViewer({ url }) {
|
||||
const { _id: projectId } = useProjectContext()
|
||||
@@ -248,4 +250,6 @@ PdfJsViewer.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default memo(PdfJsViewer)
|
||||
export default withErrorBoundary(memo(PdfJsViewer), () => (
|
||||
<ErrorBoundaryFallback type="pdf" />
|
||||
))
|
||||
|
||||
@@ -9,6 +9,8 @@ import PdfPreviewError from './pdf-preview-error'
|
||||
import PdfClearCacheButton from './pdf-clear-cache-button'
|
||||
import PdfDownloadFilesButton from './pdf-download-files-button'
|
||||
import PdfLogsEntries from './pdf-logs-entries'
|
||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import ErrorBoundaryFallback from './error-boundary-fallback'
|
||||
|
||||
function PdfLogsViewer() {
|
||||
const {
|
||||
@@ -67,4 +69,6 @@ function PdfLogsViewer() {
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PdfLogsViewer)
|
||||
export default withErrorBoundary(memo(PdfLogsViewer), () => (
|
||||
<ErrorBoundaryFallback type="logs" />
|
||||
))
|
||||
|
||||
@@ -2,8 +2,8 @@ import { memo, Suspense } from 'react'
|
||||
import PdfLogsViewer from './pdf-logs-viewer'
|
||||
import PdfViewer from './pdf-viewer'
|
||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import PdfPreviewToolbar from './pdf-preview-toolbar'
|
||||
import LoadingSpinner from '../../../shared/components/loading-spinner'
|
||||
|
||||
function PdfPreviewPane() {
|
||||
const { showLogs } = usePdfPreviewContext()
|
||||
@@ -11,7 +11,7 @@ function PdfPreviewPane() {
|
||||
return (
|
||||
<div className="pdf full-size">
|
||||
<PdfPreviewToolbar />
|
||||
<Suspense fallback={<div>Loading…</div>}>
|
||||
<Suspense fallback={<LoadingPreview />}>
|
||||
<div className="pdf-viewer">
|
||||
<PdfViewer />
|
||||
</div>
|
||||
@@ -21,4 +21,12 @@ function PdfPreviewPane() {
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(withErrorBoundary(PdfPreviewPane))
|
||||
function LoadingPreview() {
|
||||
return (
|
||||
<div className="pdf-loading-spinner-container">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PdfPreviewPane)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import PdfPreviewProvider from '../contexts/pdf-preview-context'
|
||||
import PdfPreviewPane from './pdf-preview-pane'
|
||||
import { memo } from 'react'
|
||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import ErrorBoundaryFallback from './error-boundary-fallback'
|
||||
|
||||
function PdfPreview() {
|
||||
return (
|
||||
@@ -10,4 +12,6 @@ function PdfPreview() {
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PdfPreview)
|
||||
export default withErrorBoundary(memo(PdfPreview), () => (
|
||||
<ErrorBoundaryFallback type="preview" />
|
||||
))
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Icon from './icon'
|
||||
|
||||
function LoadingSpinner() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="loading">
|
||||
<Icon type="fw" modifier="refresh" spin />
|
||||
{` ${t('loading')}…`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoadingSpinner
|
||||
@@ -8,6 +8,7 @@ UserContext.Provider.propTypes = {
|
||||
value: PropTypes.shape({
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
email: PropTypes.string,
|
||||
allowedFreeTrial: PropTypes.boolean,
|
||||
first_name: PropTypes.string,
|
||||
last_name: PropTypes.string,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useState } from 'react'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
import ContactUsModal from '../../modules/support/frontend/js/components/contact-us-modal'
|
||||
import { withContextRoot } from './utils/with-context-root'
|
||||
|
||||
export const Generic = () => {
|
||||
const [show, setShow] = useState(true)
|
||||
const handleHide = () => setShow(false)
|
||||
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post('express:/support', { status: 200 }, { delay: 1000 })
|
||||
})
|
||||
|
||||
return withContextRoot(<ContactUsModal show={show} handleHide={handleHide} />)
|
||||
}
|
||||
|
||||
export const RequestError = args => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post('express:/support', { status: 404 }, { delay: 250 })
|
||||
})
|
||||
|
||||
return withContextRoot(<ContactUsModal {...args} />)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Modals / Contact Us',
|
||||
component: ContactUsModal,
|
||||
args: {
|
||||
show: true,
|
||||
handleHide: () => {},
|
||||
},
|
||||
argTypes: {
|
||||
handleHide: { action: 'close modal' },
|
||||
},
|
||||
}
|
||||
@@ -538,3 +538,19 @@
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.pdf-error-alert {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: @pdf-bg;
|
||||
padding: @line-height-computed / 2;
|
||||
}
|
||||
|
||||
.pdf-loading-spinner-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -701,6 +701,11 @@
|
||||
"open_a_file_on_the_left": "Open a file on the left",
|
||||
"reference_error_relink_hint": "If this error persists, try re-linking your account here:",
|
||||
"pdf_rendering_error": "PDF Rendering Error",
|
||||
"pdf_preview_error": "There was a problem displaying the compilation results for this project. This is an internal __appName__ issue, not a problem with your LaTeX code.",
|
||||
"pdf_viewer_error": "There was a problem displaying this project PDF. This is an internal __appName__ issue, not a problem with your LaTeX code.",
|
||||
"log_viewer_error": "There was a problem displaying this project compilation errors and logs. This is an internal __appName__ issue, not a problem with your LaTeX code.",
|
||||
"try_recompile_project": "Please try recompiling the project. If the problem persists, <0>contact us</0>",
|
||||
"try_refresh_page": "Please try refreshing this page. If the problem persists, <0>contact us</0>.",
|
||||
"something_went_wrong_rendering_pdf": "Something went wrong while rendering this PDF.",
|
||||
"mendeley_reference_loading_error_expired": "Mendeley token expired, please re-link your account",
|
||||
"zotero_reference_loading_error_expired": "Zotero token expired, please re-link your account",
|
||||
|
||||
@@ -14,7 +14,7 @@ import { SplitTestProvider } from '../../../frontend/js/shared/context/split-tes
|
||||
import { CompileProvider } from '../../../frontend/js/shared/context/compile-context'
|
||||
|
||||
export function EditorProviders({
|
||||
user = { id: '123abd' },
|
||||
user = { id: '123abd', email: 'testuser@example.com' },
|
||||
projectId = 'project123',
|
||||
socket = {
|
||||
on: sinon.stub(),
|
||||
|
||||
Reference in New Issue
Block a user