mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 06:33:55 +01:00
Merge pull request #12448 from overleaf/jpa-compile-and-download-pdf
[web] add button to project dashboard for compiling and downloading PDF GitOrigin-RevId: c243b4a30e4720116d82d9c25bdc8be8825d6d74
This commit is contained in:
+31
@@ -11,6 +11,8 @@ import UntrashProjectButton from '../table/cells/action-buttons/untrash-project-
|
||||
import LeaveProjectButton from '../table/cells/action-buttons/leave-project-button'
|
||||
import DeleteProjectButton from '../table/cells/action-buttons/delete-project-button'
|
||||
import { Project } from '../../../../../../types/project/dashboard/api'
|
||||
import CompileAndDownloadProjectPDFButton from '../table/cells/action-buttons/compile-and-download-project-pdf-button'
|
||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||
|
||||
type ActionButtonProps = {
|
||||
project: Project
|
||||
@@ -30,6 +32,29 @@ function CopyProjectButtonMenuItem({ project, onClick }: ActionButtonProps) {
|
||||
)
|
||||
}
|
||||
|
||||
function CompileAndDownloadProjectPDFButtonMenuItem({
|
||||
project,
|
||||
onClick,
|
||||
}: ActionButtonProps) {
|
||||
return (
|
||||
<CompileAndDownloadProjectPDFButton project={project}>
|
||||
{(text, pendingCompile, downloadProject) => (
|
||||
<MenuItemButton
|
||||
onClick={() => downloadProject(onClick)}
|
||||
className="projects-action-menu-item"
|
||||
>
|
||||
{pendingCompile ? (
|
||||
<Icon type="spinner" spin className="menu-item-button-icon" />
|
||||
) : (
|
||||
<Icon type="file-pdf-o" className="menu-item-button-icon" />
|
||||
)}{' '}
|
||||
<span className="menu-item-button-text">{text}</span>
|
||||
</MenuItemButton>
|
||||
)}
|
||||
</CompileAndDownloadProjectPDFButton>
|
||||
)
|
||||
}
|
||||
|
||||
function DownloadProjectButtonMenuItem({
|
||||
project,
|
||||
onClick,
|
||||
@@ -194,6 +219,12 @@ function ActionsDropdown({ project }: ActionDropdownProps) {
|
||||
project={project}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
{isSplitTestEnabled('download-pdf-dashboard') && (
|
||||
<CompileAndDownloadProjectPDFButtonMenuItem
|
||||
project={project}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
)}
|
||||
<ArchiveProjectButtonMenuItem project={project} onClick={handleClose} />
|
||||
<TrashProjectButtonMenuItem project={project} onClick={handleClose} />
|
||||
<UnarchiveProjectButtonMenuItem
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import { Project } from '../../../../../../../../types/project/dashboard/api'
|
||||
import Icon from '../../../../../../shared/components/icon'
|
||||
import Tooltip from '../../../../../../shared/components/tooltip'
|
||||
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
|
||||
import { useLocation } from '../../../../../../shared/hooks/use-location'
|
||||
import useAbortController from '../../../../../../shared/hooks/use-abort-controller'
|
||||
import { postJSON } from '../../../../../../infrastructure/fetch-json'
|
||||
import AccessibleModal from '../../../../../../shared/components/accessible-modal'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { isSmallDevice } from '../../../../../../infrastructure/event-tracking'
|
||||
|
||||
type CompileAndDownloadProjectPDFButtonProps = {
|
||||
project: Project
|
||||
children: (
|
||||
text: string,
|
||||
pendingDownload: boolean,
|
||||
downloadProject: (fn: () => void) => void
|
||||
) => React.ReactElement
|
||||
}
|
||||
|
||||
function CompileAndDownloadProjectPDFButton({
|
||||
project,
|
||||
children,
|
||||
}: CompileAndDownloadProjectPDFButtonProps) {
|
||||
const { t } = useTranslation()
|
||||
const location = useLocation()
|
||||
|
||||
const { signal } = useAbortController()
|
||||
const [pendingCompile, setPendingCompile] = useState(false)
|
||||
|
||||
const downloadProject = useCallback(
|
||||
onDone => {
|
||||
setPendingCompile(pendingCompile => {
|
||||
if (pendingCompile) return true
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'downloadPDF',
|
||||
projectId: project.id,
|
||||
isSmallDevice,
|
||||
})
|
||||
|
||||
postJSON(`/project/${project.id}/compile`, {
|
||||
body: {
|
||||
check: 'silent',
|
||||
draft: false,
|
||||
incrementalCompilesEnabled: true,
|
||||
},
|
||||
signal,
|
||||
})
|
||||
.catch(() => ({ status: 'error' }))
|
||||
.then(data => {
|
||||
setPendingCompile(false)
|
||||
if (data.status === 'success') {
|
||||
const outputFile = data.outputFiles
|
||||
.filter((file: { path: string }) => file.path === 'output.pdf')
|
||||
.pop()
|
||||
|
||||
const params = new URLSearchParams({
|
||||
compileGroup: data.compileGroup,
|
||||
popupDownload: 'true',
|
||||
})
|
||||
if (data.clsiServerId) {
|
||||
params.set('clsiserverid', data.clsiServerId)
|
||||
}
|
||||
// Note: Triggering concurrent downloads does not work.
|
||||
// Note: This is affecting the download of .zip files as well.
|
||||
// When creating a dynamic `a` element with `download` attribute,
|
||||
// another "actual" UI click is needed to trigger downloads.
|
||||
// Forwarding the click `event` to the dynamic `a` element does
|
||||
// not work either.
|
||||
location.assign(
|
||||
`/download/project/${project.id}/build/${outputFile.build}/output/output.pdf?${params}`
|
||||
)
|
||||
onDone()
|
||||
} else {
|
||||
setShowErrorModal(true)
|
||||
}
|
||||
})
|
||||
return true
|
||||
})
|
||||
},
|
||||
[project, signal, location]
|
||||
)
|
||||
|
||||
const [showErrorModal, setShowErrorModal] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
{children(
|
||||
pendingCompile ? t('compiling') + '…' : t('download_pdf'),
|
||||
pendingCompile,
|
||||
downloadProject
|
||||
)}
|
||||
{showErrorModal && (
|
||||
<CompileErrorModal
|
||||
project={project}
|
||||
handleClose={() => {
|
||||
setShowErrorModal(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function CompileErrorModal({
|
||||
project,
|
||||
handleClose,
|
||||
}: { project: Project } & { handleClose: () => void }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
<AccessibleModal show onHide={handleClose}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
{project.name}: {t('pdf_unavailable_for_download')}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>{t('generic_linked_file_compile_error')}</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<a href={`/project/${project.id}`}>
|
||||
<Button bsStyle="primary">{t('open_project')}</Button>
|
||||
</a>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const CompileAndDownloadProjectPDFButtonTooltip = memo(
|
||||
function CompileAndDownloadProjectPDFButtonTooltip({
|
||||
project,
|
||||
}: Pick<CompileAndDownloadProjectPDFButtonProps, 'project'>) {
|
||||
return (
|
||||
<CompileAndDownloadProjectPDFButton project={project}>
|
||||
{(text, pendingCompile, compileAndDownloadProject) => (
|
||||
<Tooltip
|
||||
key={`tooltip-compile-and-download-project-${project.id}`}
|
||||
id={`compile-and-download-project-${project.id}`}
|
||||
description={text}
|
||||
overlayProps={{ placement: 'top', trigger: ['hover', 'focus'] }}
|
||||
>
|
||||
<button
|
||||
className="btn btn-link action-btn"
|
||||
aria-label={text}
|
||||
onClick={() => compileAndDownloadProject(() => {})}
|
||||
>
|
||||
{pendingCompile ? (
|
||||
<Icon type="spinner" spin />
|
||||
) : (
|
||||
<Icon type="file-pdf-o" />
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</CompileAndDownloadProjectPDFButton>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default memo(CompileAndDownloadProjectPDFButton)
|
||||
export { CompileAndDownloadProjectPDFButtonTooltip }
|
||||
+1
-1
@@ -17,7 +17,7 @@ function DownloadProjectButton({
|
||||
children,
|
||||
}: DownloadProjectButtonProps) {
|
||||
const { t } = useTranslation()
|
||||
const text = t('download')
|
||||
const text = t('download_zip_file')
|
||||
const location = useLocation()
|
||||
|
||||
const downloadProject = useCallback(() => {
|
||||
|
||||
@@ -7,6 +7,8 @@ import { UntrashProjectButtonTooltip } from './action-buttons/untrash-project-bu
|
||||
import { DownloadProjectButtonTooltip } from './action-buttons/download-project-button'
|
||||
import { LeaveProjectButtonTooltip } from './action-buttons/leave-project-button'
|
||||
import { DeleteProjectButtonTooltip } from './action-buttons/delete-project-button'
|
||||
import { CompileAndDownloadProjectPDFButtonTooltip } from './action-buttons/compile-and-download-project-pdf-button'
|
||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||
|
||||
type ActionsCellProps = {
|
||||
project: Project
|
||||
@@ -17,6 +19,9 @@ export default function ActionsCell({ project }: ActionsCellProps) {
|
||||
<>
|
||||
<CopyProjectButtonTooltip project={project} />
|
||||
<DownloadProjectButtonTooltip project={project} />
|
||||
{isSplitTestEnabled('download-pdf-dashboard') && (
|
||||
<CompileAndDownloadProjectPDFButtonTooltip project={project} />
|
||||
)}
|
||||
<ArchiveProjectButtonTooltip project={project} />
|
||||
<TrashProjectButtonTooltip project={project} />
|
||||
<UnarchiveProjectButtonTooltip project={project} />
|
||||
|
||||
Reference in New Issue
Block a user