mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 13:13:45 -05:00
Handle PDF preview on toggle between split and full-width views (#5470)
* Only hide the compile logs pane when toggled off * Handle PDF preview on toggle between split and full-width views GitOrigin-RevId: 9ceca8a06a22abfa78f245e1ae5d24af98215906
This commit is contained in:
parent
90befc1fdd
commit
f7ef2532e0
22 changed files with 500 additions and 642 deletions
|
@ -18,13 +18,10 @@ div.full-size(
|
||||||
include ./editor-no-symbol-palette
|
include ./editor-no-symbol-palette
|
||||||
|
|
||||||
.ui-layout-east
|
.ui-layout-east
|
||||||
// The pdf-preview component needs to always be rendered, even when the editor is in "full-width" mode and it's not visible.
|
div(ng-if="ui.pdfLayout == 'sideBySide'")
|
||||||
// It doesn't recompile while hidden, due to the ui.pdfHidden flag, but maintains its state for when it's shown again.
|
|
||||||
if showNewPdfPreview
|
if showNewPdfPreview
|
||||||
div(ng-show="ui.pdfLayout == 'sideBySide'")
|
|
||||||
pdf-preview()
|
pdf-preview()
|
||||||
else
|
else
|
||||||
div(ng-if="ui.pdfLayout == 'sideBySide'")
|
|
||||||
include ./pdf
|
include ./pdf
|
||||||
|
|
||||||
.ui-layout-resizer-controls.synctex-controls(
|
.ui-layout-resizer-controls.synctex-controls(
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfClearCacheButton() {
|
function PdfClearCacheButton() {
|
||||||
const { compiling, clearCache, clearingCache } = usePdfPreviewContext()
|
const { compiling, clearCache, clearingCache } = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,9 @@ import {
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
const modifierKey = /Mac/i.test(navigator.platform) ? 'Cmd' : 'Ctrl'
|
const modifierKey = /Mac/i.test(navigator.platform) ? 'Cmd' : 'Ctrl'
|
||||||
|
|
||||||
|
@ -23,11 +23,11 @@ function PdfCompileButton() {
|
||||||
setAutoCompile,
|
setAutoCompile,
|
||||||
setDraft,
|
setDraft,
|
||||||
setStopOnValidationError,
|
setStopOnValidationError,
|
||||||
|
stopOnValidationError,
|
||||||
startCompile,
|
startCompile,
|
||||||
stopCompile,
|
stopCompile,
|
||||||
stopOnValidationError,
|
|
||||||
recompileFromScratch,
|
recompileFromScratch,
|
||||||
} = usePdfPreviewContext()
|
} = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { Button, Dropdown } from 'react-bootstrap'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||||
import PdfFileList from './pdf-file-list'
|
import PdfFileList from './pdf-file-list'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfDownloadButton() {
|
function PdfDownloadButton() {
|
||||||
const { compiling, pdfDownloadUrl, fileList } = usePdfPreviewContext()
|
const { compiling, pdfDownloadUrl, fileList } = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { Dropdown } from 'react-bootstrap'
|
||||||
import PdfFileList from './pdf-file-list'
|
import PdfFileList from './pdf-file-list'
|
||||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfDownloadFilesButton() {
|
function PdfDownloadFilesButton() {
|
||||||
const { compiling, fileList } = usePdfPreviewContext()
|
const { compiling, fileList } = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { memo, useMemo } from 'react'
|
import { memo, useMemo } from 'react'
|
||||||
|
import { useLayoutContext } from '../../../shared/context/layout-context'
|
||||||
|
|
||||||
function PdfExpandButton() {
|
function PdfExpandButton() {
|
||||||
const { pdfLayout, switchLayout } = usePdfPreviewContext()
|
const { pdfLayout, switchLayout } = useLayoutContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { sendMBOnce } from '../../../infrastructure/event-tracking'
|
import { sendMBOnce } from '../../../infrastructure/event-tracking'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfHybridCodeCheckButton() {
|
function PdfHybridCodeCheckButton() {
|
||||||
const { codeCheckFailed, error, setShowLogs } = usePdfPreviewContext()
|
const { codeCheckFailed, error, setShowLogs } = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfHybridDownloadButton() {
|
function PdfHybridDownloadButton() {
|
||||||
const { pdfDownloadUrl } = usePdfPreviewContext()
|
const { pdfDownloadUrl } = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Button, Label, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
import { Button, Label, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { sendMBOnce } from '../../../infrastructure/event-tracking'
|
import { sendMBOnce } from '../../../infrastructure/event-tracking'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfHybridLogsButton() {
|
function PdfHybridLogsButton() {
|
||||||
const { error, logEntries, setShowLogs, showLogs } = usePdfPreviewContext()
|
const { error, logEntries, setShowLogs, showLogs } = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,6 @@ function PdfJsViewer({ url }) {
|
||||||
`pdf-viewer-scale:${projectId}`,
|
`pdf-viewer-scale:${projectId}`,
|
||||||
'page-width'
|
'page-width'
|
||||||
)
|
)
|
||||||
const [, setScrollTop] = usePersistedState(
|
|
||||||
`pdf-viewer-scroll-top:${projectId}`,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
|
|
||||||
// state values shared with Angular scope (highlights => editor, position => synctex buttons
|
// state values shared with Angular scope (highlights => editor, position => synctex buttons
|
||||||
const [highlights] = useScopeValue('pdf.highlights')
|
const [highlights] = useScopeValue('pdf.highlights')
|
||||||
|
@ -63,23 +59,10 @@ function PdfJsViewer({ url }) {
|
||||||
}
|
}
|
||||||
}, [pdfJsWrapper, url])
|
}, [pdfJsWrapper, url])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (pdfJsWrapper) {
|
|
||||||
// listen for 'pdf:scroll-to-position' events
|
|
||||||
const eventListener = event => {
|
|
||||||
pdfJsWrapper.container.scrollTop = event.data.position
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('pdf:scroll-to-position', eventListener)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('pdf:scroll-to-position', eventListener)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [pdfJsWrapper])
|
|
||||||
|
|
||||||
// listen for scroll events
|
// listen for scroll events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let storePositionTimer
|
||||||
|
|
||||||
if (initialised && pdfJsWrapper) {
|
if (initialised && pdfJsWrapper) {
|
||||||
// store the scroll position in localStorage, for the synctex button
|
// store the scroll position in localStorage, for the synctex button
|
||||||
const storePosition = debounce(pdfViewer => {
|
const storePosition = debounce(pdfViewer => {
|
||||||
|
@ -87,33 +70,30 @@ function PdfJsViewer({ url }) {
|
||||||
try {
|
try {
|
||||||
setPosition(pdfViewer.currentPosition)
|
setPosition(pdfViewer.currentPosition)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// TODO
|
// TODO: investigate handling missing offsetParent in jsdom
|
||||||
console.error(error)
|
// console.error(error)
|
||||||
}
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
// store the scroll position in localStorage, for use when reloading
|
storePositionTimer = window.setTimeout(() => {
|
||||||
const storeScrollTop = debounce(pdfViewer => {
|
|
||||||
// set position for "sync to code" button
|
|
||||||
setScrollTop(pdfViewer.container.scrollTop)
|
|
||||||
}, 500)
|
|
||||||
|
|
||||||
storePosition(pdfJsWrapper)
|
storePosition(pdfJsWrapper)
|
||||||
|
}, 100)
|
||||||
|
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
storeScrollTop(pdfJsWrapper)
|
|
||||||
storePosition(pdfJsWrapper)
|
storePosition(pdfJsWrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
pdfJsWrapper.container.addEventListener('scroll', scrollListener)
|
pdfJsWrapper.container.addEventListener('scroll', scrollListener)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
storePosition.cancel()
|
|
||||||
storeScrollTop.cancel()
|
|
||||||
pdfJsWrapper.container.removeEventListener('scroll', scrollListener)
|
pdfJsWrapper.container.removeEventListener('scroll', scrollListener)
|
||||||
|
if (storePositionTimer) {
|
||||||
|
window.clearTimeout(storePositionTimer)
|
||||||
|
}
|
||||||
|
storePosition.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [setPosition, setScrollTop, pdfJsWrapper, initialised])
|
}, [setPosition, pdfJsWrapper, initialised])
|
||||||
|
|
||||||
// listen for double-click events
|
// listen for double-click events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -147,14 +127,14 @@ function PdfJsViewer({ url }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// restore the scroll position
|
// restore the scroll position
|
||||||
setScrollTop(scrollTop => {
|
setPosition(position => {
|
||||||
if (scrollTop > 0) {
|
if (position) {
|
||||||
pdfJsWrapper.container.scrollTop = scrollTop
|
pdfJsWrapper.currentPosition = position
|
||||||
}
|
}
|
||||||
return scrollTop
|
return position
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [initialised, setScale, setScrollTop, pdfJsWrapper])
|
}, [initialised, setScale, setPosition, pdfJsWrapper])
|
||||||
|
|
||||||
// transmit scale value to the viewer when it changes
|
// transmit scale value to the viewer when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { memo, useCallback, useMemo } from 'react'
|
import { memo, useCallback, useMemo } from 'react'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { PdfLogsButtonContent } from './pdf-logs-button-content'
|
import { PdfLogsButtonContent } from './pdf-logs-button-content'
|
||||||
import { sendMBOnce } from '../../../infrastructure/event-tracking'
|
import { sendMBOnce } from '../../../infrastructure/event-tracking'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfLogsButton() {
|
function PdfLogsButton() {
|
||||||
const {
|
const {
|
||||||
|
@ -11,7 +11,7 @@ function PdfLogsButton() {
|
||||||
logEntries,
|
logEntries,
|
||||||
showLogs,
|
showLogs,
|
||||||
setShowLogs,
|
setShowLogs,
|
||||||
} = usePdfPreviewContext()
|
} = useCompileContext()
|
||||||
|
|
||||||
const buttonStyle = useMemo(() => {
|
const buttonStyle = useMemo(() => {
|
||||||
if (showLogs) {
|
if (showLogs) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import PreviewLogsPaneEntry from '../../preview/components/preview-logs-pane-entry'
|
import PreviewLogsPaneEntry from '../../preview/components/preview-logs-pane-entry'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
import PdfValidationIssue from './pdf-validation-issue'
|
import PdfValidationIssue from './pdf-validation-issue'
|
||||||
import TimeoutUpgradePrompt from './timeout-upgrade-prompt'
|
import TimeoutUpgradePrompt from './timeout-upgrade-prompt'
|
||||||
import PdfPreviewError from './pdf-preview-error'
|
import PdfPreviewError from './pdf-preview-error'
|
||||||
|
@ -12,6 +12,7 @@ import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||||
import ErrorBoundaryFallback from './error-boundary-fallback'
|
import ErrorBoundaryFallback from './error-boundary-fallback'
|
||||||
import PdfCodeCheckFailedNotice from '../../preview/components/pdf-code-check-failed-notice'
|
import PdfCodeCheckFailedNotice from '../../preview/components/pdf-code-check-failed-notice'
|
||||||
import PdfLogsPaneInfoNotice from '../../preview/components/pdf-logs-pane-info-notice'
|
import PdfLogsPaneInfoNotice from '../../preview/components/pdf-logs-pane-info-notice'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
function PdfLogsViewer() {
|
function PdfLogsViewer() {
|
||||||
const {
|
const {
|
||||||
|
@ -20,12 +21,13 @@ function PdfLogsViewer() {
|
||||||
logEntries,
|
logEntries,
|
||||||
rawLog,
|
rawLog,
|
||||||
validationIssues,
|
validationIssues,
|
||||||
} = usePdfPreviewContext()
|
showLogs,
|
||||||
|
} = useCompileContext()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs-pane">
|
<div className={classnames('logs-pane', { hidden: !showLogs })}>
|
||||||
<div className="logs-pane-content">
|
<div className="logs-pane-content">
|
||||||
<PdfLogsPaneInfoNotice />
|
<PdfLogsPaneInfoNotice />
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { memo, Suspense } from 'react'
|
import { memo, Suspense } from 'react'
|
||||||
import PdfLogsViewer from './pdf-logs-viewer'
|
import PdfLogsViewer from './pdf-logs-viewer'
|
||||||
import PdfViewer from './pdf-viewer'
|
import PdfViewer from './pdf-viewer'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import LoadingSpinner from '../../../shared/components/loading-spinner'
|
import LoadingSpinner from '../../../shared/components/loading-spinner'
|
||||||
import PdfHybridPreviewToolbar from './pdf-preview-hybrid-toolbar'
|
import PdfHybridPreviewToolbar from './pdf-preview-hybrid-toolbar'
|
||||||
import PdfPreviewToolbar from './pdf-preview-toolbar'
|
import PdfPreviewToolbar from './pdf-preview-toolbar'
|
||||||
|
@ -11,8 +10,6 @@ const newPreviewToolbar = new URLSearchParams(window.location.search).has(
|
||||||
)
|
)
|
||||||
|
|
||||||
function PdfPreviewPane() {
|
function PdfPreviewPane() {
|
||||||
const { showLogs } = usePdfPreviewContext()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pdf full-size">
|
<div className="pdf full-size">
|
||||||
{newPreviewToolbar ? <PdfPreviewToolbar /> : <PdfHybridPreviewToolbar />}
|
{newPreviewToolbar ? <PdfPreviewToolbar /> : <PdfHybridPreviewToolbar />}
|
||||||
|
@ -21,7 +18,7 @@ function PdfPreviewPane() {
|
||||||
<PdfViewer />
|
<PdfViewer />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
{showLogs && <PdfLogsViewer />}
|
<PdfLogsViewer />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
import PdfPreviewProvider from '../contexts/pdf-preview-context'
|
|
||||||
import PdfPreviewPane from './pdf-preview-pane'
|
import PdfPreviewPane from './pdf-preview-pane'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||||
import ErrorBoundaryFallback from './error-boundary-fallback'
|
import ErrorBoundaryFallback from './error-boundary-fallback'
|
||||||
|
|
||||||
function PdfPreview() {
|
function PdfPreview() {
|
||||||
return (
|
return <PdfPreviewPane />
|
||||||
<PdfPreviewProvider>
|
|
||||||
<PdfPreviewPane />
|
|
||||||
</PdfPreviewProvider>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(memo(PdfPreview), () => (
|
export default withErrorBoundary(memo(PdfPreview), () => (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
||||||
import { usePdfPreviewContext } from '../contexts/pdf-preview-context'
|
|
||||||
import { lazy, memo, useEffect } from 'react'
|
import { lazy, memo, useEffect } from 'react'
|
||||||
|
import { useCompileContext } from '../../../shared/context/compile-context'
|
||||||
|
|
||||||
const PdfJsViewer = lazy(() =>
|
const PdfJsViewer = lazy(() =>
|
||||||
import(/* webpackChunkName: "pdf-js-viewer" */ './pdf-js-viewer')
|
import(/* webpackChunkName: "pdf-js-viewer" */ './pdf-js-viewer')
|
||||||
|
@ -19,7 +19,7 @@ function PdfViewer() {
|
||||||
}
|
}
|
||||||
}, [setPdfViewer])
|
}, [setPdfViewer])
|
||||||
|
|
||||||
const { pdfUrl } = usePdfPreviewContext()
|
const { pdfUrl } = useCompileContext()
|
||||||
|
|
||||||
if (!pdfUrl) {
|
if (!pdfUrl) {
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -1,494 +0,0 @@
|
||||||
import {
|
|
||||||
createContext,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
|
||||||
import { useProjectContext } from '../../../shared/context/project-context'
|
|
||||||
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
|
||||||
import {
|
|
||||||
buildLogEntryAnnotations,
|
|
||||||
handleOutputFiles,
|
|
||||||
} from '../util/output-files'
|
|
||||||
import {
|
|
||||||
send,
|
|
||||||
sendMB,
|
|
||||||
sendMBSampled,
|
|
||||||
} from '../../../infrastructure/event-tracking'
|
|
||||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
|
||||||
import useAbortController from '../../../shared/hooks/use-abort-controller'
|
|
||||||
import DocumentCompiler from '../util/compiler'
|
|
||||||
import { useIdeContext } from '../../../shared/context/ide-context'
|
|
||||||
import { useLayoutContext } from '../../../shared/context/layout-context'
|
|
||||||
import { useCompileContext } from '../../../shared/context/compile-context'
|
|
||||||
|
|
||||||
export const PdfPreviewContext = createContext(undefined)
|
|
||||||
|
|
||||||
PdfPreviewProvider.propTypes = {
|
|
||||||
children: PropTypes.any,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PdfPreviewProvider({ children }) {
|
|
||||||
const ide = useIdeContext()
|
|
||||||
|
|
||||||
const { pdfHidden, pdfLayout, setPdfLayout, setView } = useLayoutContext()
|
|
||||||
|
|
||||||
const project = useProjectContext()
|
|
||||||
|
|
||||||
const projectId = project._id
|
|
||||||
|
|
||||||
const { hasPremiumCompile, isProjectOwner } = useEditorContext()
|
|
||||||
|
|
||||||
const {
|
|
||||||
logEntries,
|
|
||||||
pdfDownloadUrl,
|
|
||||||
pdfUrl,
|
|
||||||
setClsiServerId,
|
|
||||||
setLogEntries,
|
|
||||||
setLogEntryAnnotations,
|
|
||||||
setPdfDownloadUrl,
|
|
||||||
setPdfUrl,
|
|
||||||
setUncompiled,
|
|
||||||
uncompiled,
|
|
||||||
} = useCompileContext()
|
|
||||||
|
|
||||||
// whether a compile is in progress
|
|
||||||
const [compiling, setCompiling] = useState(false)
|
|
||||||
|
|
||||||
// data received in response to a compile request
|
|
||||||
const [data, setData] = useState()
|
|
||||||
|
|
||||||
// whether the project has been compiled yet
|
|
||||||
const [compiledOnce, setCompiledOnce] = useState(false)
|
|
||||||
|
|
||||||
// whether the cache is being cleared
|
|
||||||
const [clearingCache, setClearingCache] = useState(false)
|
|
||||||
|
|
||||||
// whether the logs should be visible
|
|
||||||
const [showLogs, setShowLogs] = useState(false)
|
|
||||||
|
|
||||||
// an error that occurred
|
|
||||||
const [error, setError] = useState()
|
|
||||||
|
|
||||||
// the list of files that can be downloaded
|
|
||||||
const [fileList, setFileList] = useState()
|
|
||||||
|
|
||||||
// the raw contents of the log file
|
|
||||||
const [rawLog, setRawLog] = useState()
|
|
||||||
|
|
||||||
// validation issues from CLSI
|
|
||||||
const [validationIssues, setValidationIssues] = useState()
|
|
||||||
|
|
||||||
// whether autocompile is switched on
|
|
||||||
const [autoCompile, _setAutoCompile] = usePersistedState(
|
|
||||||
`autocompile_enabled:${projectId}`,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
// whether the compile should run in draft mode
|
|
||||||
const [draft, setDraft] = usePersistedState(`draft:${projectId}`, false, true)
|
|
||||||
|
|
||||||
// whether compiling should be prevented if there are linting errors
|
|
||||||
const [stopOnValidationError, setStopOnValidationError] = usePersistedState(
|
|
||||||
`stop_on_validation_error:${projectId}`,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
// the Document currently open in the editor
|
|
||||||
const [currentDoc] = useScopeValue('editor.sharejs_doc')
|
|
||||||
|
|
||||||
// whether the editor linter found errors
|
|
||||||
const [hasLintingError, setHasLintingError] = useScopeValue('hasLintingError')
|
|
||||||
|
|
||||||
// whether syntax validation is enabled globally
|
|
||||||
const [syntaxValidation] = useScopeValue('settings.syntaxValidation')
|
|
||||||
|
|
||||||
// the timestamp that a doc was last changed or saved
|
|
||||||
const [changedAt, setChangedAt] = useState(0)
|
|
||||||
|
|
||||||
const { signal } = useAbortController()
|
|
||||||
|
|
||||||
// the document compiler
|
|
||||||
const [compiler] = useState(() => {
|
|
||||||
return new DocumentCompiler({
|
|
||||||
project,
|
|
||||||
setChangedAt,
|
|
||||||
setCompiling,
|
|
||||||
setData,
|
|
||||||
setError,
|
|
||||||
signal,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// clean up the compiler on unmount
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
compiler.destroy()
|
|
||||||
}
|
|
||||||
}, [compiler])
|
|
||||||
|
|
||||||
// keep currentDoc in sync with the compiler
|
|
||||||
useEffect(() => {
|
|
||||||
compiler.currentDoc = currentDoc
|
|
||||||
}, [compiler, currentDoc])
|
|
||||||
|
|
||||||
// keep draft setting in sync with the compiler
|
|
||||||
useEffect(() => {
|
|
||||||
compiler.draft = draft
|
|
||||||
}, [compiler, draft])
|
|
||||||
|
|
||||||
// pass the "uncompiled" value up into the scope for use outside this context provider
|
|
||||||
useEffect(() => {
|
|
||||||
setUncompiled(changedAt > 0)
|
|
||||||
}, [setUncompiled, changedAt])
|
|
||||||
|
|
||||||
// record changes to the autocompile setting
|
|
||||||
const setAutoCompile = useCallback(
|
|
||||||
value => {
|
|
||||||
_setAutoCompile(value)
|
|
||||||
sendMB('autocompile-setting-changed', { value })
|
|
||||||
},
|
|
||||||
[_setAutoCompile]
|
|
||||||
)
|
|
||||||
|
|
||||||
// always compile the PDF once after opening the project, after the doc has loaded
|
|
||||||
useEffect(() => {
|
|
||||||
if (!compiledOnce && currentDoc) {
|
|
||||||
setCompiledOnce(true)
|
|
||||||
compiler.compile({ isAutoCompileOnLoad: true })
|
|
||||||
}
|
|
||||||
}, [compiledOnce, currentDoc, compiler])
|
|
||||||
|
|
||||||
// handle the data returned from a compile request
|
|
||||||
// note: this should _only_ run when `data` changes,
|
|
||||||
// the other dependencies must all be static
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
if (data.clsiServerId) {
|
|
||||||
setClsiServerId(data.clsiServerId) // set in scope, for PdfSynctexController
|
|
||||||
compiler.clsiServerId = data.clsiServerId
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.compileGroup) {
|
|
||||||
compiler.compileGroup = data.compileGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.outputFiles) {
|
|
||||||
handleOutputFiles(projectId, data).then(result => {
|
|
||||||
setLogEntryAnnotations(
|
|
||||||
buildLogEntryAnnotations(result.logEntries.all, ide.fileTreeManager)
|
|
||||||
)
|
|
||||||
setLogEntries(result.logEntries)
|
|
||||||
setFileList(result.fileList)
|
|
||||||
setPdfDownloadUrl(result.pdfDownloadUrl)
|
|
||||||
setPdfUrl(result.pdfUrl)
|
|
||||||
setRawLog(result.log)
|
|
||||||
|
|
||||||
// sample compile stats for real users
|
|
||||||
if (!window.user.alphaProgram && data.status === 'success') {
|
|
||||||
sendMBSampled(
|
|
||||||
'compile-result',
|
|
||||||
{
|
|
||||||
errors: result.logEntries.errors.length,
|
|
||||||
warnings: result.logEntries.warnings.length,
|
|
||||||
typesetting: result.logEntries.typesetting.length,
|
|
||||||
newPdfPreview: true, // TODO: is this useful?
|
|
||||||
},
|
|
||||||
0.01
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data.status) {
|
|
||||||
case 'success':
|
|
||||||
setError(undefined)
|
|
||||||
setShowLogs(false)
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'clsi-maintenance':
|
|
||||||
case 'compile-in-progress':
|
|
||||||
case 'exited':
|
|
||||||
case 'failure':
|
|
||||||
case 'project-too-large':
|
|
||||||
case 'terminated':
|
|
||||||
case 'too-recently-compiled':
|
|
||||||
setError(data.status)
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'timedout':
|
|
||||||
setError('timedout')
|
|
||||||
|
|
||||||
if (!hasPremiumCompile && isProjectOwner) {
|
|
||||||
send(
|
|
||||||
'subscription-funnel',
|
|
||||||
'editor-click-feature',
|
|
||||||
'compile-timeout'
|
|
||||||
)
|
|
||||||
sendMB('paywall-prompt', {
|
|
||||||
'paywall-type': 'compile-timeout',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'autocompile-backoff':
|
|
||||||
if (!data.options.isAutoCompileOnLoad) {
|
|
||||||
setError('autocompile-disabled')
|
|
||||||
setAutoCompile(false)
|
|
||||||
sendMB('autocompile-rate-limited', { hasPremiumCompile })
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'unavailable':
|
|
||||||
setError('clsi-unavailable')
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'validation-problems':
|
|
||||||
setError('validation-problems')
|
|
||||||
setValidationIssues(data.validationProblems)
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
setError('error')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
compiler,
|
|
||||||
data,
|
|
||||||
ide,
|
|
||||||
hasPremiumCompile,
|
|
||||||
isProjectOwner,
|
|
||||||
projectId,
|
|
||||||
setAutoCompile,
|
|
||||||
setClsiServerId,
|
|
||||||
setLogEntries,
|
|
||||||
setLogEntryAnnotations,
|
|
||||||
setPdfDownloadUrl,
|
|
||||||
setPdfUrl,
|
|
||||||
])
|
|
||||||
|
|
||||||
// switch to logs if there's an error
|
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
setShowLogs(true)
|
|
||||||
}
|
|
||||||
}, [error])
|
|
||||||
|
|
||||||
// recompile on key press
|
|
||||||
useEffect(() => {
|
|
||||||
const listener = event => {
|
|
||||||
compiler.compile(event.detail)
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('pdf:recompile', listener)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('pdf:recompile', listener)
|
|
||||||
}
|
|
||||||
}, [compiler])
|
|
||||||
|
|
||||||
// whether there has been an autocompile linting error, if syntax validation is switched on
|
|
||||||
const autoCompileLintingError = Boolean(
|
|
||||||
autoCompile && syntaxValidation && hasLintingError
|
|
||||||
)
|
|
||||||
|
|
||||||
const codeCheckFailed = stopOnValidationError && autoCompileLintingError
|
|
||||||
|
|
||||||
// show that the project has pending changes
|
|
||||||
const hasChanges = Boolean(
|
|
||||||
autoCompile && uncompiled && compiledOnce && !codeCheckFailed
|
|
||||||
)
|
|
||||||
|
|
||||||
// the project is available for auto-compiling
|
|
||||||
const canAutoCompile = Boolean(
|
|
||||||
autoCompile && !compiling && !pdfHidden && !codeCheckFailed
|
|
||||||
)
|
|
||||||
|
|
||||||
// call the debounced autocompile function if the project is available for auto-compiling and it has changed
|
|
||||||
useEffect(() => {
|
|
||||||
if (canAutoCompile && changedAt > 0) {
|
|
||||||
compiler.debouncedAutoCompile()
|
|
||||||
} else {
|
|
||||||
compiler.debouncedAutoCompile.cancel()
|
|
||||||
}
|
|
||||||
}, [compiler, canAutoCompile, changedAt])
|
|
||||||
|
|
||||||
// cancel debounced recompile on unmount
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
compiler.debouncedAutoCompile.cancel()
|
|
||||||
}
|
|
||||||
}, [compiler])
|
|
||||||
|
|
||||||
// record doc changes when notified by the editor
|
|
||||||
useEffect(() => {
|
|
||||||
const listener = () => {
|
|
||||||
setChangedAt(Date.now())
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('doc:changed', listener)
|
|
||||||
window.addEventListener('doc:saved', listener)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('doc:changed', listener)
|
|
||||||
window.removeEventListener('doc:saved', listener)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// start a compile manually
|
|
||||||
const startCompile = useCallback(() => {
|
|
||||||
compiler.compile()
|
|
||||||
}, [compiler])
|
|
||||||
|
|
||||||
// stop a compile manually
|
|
||||||
const stopCompile = useCallback(() => {
|
|
||||||
compiler.stopCompile()
|
|
||||||
}, [compiler])
|
|
||||||
|
|
||||||
// clear the compile cache
|
|
||||||
const clearCache = useCallback(() => {
|
|
||||||
setClearingCache(true)
|
|
||||||
|
|
||||||
return compiler.clearCache().finally(() => {
|
|
||||||
setClearingCache(false)
|
|
||||||
})
|
|
||||||
}, [compiler])
|
|
||||||
|
|
||||||
// clear the cache then run a compile, triggered by a menu item
|
|
||||||
const recompileFromScratch = useCallback(() => {
|
|
||||||
clearCache().then(() => {
|
|
||||||
compiler.compile()
|
|
||||||
})
|
|
||||||
}, [clearCache, compiler])
|
|
||||||
|
|
||||||
// switch to either side-by-side or flat (full-width) layout
|
|
||||||
// TODO: move this into LayoutContext?
|
|
||||||
const switchLayout = useCallback(() => {
|
|
||||||
setPdfLayout(layout => {
|
|
||||||
const newLayout = layout === 'sideBySide' ? 'flat' : 'sideBySide'
|
|
||||||
setView(newLayout === 'sideBySide' ? 'editor' : 'pdf')
|
|
||||||
setPdfLayout(newLayout)
|
|
||||||
window.localStorage.setItem('pdf.layout', newLayout)
|
|
||||||
})
|
|
||||||
}, [setPdfLayout, setView])
|
|
||||||
|
|
||||||
// the context value, memoized to minimize re-rendering
|
|
||||||
const value = useMemo(() => {
|
|
||||||
return {
|
|
||||||
autoCompile,
|
|
||||||
codeCheckFailed,
|
|
||||||
clearCache,
|
|
||||||
clearingCache,
|
|
||||||
compiling,
|
|
||||||
draft,
|
|
||||||
error,
|
|
||||||
fileList,
|
|
||||||
hasChanges,
|
|
||||||
hasLintingError,
|
|
||||||
logEntries,
|
|
||||||
pdfDownloadUrl,
|
|
||||||
pdfLayout,
|
|
||||||
pdfUrl,
|
|
||||||
rawLog,
|
|
||||||
recompileFromScratch,
|
|
||||||
setAutoCompile,
|
|
||||||
setDraft,
|
|
||||||
setHasLintingError, // only for stories
|
|
||||||
setShowLogs,
|
|
||||||
setStopOnValidationError,
|
|
||||||
showLogs,
|
|
||||||
startCompile,
|
|
||||||
stopCompile,
|
|
||||||
stopOnValidationError,
|
|
||||||
switchLayout,
|
|
||||||
uncompiled,
|
|
||||||
validationIssues,
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
autoCompile,
|
|
||||||
codeCheckFailed,
|
|
||||||
clearCache,
|
|
||||||
clearingCache,
|
|
||||||
compiling,
|
|
||||||
draft,
|
|
||||||
error,
|
|
||||||
fileList,
|
|
||||||
hasChanges,
|
|
||||||
hasLintingError,
|
|
||||||
logEntries,
|
|
||||||
pdfDownloadUrl,
|
|
||||||
pdfLayout,
|
|
||||||
pdfUrl,
|
|
||||||
rawLog,
|
|
||||||
recompileFromScratch,
|
|
||||||
setAutoCompile,
|
|
||||||
setDraft,
|
|
||||||
setHasLintingError, // only for stories
|
|
||||||
setStopOnValidationError,
|
|
||||||
showLogs,
|
|
||||||
startCompile,
|
|
||||||
stopCompile,
|
|
||||||
stopOnValidationError,
|
|
||||||
switchLayout,
|
|
||||||
uncompiled,
|
|
||||||
validationIssues,
|
|
||||||
])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PdfPreviewContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</PdfPreviewContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
PdfPreviewContext.Provider.propTypes = {
|
|
||||||
value: PropTypes.shape({
|
|
||||||
autoCompile: PropTypes.bool.isRequired,
|
|
||||||
clearCache: PropTypes.func.isRequired,
|
|
||||||
clearingCache: PropTypes.bool.isRequired,
|
|
||||||
codeCheckFailed: PropTypes.bool.isRequired,
|
|
||||||
compiling: PropTypes.bool.isRequired,
|
|
||||||
draft: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.string,
|
|
||||||
fileList: PropTypes.object,
|
|
||||||
hasChanges: PropTypes.bool.isRequired,
|
|
||||||
hasLintingError: PropTypes.bool,
|
|
||||||
logEntries: PropTypes.object,
|
|
||||||
pdfDownloadUrl: PropTypes.string,
|
|
||||||
pdfLayout: PropTypes.string,
|
|
||||||
pdfUrl: PropTypes.string,
|
|
||||||
rawLog: PropTypes.string,
|
|
||||||
recompileFromScratch: PropTypes.func.isRequired,
|
|
||||||
setAutoCompile: PropTypes.func.isRequired,
|
|
||||||
setDraft: PropTypes.func.isRequired,
|
|
||||||
setHasLintingError: PropTypes.func.isRequired, // only for storybook
|
|
||||||
setShowLogs: PropTypes.func.isRequired,
|
|
||||||
setStopOnValidationError: PropTypes.func.isRequired,
|
|
||||||
showLogs: PropTypes.bool.isRequired,
|
|
||||||
startCompile: PropTypes.func.isRequired,
|
|
||||||
stopCompile: PropTypes.func.isRequired,
|
|
||||||
stopOnValidationError: PropTypes.bool.isRequired,
|
|
||||||
switchLayout: PropTypes.func.isRequired,
|
|
||||||
uncompiled: PropTypes.bool,
|
|
||||||
validationIssues: PropTypes.object,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
export function usePdfPreviewContext() {
|
|
||||||
const context = useContext(PdfPreviewContext)
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
throw new Error(
|
|
||||||
'usePdfPreviewContext is only available inside PdfPreviewProvider'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return context
|
|
||||||
}
|
|
|
@ -45,10 +45,6 @@ export default class DocumentCompiler {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.debouncedAutoCompile.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// The main "compile" function.
|
// The main "compile" function.
|
||||||
// Call this directly to run a compile now, otherwise call debouncedAutoCompile.
|
// Call this directly to run a compile now, otherwise call debouncedAutoCompile.
|
||||||
async compile(options = {}) {
|
async compile(options = {}) {
|
||||||
|
|
|
@ -151,6 +151,23 @@ export default class PDFJSWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set currentPosition(position) {
|
||||||
|
const destArray = [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
name: 'XYZ', // 'XYZ' = scroll to the given coordinates
|
||||||
|
},
|
||||||
|
position.offset.left,
|
||||||
|
position.offset.top,
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
|
||||||
|
this.viewer.scrollPageIntoView({
|
||||||
|
pageNumber: position.page + 1,
|
||||||
|
destArray,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
abortDocumentLoading() {
|
abortDocumentLoading() {
|
||||||
this.loadDocumentTask = undefined
|
this.loadDocumentTask = undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,72 @@
|
||||||
import { createContext, useContext, useMemo } from 'react'
|
import {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import useScopeValue from '../hooks/use-scope-value'
|
import useScopeValue from '../hooks/use-scope-value'
|
||||||
|
import usePersistedState from '../hooks/use-persisted-state'
|
||||||
|
import useAbortController from '../hooks/use-abort-controller'
|
||||||
|
import DocumentCompiler from '../../features/pdf-preview/util/compiler'
|
||||||
|
import {
|
||||||
|
send,
|
||||||
|
sendMB,
|
||||||
|
sendMBSampled,
|
||||||
|
} from '../../infrastructure/event-tracking'
|
||||||
|
import {
|
||||||
|
buildLogEntryAnnotations,
|
||||||
|
handleOutputFiles,
|
||||||
|
} from '../../features/pdf-preview/util/output-files'
|
||||||
|
import { useIdeContext } from './ide-context'
|
||||||
|
import { useProjectContext } from './project-context'
|
||||||
|
import { useEditorContext } from './editor-context'
|
||||||
|
|
||||||
export const CompileContext = createContext()
|
export const CompileContext = createContext()
|
||||||
|
|
||||||
CompileContext.Provider.propTypes = {
|
CompileContext.Provider.propTypes = {
|
||||||
value: PropTypes.shape({
|
value: PropTypes.shape({
|
||||||
|
autoCompile: PropTypes.bool.isRequired,
|
||||||
|
clearingCache: PropTypes.bool.isRequired,
|
||||||
clsiServerId: PropTypes.string,
|
clsiServerId: PropTypes.string,
|
||||||
|
codeCheckFailed: PropTypes.bool.isRequired,
|
||||||
|
compiling: PropTypes.bool.isRequired,
|
||||||
|
draft: PropTypes.bool.isRequired,
|
||||||
|
error: PropTypes.string,
|
||||||
|
fileList: PropTypes.object,
|
||||||
|
hasChanges: PropTypes.bool.isRequired,
|
||||||
|
hasLintingError: PropTypes.bool,
|
||||||
logEntries: PropTypes.object,
|
logEntries: PropTypes.object,
|
||||||
logEntryAnnotations: PropTypes.object,
|
logEntryAnnotations: PropTypes.object,
|
||||||
pdfDownloadUrl: PropTypes.string,
|
pdfDownloadUrl: PropTypes.string,
|
||||||
pdfUrl: PropTypes.string,
|
pdfUrl: PropTypes.string,
|
||||||
setClsiServerId: PropTypes.func.isRequired,
|
rawLog: PropTypes.string,
|
||||||
setLogEntries: PropTypes.func.isRequired,
|
setAutoCompile: PropTypes.func.isRequired,
|
||||||
setLogEntryAnnotations: PropTypes.func.isRequired,
|
setDraft: PropTypes.func.isRequired,
|
||||||
setPdfDownloadUrl: PropTypes.func.isRequired,
|
setHasLintingError: PropTypes.func.isRequired, // only for storybook
|
||||||
setPdfUrl: PropTypes.func.isRequired,
|
setShowLogs: PropTypes.func.isRequired,
|
||||||
setUncompiled: PropTypes.func.isRequired,
|
setStopOnValidationError: PropTypes.func.isRequired,
|
||||||
|
showLogs: PropTypes.bool.isRequired,
|
||||||
|
stopOnValidationError: PropTypes.bool.isRequired,
|
||||||
uncompiled: PropTypes.bool,
|
uncompiled: PropTypes.bool,
|
||||||
|
validationIssues: PropTypes.object,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CompileProvider({ children }) {
|
export function CompileProvider({ children }) {
|
||||||
|
const ide = useIdeContext()
|
||||||
|
|
||||||
|
const { hasPremiumCompile, isProjectOwner } = useEditorContext()
|
||||||
|
|
||||||
|
const project = useProjectContext()
|
||||||
|
|
||||||
|
const projectId = project._id
|
||||||
|
|
||||||
|
// whether a compile is in progress
|
||||||
|
const [compiling, setCompiling] = useState(false)
|
||||||
|
|
||||||
// the log entries parsed from the compile output log
|
// the log entries parsed from the compile output log
|
||||||
const [logEntries, setLogEntries] = useScopeValue('pdf.logEntries')
|
const [logEntries, setLogEntries] = useScopeValue('pdf.logEntries')
|
||||||
|
|
||||||
|
@ -42,34 +87,368 @@ export function CompileProvider({ children }) {
|
||||||
// the id of the CLSI server which ran the compile
|
// the id of the CLSI server which ran the compile
|
||||||
const [clsiServerId, setClsiServerId] = useScopeValue('pdf.clsiServerId')
|
const [clsiServerId, setClsiServerId] = useScopeValue('pdf.clsiServerId')
|
||||||
|
|
||||||
|
// data received in response to a compile request
|
||||||
|
const [data, setData] = useState()
|
||||||
|
|
||||||
|
// whether the project has been compiled yet
|
||||||
|
const [compiledOnce, setCompiledOnce] = useState(false)
|
||||||
|
|
||||||
|
// whether the cache is being cleared
|
||||||
|
const [clearingCache, setClearingCache] = useState(false)
|
||||||
|
|
||||||
|
// whether the logs should be visible
|
||||||
|
const [showLogs, setShowLogs] = useState(false)
|
||||||
|
|
||||||
|
// an error that occurred
|
||||||
|
const [error, setError] = useState()
|
||||||
|
|
||||||
|
// the list of files that can be downloaded
|
||||||
|
const [fileList, setFileList] = useState()
|
||||||
|
|
||||||
|
// the raw contents of the log file
|
||||||
|
const [rawLog, setRawLog] = useState()
|
||||||
|
|
||||||
|
// validation issues from CLSI
|
||||||
|
const [validationIssues, setValidationIssues] = useState()
|
||||||
|
|
||||||
|
// whether autocompile is switched on
|
||||||
|
const [autoCompile, _setAutoCompile] = usePersistedState(
|
||||||
|
`autocompile_enabled:${projectId}`,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
// whether the compile should run in draft mode
|
||||||
|
const [draft, setDraft] = usePersistedState(`draft:${projectId}`, false, true)
|
||||||
|
|
||||||
|
// whether compiling should be prevented if there are linting errors
|
||||||
|
const [stopOnValidationError, setStopOnValidationError] = usePersistedState(
|
||||||
|
`stop_on_validation_error:${projectId}`,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
// the Document currently open in the editor
|
||||||
|
const [currentDoc] = useScopeValue('editor.sharejs_doc')
|
||||||
|
|
||||||
|
// whether the editor linter found errors
|
||||||
|
const [hasLintingError, setHasLintingError] = useScopeValue('hasLintingError')
|
||||||
|
|
||||||
|
// whether syntax validation is enabled globally
|
||||||
|
const [syntaxValidation] = useScopeValue('settings.syntaxValidation')
|
||||||
|
|
||||||
|
// the timestamp that a doc was last changed or saved
|
||||||
|
const [changedAt, setChangedAt] = useState(0)
|
||||||
|
|
||||||
|
const { signal } = useAbortController()
|
||||||
|
|
||||||
|
// the document compiler
|
||||||
|
const [compiler] = useState(() => {
|
||||||
|
return new DocumentCompiler({
|
||||||
|
project,
|
||||||
|
setChangedAt,
|
||||||
|
setCompiling,
|
||||||
|
setData,
|
||||||
|
setError,
|
||||||
|
signal,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// keep currentDoc in sync with the compiler
|
||||||
|
useEffect(() => {
|
||||||
|
compiler.currentDoc = currentDoc
|
||||||
|
}, [compiler, currentDoc])
|
||||||
|
|
||||||
|
// keep draft setting in sync with the compiler
|
||||||
|
useEffect(() => {
|
||||||
|
compiler.draft = draft
|
||||||
|
}, [compiler, draft])
|
||||||
|
|
||||||
|
// pass the "uncompiled" value up into the scope for use outside this context provider
|
||||||
|
useEffect(() => {
|
||||||
|
setUncompiled(changedAt > 0)
|
||||||
|
}, [setUncompiled, changedAt])
|
||||||
|
|
||||||
|
// record changes to the autocompile setting
|
||||||
|
const setAutoCompile = useCallback(
|
||||||
|
value => {
|
||||||
|
_setAutoCompile(value)
|
||||||
|
sendMB('autocompile-setting-changed', { value })
|
||||||
|
},
|
||||||
|
[_setAutoCompile]
|
||||||
|
)
|
||||||
|
|
||||||
|
// always compile the PDF once after opening the project, after the doc has loaded
|
||||||
|
useEffect(() => {
|
||||||
|
if (!compiledOnce && currentDoc) {
|
||||||
|
setCompiledOnce(true)
|
||||||
|
compiler.compile({ isAutoCompileOnLoad: true })
|
||||||
|
}
|
||||||
|
}, [compiledOnce, currentDoc, compiler])
|
||||||
|
|
||||||
|
// handle the data returned from a compile request
|
||||||
|
// note: this should _only_ run when `data` changes,
|
||||||
|
// the other dependencies must all be static
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
if (data.clsiServerId) {
|
||||||
|
setClsiServerId(data.clsiServerId) // set in scope, for PdfSynctexController
|
||||||
|
compiler.clsiServerId = data.clsiServerId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.compileGroup) {
|
||||||
|
compiler.compileGroup = data.compileGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.outputFiles) {
|
||||||
|
handleOutputFiles(projectId, data).then(result => {
|
||||||
|
setLogEntryAnnotations(
|
||||||
|
buildLogEntryAnnotations(result.logEntries.all, ide.fileTreeManager)
|
||||||
|
)
|
||||||
|
setLogEntries(result.logEntries)
|
||||||
|
setFileList(result.fileList)
|
||||||
|
setPdfDownloadUrl(result.pdfDownloadUrl)
|
||||||
|
setPdfUrl(result.pdfUrl)
|
||||||
|
setRawLog(result.log)
|
||||||
|
|
||||||
|
// sample compile stats for real users
|
||||||
|
if (!window.user.alphaProgram && data.status === 'success') {
|
||||||
|
sendMBSampled(
|
||||||
|
'compile-result',
|
||||||
|
{
|
||||||
|
errors: result.logEntries.errors.length,
|
||||||
|
warnings: result.logEntries.warnings.length,
|
||||||
|
typesetting: result.logEntries.typesetting.length,
|
||||||
|
newPdfPreview: true, // TODO: is this useful?
|
||||||
|
},
|
||||||
|
0.01
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data.status) {
|
||||||
|
case 'success':
|
||||||
|
setError(undefined)
|
||||||
|
setShowLogs(false)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'clsi-maintenance':
|
||||||
|
case 'compile-in-progress':
|
||||||
|
case 'exited':
|
||||||
|
case 'failure':
|
||||||
|
case 'project-too-large':
|
||||||
|
case 'rate-limited':
|
||||||
|
case 'terminated':
|
||||||
|
case 'too-recently-compiled':
|
||||||
|
setError(data.status)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'timedout':
|
||||||
|
setError('timedout')
|
||||||
|
|
||||||
|
if (!hasPremiumCompile && isProjectOwner) {
|
||||||
|
send(
|
||||||
|
'subscription-funnel',
|
||||||
|
'editor-click-feature',
|
||||||
|
'compile-timeout'
|
||||||
|
)
|
||||||
|
sendMB('paywall-prompt', {
|
||||||
|
'paywall-type': 'compile-timeout',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'autocompile-backoff':
|
||||||
|
if (!data.options.isAutoCompileOnLoad) {
|
||||||
|
setError('autocompile-disabled')
|
||||||
|
setAutoCompile(false)
|
||||||
|
sendMB('autocompile-rate-limited', { hasPremiumCompile })
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'unavailable':
|
||||||
|
setError('clsi-unavailable')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'validation-problems':
|
||||||
|
setError('validation-problems')
|
||||||
|
setValidationIssues(data.validationProblems)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
setError('error')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
compiler,
|
||||||
|
data,
|
||||||
|
ide,
|
||||||
|
hasPremiumCompile,
|
||||||
|
isProjectOwner,
|
||||||
|
projectId,
|
||||||
|
setAutoCompile,
|
||||||
|
setClsiServerId,
|
||||||
|
setLogEntries,
|
||||||
|
setLogEntryAnnotations,
|
||||||
|
setPdfDownloadUrl,
|
||||||
|
setPdfUrl,
|
||||||
|
])
|
||||||
|
|
||||||
|
// switch to logs if there's an error
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) {
|
||||||
|
setShowLogs(true)
|
||||||
|
}
|
||||||
|
}, [error])
|
||||||
|
|
||||||
|
// recompile on key press
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = event => {
|
||||||
|
compiler.compile(event.detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('pdf:recompile', listener)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('pdf:recompile', listener)
|
||||||
|
}
|
||||||
|
}, [compiler])
|
||||||
|
|
||||||
|
// whether there has been an autocompile linting error, if syntax validation is switched on
|
||||||
|
const autoCompileLintingError = Boolean(
|
||||||
|
autoCompile && syntaxValidation && hasLintingError
|
||||||
|
)
|
||||||
|
|
||||||
|
const codeCheckFailed = stopOnValidationError && autoCompileLintingError
|
||||||
|
|
||||||
|
// show that the project has pending changes
|
||||||
|
const hasChanges = Boolean(
|
||||||
|
autoCompile && uncompiled && compiledOnce && !codeCheckFailed
|
||||||
|
)
|
||||||
|
|
||||||
|
// the project is available for auto-compiling
|
||||||
|
const canAutoCompile = Boolean(autoCompile && !compiling && !codeCheckFailed)
|
||||||
|
|
||||||
|
// call the debounced autocompile function if the project is available for auto-compiling and it has changed
|
||||||
|
useEffect(() => {
|
||||||
|
if (canAutoCompile && changedAt > 0) {
|
||||||
|
compiler.debouncedAutoCompile()
|
||||||
|
} else {
|
||||||
|
compiler.debouncedAutoCompile.cancel()
|
||||||
|
}
|
||||||
|
}, [compiler, canAutoCompile, changedAt])
|
||||||
|
|
||||||
|
// cancel debounced recompile on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
compiler.debouncedAutoCompile.cancel()
|
||||||
|
}
|
||||||
|
}, [compiler])
|
||||||
|
|
||||||
|
// record doc changes when notified by the editor
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = event => {
|
||||||
|
setChangedAt(Date.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('doc:changed', listener)
|
||||||
|
window.addEventListener('doc:saved', listener)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('doc:changed', listener)
|
||||||
|
window.removeEventListener('doc:saved', listener)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// start a compile manually
|
||||||
|
const startCompile = useCallback(() => {
|
||||||
|
compiler.compile()
|
||||||
|
}, [compiler])
|
||||||
|
|
||||||
|
// stop a compile manually
|
||||||
|
const stopCompile = useCallback(() => {
|
||||||
|
compiler.stopCompile()
|
||||||
|
}, [compiler])
|
||||||
|
|
||||||
|
// clear the compile cache
|
||||||
|
const clearCache = useCallback(() => {
|
||||||
|
setClearingCache(true)
|
||||||
|
|
||||||
|
return compiler.clearCache().finally(() => {
|
||||||
|
setClearingCache(false)
|
||||||
|
})
|
||||||
|
}, [compiler, setClearingCache])
|
||||||
|
|
||||||
|
// clear the cache then run a compile, triggered by a menu item
|
||||||
|
const recompileFromScratch = useCallback(() => {
|
||||||
|
clearCache().then(() => {
|
||||||
|
compiler.compile()
|
||||||
|
})
|
||||||
|
}, [clearCache, compiler])
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
autoCompile,
|
||||||
|
clearCache,
|
||||||
|
clearingCache,
|
||||||
clsiServerId,
|
clsiServerId,
|
||||||
|
codeCheckFailed,
|
||||||
|
compiling,
|
||||||
|
draft,
|
||||||
|
error,
|
||||||
|
fileList,
|
||||||
|
hasChanges,
|
||||||
|
hasLintingError,
|
||||||
logEntries,
|
logEntries,
|
||||||
logEntryAnnotations,
|
logEntryAnnotations,
|
||||||
pdfDownloadUrl,
|
pdfDownloadUrl,
|
||||||
pdfUrl,
|
pdfUrl,
|
||||||
setClsiServerId,
|
rawLog,
|
||||||
setLogEntries,
|
recompileFromScratch,
|
||||||
setLogEntryAnnotations,
|
setAutoCompile,
|
||||||
setPdfDownloadUrl,
|
setClearingCache,
|
||||||
setPdfUrl,
|
setCompiling,
|
||||||
setUncompiled,
|
setDraft,
|
||||||
|
setHasLintingError, // only for stories
|
||||||
|
setShowLogs,
|
||||||
|
setStopOnValidationError,
|
||||||
|
showLogs,
|
||||||
|
startCompile,
|
||||||
|
stopCompile,
|
||||||
|
stopOnValidationError,
|
||||||
uncompiled,
|
uncompiled,
|
||||||
|
validationIssues,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
|
autoCompile,
|
||||||
|
clearCache,
|
||||||
|
clearingCache,
|
||||||
clsiServerId,
|
clsiServerId,
|
||||||
|
codeCheckFailed,
|
||||||
|
compiling,
|
||||||
|
draft,
|
||||||
|
error,
|
||||||
|
fileList,
|
||||||
|
hasChanges,
|
||||||
|
hasLintingError,
|
||||||
logEntries,
|
logEntries,
|
||||||
logEntryAnnotations,
|
logEntryAnnotations,
|
||||||
pdfDownloadUrl,
|
pdfDownloadUrl,
|
||||||
pdfUrl,
|
pdfUrl,
|
||||||
setClsiServerId,
|
rawLog,
|
||||||
setLogEntries,
|
recompileFromScratch,
|
||||||
setLogEntryAnnotations,
|
setAutoCompile,
|
||||||
setPdfDownloadUrl,
|
setDraft,
|
||||||
setPdfUrl,
|
setHasLintingError,
|
||||||
setUncompiled,
|
setStopOnValidationError,
|
||||||
|
showLogs,
|
||||||
|
startCompile,
|
||||||
|
stopCompile,
|
||||||
|
stopOnValidationError,
|
||||||
uncompiled,
|
uncompiled,
|
||||||
|
validationIssues,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { createContext, useContext, useCallback, useMemo } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import useScopeValue from '../hooks/use-scope-value'
|
import useScopeValue from '../hooks/use-scope-value'
|
||||||
import { useIdeContext } from './ide-context'
|
import { useIdeContext } from './ide-context'
|
||||||
|
import localStorage from '../../infrastructure/local-storage'
|
||||||
|
|
||||||
export const LayoutContext = createContext()
|
export const LayoutContext = createContext()
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ LayoutContext.Provider.propTypes = {
|
||||||
setReviewPanelOpen: PropTypes.func.isRequired,
|
setReviewPanelOpen: PropTypes.func.isRequired,
|
||||||
leftMenuShown: PropTypes.bool,
|
leftMenuShown: PropTypes.bool,
|
||||||
setLeftMenuShown: PropTypes.func.isRequired,
|
setLeftMenuShown: PropTypes.func.isRequired,
|
||||||
pdfLayout: PropTypes.oneOf(['sideBySide', 'flat', 'split']).isRequired,
|
pdfLayout: PropTypes.oneOf(['sideBySide', 'flat']).isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,14 +54,20 @@ export function LayoutProvider({ children }) {
|
||||||
// whether to display the editor and preview side-by-side or full-width ("flat")
|
// whether to display the editor and preview side-by-side or full-width ("flat")
|
||||||
const [pdfLayout, setPdfLayout] = useScopeValue('ui.pdfLayout')
|
const [pdfLayout, setPdfLayout] = useScopeValue('ui.pdfLayout')
|
||||||
|
|
||||||
// whether the PDF preview pane is hidden
|
// switch to either side-by-side or flat (full-width) layout
|
||||||
const [pdfHidden] = useScopeValue('ui.pdfHidden')
|
const switchLayout = useCallback(() => {
|
||||||
|
setPdfLayout(layout => {
|
||||||
|
const newLayout = layout === 'sideBySide' ? 'flat' : 'sideBySide'
|
||||||
|
setView(newLayout === 'sideBySide' ? 'editor' : 'pdf')
|
||||||
|
setPdfLayout(newLayout)
|
||||||
|
localStorage.setItem('pdf.layout', newLayout)
|
||||||
|
})
|
||||||
|
}, [setPdfLayout, setView])
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
chatIsOpen,
|
chatIsOpen,
|
||||||
leftMenuShown,
|
leftMenuShown,
|
||||||
pdfHidden,
|
|
||||||
pdfLayout,
|
pdfLayout,
|
||||||
reviewPanelOpen,
|
reviewPanelOpen,
|
||||||
setChatIsOpen,
|
setChatIsOpen,
|
||||||
|
@ -68,12 +75,12 @@ export function LayoutProvider({ children }) {
|
||||||
setPdfLayout,
|
setPdfLayout,
|
||||||
setReviewPanelOpen,
|
setReviewPanelOpen,
|
||||||
setView,
|
setView,
|
||||||
|
switchLayout,
|
||||||
view,
|
view,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
chatIsOpen,
|
chatIsOpen,
|
||||||
leftMenuShown,
|
leftMenuShown,
|
||||||
pdfHidden,
|
|
||||||
pdfLayout,
|
pdfLayout,
|
||||||
reviewPanelOpen,
|
reviewPanelOpen,
|
||||||
setChatIsOpen,
|
setChatIsOpen,
|
||||||
|
@ -81,6 +88,7 @@ export function LayoutProvider({ children }) {
|
||||||
setPdfLayout,
|
setPdfLayout,
|
||||||
setReviewPanelOpen,
|
setReviewPanelOpen,
|
||||||
setView,
|
setView,
|
||||||
|
switchLayout,
|
||||||
view,
|
view,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,11 +17,11 @@ export function ContextRoot({ children, ide, settings }) {
|
||||||
<UserProvider>
|
<UserProvider>
|
||||||
<ProjectProvider>
|
<ProjectProvider>
|
||||||
<EditorProvider settings={settings}>
|
<EditorProvider settings={settings}>
|
||||||
<CompileProvider>
|
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
|
<CompileProvider>
|
||||||
<ChatProvider>{children}</ChatProvider>
|
<ChatProvider>{children}</ChatProvider>
|
||||||
</LayoutProvider>
|
|
||||||
</CompileProvider>
|
</CompileProvider>
|
||||||
|
</LayoutProvider>
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</ProjectProvider>
|
</ProjectProvider>
|
||||||
</UserProvider>
|
</UserProvider>
|
||||||
|
|
|
@ -3,9 +3,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import useFetchMock from './hooks/use-fetch-mock'
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
import { setupContext } from './fixtures/context'
|
import { setupContext } from './fixtures/context'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import PdfPreviewProvider, {
|
|
||||||
usePdfPreviewContext,
|
|
||||||
} from '../js/features/pdf-preview/contexts/pdf-preview-context'
|
|
||||||
import PdfPreviewPane from '../js/features/pdf-preview/components/pdf-preview-pane'
|
import PdfPreviewPane from '../js/features/pdf-preview/components/pdf-preview-pane'
|
||||||
import PdfPreview from '../js/features/pdf-preview/components/pdf-preview'
|
import PdfPreview from '../js/features/pdf-preview/components/pdf-preview'
|
||||||
import PdfPreviewToolbar from '../js/features/pdf-preview/components/pdf-preview-toolbar'
|
import PdfPreviewToolbar from '../js/features/pdf-preview/components/pdf-preview-toolbar'
|
||||||
|
@ -15,6 +12,7 @@ import PdfLogsViewer from '../js/features/pdf-preview/components/pdf-logs-viewer
|
||||||
import examplePdf from './fixtures/storybook-example.pdf'
|
import examplePdf from './fixtures/storybook-example.pdf'
|
||||||
import PdfPreviewError from '../js/features/pdf-preview/components/pdf-preview-error'
|
import PdfPreviewError from '../js/features/pdf-preview/components/pdf-preview-error'
|
||||||
import PdfPreviewHybridToolbar from '../js/features/pdf-preview/components/pdf-preview-hybrid-toolbar'
|
import PdfPreviewHybridToolbar from '../js/features/pdf-preview/components/pdf-preview-hybrid-toolbar'
|
||||||
|
import { useCompileContext } from '../js/shared/context/compile-context'
|
||||||
|
|
||||||
setupContext()
|
setupContext()
|
||||||
|
|
||||||
|
@ -230,7 +228,7 @@ export const Interactive = () => {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const Inner = () => {
|
const Inner = () => {
|
||||||
const context = usePdfPreviewContext()
|
const context = useCompileContext()
|
||||||
|
|
||||||
const { setHasLintingError } = context
|
const { setHasLintingError } = context
|
||||||
|
|
||||||
|
@ -369,10 +367,8 @@ export const Interactive = () => {
|
||||||
|
|
||||||
return withContextRoot(
|
return withContextRoot(
|
||||||
<div className="pdf-viewer">
|
<div className="pdf-viewer">
|
||||||
<PdfPreviewProvider>
|
|
||||||
<PdfPreviewPane />
|
<PdfPreviewPane />
|
||||||
<Inner />
|
<Inner />
|
||||||
</PdfPreviewProvider>
|
|
||||||
</div>,
|
</div>,
|
||||||
scope
|
scope
|
||||||
)
|
)
|
||||||
|
@ -406,7 +402,7 @@ export const CompileError = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const Inner = () => {
|
const Inner = () => {
|
||||||
const { startCompile } = usePdfPreviewContext()
|
const { startCompile } = useCompileContext()
|
||||||
|
|
||||||
const handleStatusChange = useCallback(
|
const handleStatusChange = useCallback(
|
||||||
event => {
|
event => {
|
||||||
|
@ -441,10 +437,10 @@ export const CompileError = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return withContextRoot(
|
return withContextRoot(
|
||||||
<PdfPreviewProvider>
|
<>
|
||||||
<PdfPreviewPane />
|
<PdfPreviewPane />
|
||||||
<Inner />
|
<Inner />
|
||||||
</PdfPreviewProvider>,
|
</>,
|
||||||
scope
|
scope
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -470,7 +466,7 @@ const compileErrors = [
|
||||||
|
|
||||||
export const DisplayError = () => {
|
export const DisplayError = () => {
|
||||||
return withContextRoot(
|
return withContextRoot(
|
||||||
<PdfPreviewProvider>
|
<>
|
||||||
{compileErrors.map(error => (
|
{compileErrors.map(error => (
|
||||||
<div
|
<div
|
||||||
key={error}
|
key={error}
|
||||||
|
@ -480,7 +476,7 @@ export const DisplayError = () => {
|
||||||
<PdfPreviewError error={error} />
|
<PdfPreviewError error={error} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</PdfPreviewProvider>,
|
</>,
|
||||||
scope
|
scope
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -489,11 +485,9 @@ export const Toolbar = () => {
|
||||||
useFetchMock(fetchMock => mockCompile(fetchMock, 500))
|
useFetchMock(fetchMock => mockCompile(fetchMock, 500))
|
||||||
|
|
||||||
return withContextRoot(
|
return withContextRoot(
|
||||||
<PdfPreviewProvider>
|
|
||||||
<div className="pdf">
|
<div className="pdf">
|
||||||
<PdfPreviewToolbar />
|
<PdfPreviewToolbar />
|
||||||
</div>
|
</div>,
|
||||||
</PdfPreviewProvider>,
|
|
||||||
scope
|
scope
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -505,11 +499,9 @@ export const HybridToolbar = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return withContextRoot(
|
return withContextRoot(
|
||||||
<PdfPreviewProvider>
|
|
||||||
<div className="pdf">
|
<div className="pdf">
|
||||||
<PdfPreviewHybridToolbar />
|
<PdfPreviewHybridToolbar />
|
||||||
</div>
|
</div>,
|
||||||
</PdfPreviewProvider>,
|
|
||||||
scope
|
scope
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -530,21 +522,15 @@ export const FileList = () => {
|
||||||
|
|
||||||
export const Logs = () => {
|
export const Logs = () => {
|
||||||
useFetchMock(fetchMock => {
|
useFetchMock(fetchMock => {
|
||||||
mockCompile(fetchMock, 0)
|
mockCompileError(fetchMock, 400, 0)
|
||||||
mockBuildFile(fetchMock)
|
mockBuildFile(fetchMock)
|
||||||
mockClearCache(fetchMock)
|
mockClearCache(fetchMock)
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatchProjectJoined()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return withContextRoot(
|
return withContextRoot(
|
||||||
<PdfPreviewProvider>
|
|
||||||
<div className="pdf">
|
<div className="pdf">
|
||||||
<PdfLogsViewer />
|
<PdfLogsViewer />
|
||||||
</div>
|
</div>,
|
||||||
</PdfPreviewProvider>,
|
|
||||||
scope
|
scope
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -577,10 +563,5 @@ export const ValidationIssues = () => {
|
||||||
dispatchProjectJoined()
|
dispatchProjectJoined()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return withContextRoot(
|
return withContextRoot(<PdfPreviewPane />, scope)
|
||||||
<PdfPreviewProvider>
|
|
||||||
<PdfPreviewPane />
|
|
||||||
</PdfPreviewProvider>,
|
|
||||||
scope
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue