diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug
index 121468cec5..aeffef7617 100644
--- a/services/web/app/views/project/editor/pdf.pug
+++ b/services/web/app/views/project/editor/pdf.pug
@@ -3,11 +3,13 @@ div.full-size.pdf(ng-controller="PdfController")
preview-pane(
compiler-state=`{
isAutoCompileOn: autocompile_enabled,
+ isClearingCache: pdf.clearingCache,
isCompiling: pdf.compiling,
isDraftModeOn: draft,
isSyntaxCheckOn: stop_on_validation_error,
logEntries: pdf.logEntries ? pdf.logEntries : {}
}`
+ on-clear-cache="clearCache"
on-recompile="recompile"
on-run-syntax-check-now="runSyntaxCheckNow"
on-set-auto-compile="setAutoCompile"
@@ -379,6 +381,7 @@ script(type='text/ng-template', id='clearCacheModalTemplate')
.modal-body
p #{translate("clear_cache_explanation")}
p #{translate("clear_cache_is_safe")}
+ .alert.alert-danger(ng-if="state.error") #{translate("generic_something_went_wrong")}.
.modal-footer
button.btn.btn-default(
ng-click="cancel()"
diff --git a/services/web/frontend/extracted-translation-keys.json b/services/web/frontend/extracted-translation-keys.json
index 9e17e67058..b0554a1f42 100644
--- a/services/web/frontend/extracted-translation-keys.json
+++ b/services/web/frontend/extracted-translation-keys.json
@@ -29,5 +29,8 @@
"your_project_has_errors",
"view_warnings",
"view_logs",
- "view_pdf"
+ "view_pdf",
+ "recompile_from_scratch",
+ "run_syntax_check_now",
+ "toggle_compile_options_menu"
]
diff --git a/services/web/frontend/js/features/preview/components/preview-pane.js b/services/web/frontend/js/features/preview/components/preview-pane.js
index de2a5c3b43..795461a8fc 100644
--- a/services/web/frontend/js/features/preview/components/preview-pane.js
+++ b/services/web/frontend/js/features/preview/components/preview-pane.js
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'
function PreviewPane({
compilerState,
+ onClearCache,
onRecompile,
onRunSyntaxCheckNow,
onSetAutoCompile,
@@ -35,6 +36,7 @@ function PreviewPane({
compilerState={compilerState}
logsState={{ nErrors, nWarnings, nLogEntries }}
showLogs={showLogs}
+ onClearCache={onClearCache}
onRecompile={onRecompile}
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
onSetAutoCompile={onSetAutoCompile}
@@ -67,6 +69,7 @@ PreviewPane.propTypes = {
isSyntaxCheckOn: PropTypes.bool.isRequired,
logEntries: PropTypes.object.isRequired
}),
+ onClearCache: PropTypes.func.isRequired,
onRecompile: PropTypes.func.isRequired,
onRunSyntaxCheckNow: PropTypes.func.isRequired,
onSetAutoCompile: PropTypes.func.isRequired,
diff --git a/services/web/frontend/js/features/preview/components/preview-recompile-button.js b/services/web/frontend/js/features/preview/components/preview-recompile-button.js
index d715b22f9e..f93530d53d 100644
--- a/services/web/frontend/js/features/preview/components/preview-recompile-button.js
+++ b/services/web/frontend/js/features/preview/components/preview-recompile-button.js
@@ -7,10 +7,12 @@ import Icon from '../../../shared/components/icon'
function PreviewRecompileButton({
compilerState: {
isAutoCompileOn,
+ isClearingCache,
isCompiling,
isDraftModeOn,
isSyntaxCheckOn
},
+ onClearCache,
onRecompile,
onRunSyntaxCheckNow,
onSetAutoCompile,
@@ -19,6 +21,16 @@ function PreviewRecompileButton({
}) {
const { t } = useTranslation()
+ function handleRecompileFromScratch() {
+ onClearCache()
+ .then(() => {
+ onRecompile()
+ })
+ .catch(error => {
+ console.error(error)
+ })
+ }
+
function handleSelectAutoCompileOn() {
onSetAutoCompile(true)
}
@@ -47,7 +59,7 @@ function PreviewRecompileButton({
-
+
+
+
)
@@ -97,11 +120,13 @@ function PreviewRecompileButton({
PreviewRecompileButton.propTypes = {
compilerState: PropTypes.shape({
isAutoCompileOn: PropTypes.bool.isRequired,
+ isClearingCache: PropTypes.bool.isRequired,
isCompiling: PropTypes.bool.isRequired,
isDraftModeOn: PropTypes.bool.isRequired,
isSyntaxCheckOn: PropTypes.bool.isRequired,
logEntries: PropTypes.object.isRequired
}),
+ onClearCache: PropTypes.func.isRequired,
onRecompile: PropTypes.func.isRequired,
onRunSyntaxCheckNow: PropTypes.func.isRequired,
onSetAutoCompile: PropTypes.func.isRequired,
diff --git a/services/web/frontend/js/features/preview/components/preview-toolbar.js b/services/web/frontend/js/features/preview/components/preview-toolbar.js
index 3e8c0b1021..b4ae5cf33c 100644
--- a/services/web/frontend/js/features/preview/components/preview-toolbar.js
+++ b/services/web/frontend/js/features/preview/components/preview-toolbar.js
@@ -6,6 +6,7 @@ import PreviewLogsToggleButton from './preview-logs-toggle-button'
function PreviewToolbar({
compilerState,
logsState,
+ onClearCache,
onRecompile,
onRunSyntaxCheckNow,
onSetAutoCompile,
@@ -24,6 +25,7 @@ function PreviewToolbar({
onSetAutoCompile={onSetAutoCompile}
onSetDraftMode={onSetDraftMode}
onSetSyntaxCheck={onSetSyntaxCheck}
+ onClearCache={onClearCache}
/>
@@ -51,6 +53,7 @@ PreviewToolbar.propTypes = {
nLogEntries: PropTypes.number.isRequired
}),
showLogs: PropTypes.bool.isRequired,
+ onClearCache: PropTypes.func.isRequired,
onRecompile: PropTypes.func.isRequired,
onRunSyntaxCheckNow: PropTypes.func.isRequired,
onSetAutoCompile: PropTypes.func.isRequired,
diff --git a/services/web/frontend/js/ide/pdf/controllers/PdfController.js b/services/web/frontend/js/ide/pdf/controllers/PdfController.js
index 52dd8fbb39..8fa6d65c76 100644
--- a/services/web/frontend/js/ide/pdf/controllers/PdfController.js
+++ b/services/web/frontend/js/ide/pdf/controllers/PdfController.js
@@ -20,12 +20,14 @@ App.controller('PdfController', function(
$modal,
synctex,
eventTracking,
- localStorage
+ localStorage,
+ $q
) {
let autoCompile = true
// pdf.view = uncompiled | pdf | errors
$scope.pdf.view = $scope.pdf.url ? 'pdf' : 'uncompiled'
+ $scope.pdf.clearingCache = false
$scope.shouldShowLogs = false
$scope.wikiEnabled = window.wikiEnabled
@@ -796,7 +798,10 @@ App.controller('PdfController', function(
}
$scope.clearCache = function() {
- return $http({
+ $scope.pdf.clearingCache = true
+ const deferred = $q.defer()
+
+ $http({
url: `/project/${$scope.project_id}/output`,
method: 'DELETE',
params: {
@@ -806,6 +811,20 @@ App.controller('PdfController', function(
'X-Csrf-Token': window.csrfToken
}
})
+ .then(function(response) {
+ $scope.pdf.clearingCache = false
+ return deferred.resolve()
+ })
+ .catch(function(response) {
+ console.error(response)
+ const error = response.data
+ $scope.pdf.clearingCache = false
+ $scope.pdf.renderingError = false
+ $scope.pdf.error = true
+ $scope.pdf.view = 'errors'
+ return deferred.reject(error)
+ })
+ return deferred.promise
}
$scope.toggleLogs = function() {
@@ -1056,14 +1075,20 @@ App.controller('PdfLogEntryController', function($scope, ide, eventTracking) {
})
App.controller('ClearCacheModalController', function($scope, $modalInstance) {
- $scope.state = { inflight: false }
+ $scope.state = { error: false, inflight: false }
$scope.clear = function() {
$scope.state.inflight = true
- $scope.clearCache().then(function() {
- $scope.state.inflight = false
- $modalInstance.close()
- })
+ $scope
+ .clearCache()
+ .then(function() {
+ $scope.state.inflight = false
+ $modalInstance.close()
+ })
+ .catch(function() {
+ $scope.state.error = true
+ $scope.state.inflight = false
+ })
}
$scope.cancel = () => $modalInstance.dismiss('cancel')
diff --git a/services/web/locales/en.json b/services/web/locales/en.json
index 732c34fa3d..e6e449d8f7 100644
--- a/services/web/locales/en.json
+++ b/services/web/locales/en.json
@@ -3,6 +3,7 @@
"n_warnings_plural": "__count__ warnings",
"n_errors": "__count__ error",
"n_errors_plural": "__count__ errors",
+ "toggle_compile_options_menu": "Toggle compile options menu",
"view_pdf": "View PDF",
"your_project_has_errors": "Your project has errors",
"view_warnings": "View warnings",
diff --git a/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js b/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js
new file mode 100644
index 0000000000..df3ec91438
--- /dev/null
+++ b/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js
@@ -0,0 +1,118 @@
+import React from 'react'
+import sinon from 'sinon'
+import { screen, render, fireEvent } from '@testing-library/react'
+import PreviewRecompileButton from '../../../../../frontend/js/features/preview/components/preview-recompile-button'
+const { expect } = require('chai')
+
+describe('
', function() {
+ let onRecompile, onClearCache
+
+ beforeEach(function() {
+ onRecompile = sinon.stub().resolves()
+ onClearCache = sinon.stub().resolves()
+ })
+
+ it('renders all items', function() {
+ renderPreviewRecompileButton()
+
+ const menuItems = screen.getAllByRole('menuitem')
+ expect(menuItems.length).to.equal(8)
+ expect(menuItems.map(item => item.textContent)).to.deep.equal([
+ 'On',
+ 'Off',
+ 'Normal',
+ 'Fast [draft]',
+ 'Check syntax before compile',
+ "Don't check syntax",
+ 'Run syntax check now',
+ 'Recompile from scratch'
+ ])
+
+ const menuHeadingItems = screen.getAllByRole('heading')
+ expect(menuHeadingItems.length).to.equal(3)
+ expect(menuHeadingItems.map(item => item.textContent)).to.deep.equal([
+ 'Auto Compile',
+ 'Compile Mode',
+ 'Syntax Checks'
+ ])
+ })
+
+ describe('Recompile from scratch', function() {
+ describe('click', function() {
+ it('should call onClearCache and onRecompile', async function() {
+ renderPreviewRecompileButton()
+
+ const button = screen.getByRole('menuitem', {
+ name: 'Recompile from scratch'
+ })
+ await fireEvent.click(button)
+ expect(onClearCache).to.have.been.calledOnce
+ expect(onRecompile).to.have.been.calledOnce
+ })
+ })
+ describe('processing', function() {
+ it('shows processing view and disable menuItem when clearing cache', function() {
+ renderPreviewRecompileButton({ isClearingCache: true })
+
+ screen.getByRole('button', { name: 'Compiling …' })
+ expect(
+ screen
+ .getByRole('menuitem', {
+ name: 'Recompile from scratch'
+ })
+ .getAttribute('aria-disabled')
+ ).to.equal('true')
+ expect(
+ screen
+ .getByRole('menuitem', {
+ name: 'Recompile from scratch'
+ })
+ .closest('li')
+ .getAttribute('class')
+ ).to.equal('disabled')
+ })
+
+ it('shows processing view and disable menuItem when recompiling', function() {
+ renderPreviewRecompileButton({ isCompiling: true })
+
+ screen.getByRole('button', { name: 'Compiling …' })
+ expect(
+ screen
+ .getByRole('menuitem', {
+ name: 'Recompile from scratch'
+ })
+ .getAttribute('aria-disabled')
+ ).to.equal('true')
+ expect(
+ screen
+ .getByRole('menuitem', {
+ name: 'Recompile from scratch'
+ })
+ .closest('li')
+ .getAttribute('class')
+ ).to.equal('disabled')
+ })
+ })
+ })
+
+ function renderPreviewRecompileButton(compilerState = {}) {
+ render(
+
{}}
+ onSetAutoCompile={() => {}}
+ onSetDraftMode={() => {}}
+ onSetSyntaxCheck={() => {}}
+ onClearCache={onClearCache}
+ />
+ )
+ }
+})