Merge pull request #321 from sharelatex/ja-recompile-tweaks

Animate toolbar for auto-compile and consolidate trigger logic
This commit is contained in:
James Allen 2018-02-14 09:34:16 +00:00 committed by GitHub
commit a7fabb8e43
9 changed files with 173 additions and 54 deletions

View file

@ -6,21 +6,21 @@ aside.file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
tooltip-html="'"+translate('new_file').replace(' ', '<br>')+"'",
tooltip-placement="bottom"
)
i.fa.fa-file
i.fa.fa-fw.fa-file
a(
href,
ng-click="openNewFolderModal()",
tooltip-html="'"+translate('new_folder').replace(' ', '<br>')+"'",
tooltip-placement="bottom"
)
i.fa.fa-folder
i.fa.fa-fw.fa-folder
a(
href,
ng-click="openUploadFileModal()",
tooltip=translate('upload'),
tooltip-placement="bottom"
)
i.fa.fa-upload
i.fa.fa-fw.fa-upload
.toolbar-right
a(
@ -30,7 +30,7 @@ aside.file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
tooltip-placement="bottom",
ng-show="multiSelectedCount == 0"
)
i.fa.fa-pencil
i.fa.fa-fw.fa-pencil
a(
href,
ng-click="openDeleteModalForSelected()",
@ -38,7 +38,7 @@ aside.file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
tooltip-placement="bottom",
tooltip-append-to-body="true"
)
i.fa.fa-trash-o
i.fa.fa-fw.fa-trash-o
.file-tree-inner(

View file

@ -1,5 +1,5 @@
div.full-size.pdf(ng-controller="PdfController")
.toolbar.toolbar-pdf
.toolbar.toolbar-pdf(ng-class="{ 'uncompiled-changes': uncompiledChanges && !autoCompileLintingError }")
.btn-group.btn-recompile-group#recompile(
dropdown,
tooltip-html="'"+translate('recompile_pdf')+" <span class=\"keyboard-shortcut\">({{modifierKey}} + Enter)</span>'"
@ -65,7 +65,7 @@ div.full-size.pdf(ng-controller="PdfController")
tooltip=translate('stop_compile')
tooltip-placement="bottom"
)
i.fa.fa-stop()
i.fa.fa-fw.fa-stop()
a.log-btn(
href
ng-click="toggleLogs()"
@ -73,7 +73,7 @@ div.full-size.pdf(ng-controller="PdfController")
tooltip=translate('logs_and_output_files')
tooltip-placement="bottom"
)
i.fa.fa-file-text-o
i.fa.fa-fw.fa-file-text-o
span.label(
ng-show="pdf.logEntries.warnings.length + pdf.logEntries.errors.length > 0"
ng-class="{\
@ -89,9 +89,21 @@ div.full-size.pdf(ng-controller="PdfController")
tooltip=translate('download_pdf')
tooltip-placement="bottom"
)
i.fa.fa-download
i.fa.fa-fw.fa-download
.toolbar-right
span.auto-compile-status.small(
ng-show="uncompiledChanges && !compiling && !autoCompileLintingError"
) #{translate('uncompiled_changes')}
span.auto-compile-status.auto-compile-error.small(
ng-show="autoCompileLintingError"
tooltip-placement="bottom"
tooltip=translate("code_check_failed_explanation")
tooltip-append-to-body="true"
)
i.fa.fa-fw.fa-exclamation-triangle
|
| #{translate("code_check_failed")}
a(
href,
ng-click="switchToFlatLayout()"

View file

@ -352,8 +352,13 @@ define [
v: version
@doc.on "change", (ops, oldSnapshot, msg) =>
@_applyOpsToRanges(ops, oldSnapshot, msg)
@ide.$scope.$emit "doc:changed",
doc_id: @doc_id
@doc.on "flipped_pending_to_inflight", () =>
@trigger "flipped_pending_to_inflight"
@doc.on "saved", () =>
@ide.$scope.$emit "doc:saved",
doc_id: @doc_id
_onError: (error, meta = {}) ->
meta.doc_id = @doc_id

View file

@ -46,6 +46,8 @@ define [
@trigger "remoteop", args...
@_doc.on "flipped_pending_to_inflight", () =>
@trigger "flipped_pending_to_inflight"
@_doc.on "saved", () =>
@trigger "saved"
@_doc.on "error", (e) =>
@_handleError(e)

View file

@ -164,6 +164,9 @@ class Doc
@inflightOp = null
@inflightSubmittedIds.length = 0
if @pendingOp == null # All ops are acked
@emit 'saved'
error = msg.error
if error
# The server has rejected an op from the client for some reason.

View file

@ -5,8 +5,12 @@ define [
"libs/bib-log-parser"
"services/log-hints-feedback"
], (App, Ace, HumanReadableLogs, BibLogParser) ->
AUTO_COMPILE_TIMEOUT = 5000
OP_ACKNOWLEDGEMENT_TIMEOUT = 1100
AUTO_COMPILE_MAX_WAIT = 5000
# We add a 1 second debounce to sending user changes to server if they aren't
# collaborating with anyone. This needs to be higher than that, and allow for
# client to server latency, otherwise we compile before the op reaches the server
# and then again on ack.
AUTO_COMPILE_DEBOUNCE = 2000
App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) ->
# enable per-user containers by default
@ -75,38 +79,102 @@ define [
$scope.pdf.view = 'errors'
$scope.pdf.renderingError = true
autoCompileTimeout = null
autoCompileInterval = null
autoCompileIfReady = () ->
if $scope.pdf.compiling
return
# Only checking linting if syntaxValidation is on and visible to the user
autoCompileLintingError = ide.$scope.hasLintingError and ide.$scope.settings.syntaxValidation
if $scope.autoCompileLintingError != autoCompileLintingError
$scope.$apply () ->
$scope.autoCompileLintingError = autoCompileLintingError
# We've likely been waiting a while until the user fixed the linting, but we
# don't want to compile as soon as it is fixed, so reset the timeout.
$scope.startedTryingAutoCompileAt = Date.now()
$scope.docLastChangedAt = Date.now()
if autoCompileLintingError
return
# If there's a longish compile, don't compile immediately after if user is still typing
startedTryingAt = Math.max($scope.startedTryingAutoCompileAt, $scope.lastFinishedCompileAt || 0)
timeSinceStartedTrying = Date.now() - startedTryingAt
timeSinceLastChange = Date.now() - $scope.docLastChangedAt
shouldCompile = false
if timeSinceLastChange > AUTO_COMPILE_DEBOUNCE # Don't compile in the middle of the user typing
shouldCompile = true
else if timeSinceStartedTrying > AUTO_COMPILE_MAX_WAIT # Unless they type for a long time
shouldCompile = true
else if timeSinceStartedTrying < 0 or timeSinceLastChange < 0
# If time is non-monotonic, assume that the user's system clock has been
# changed and continue with compile
shouldCompile = true
if shouldCompile
triggerAutoCompile()
triggerAutoCompile = () ->
return if autoCompileTimeout or $scope.ui.pdfHidden
$scope.recompile(isAutoCompileOnChange: true)
timeSinceLastCompile = Date.now() - $scope.recompiledAt
# If time is non-monotonic, assume that the user's system clock has been
# changed and continue with recompile
isTimeNonMonotonic = timeSinceLastCompile < 0
startTryingAutoCompile = () ->
return if autoCompileInterval?
$scope.startedTryingAutoCompileAt = Date.now()
autoCompileInterval = setInterval autoCompileIfReady, 200
if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT
# If user has code check disabled, it is likely because they have
# linting errors that they are ignoring. Therefore it doesn't make sense
# to block auto compiles. It also causes problems where server-provided
# linting errors aren't cleared after typing
if (ide.$scope.settings.syntaxValidation and !ide.$scope.hasLintingError)
$scope.recompile(isAutoCompileOnChange: true) # compile if no linting errors
else if !ide.$scope.settings.syntaxValidation
$scope.recompile(isAutoCompileOnChange: true) # always recompile
stopTryingAutoCompile = () ->
clearInterval autoCompileInterval
autoCompileInterval = null
$scope.$watch "uncompiledChanges", (uncompiledChanges) ->
if uncompiledChanges
startTryingAutoCompile()
else
# Extend remainder of timeout
autoCompileTimeout = setTimeout () ->
autoCompileTimeout = null
triggerAutoCompile()
, AUTO_COMPILE_TIMEOUT - timeSinceLastCompile
stopTryingAutoCompile()
autoCompileListener = null
$scope.uncompiledChanges = false
recalculateUncompiledChanges = () ->
if $scope.ui.pdfHidden
# Don't bother auto-compiling if pdf isn't visible
$scope.uncompiledChanges = false
else if !$scope.docLastChangedAt?
$scope.uncompiledChanges = false
else if !$scope.lastStartedCompileAt? or $scope.docLastChangedAt > $scope.lastStartedCompileAt
$scope.uncompiledChanges = true
else
$scope.uncompiledChanges = false
_updateDocLastChangedAt = () ->
$scope.docLastChangedAt = Date.now()
recalculateUncompiledChanges()
onDocChanged = () ->
$scope.autoCompileLintingError = false
_updateDocLastChangedAt()
onDocSaved = () ->
# We use the save as a trigger too, to account for the delay between the client
# and server. Otherwise, we might have compiled after the user made
# the change on the client, but before the server had it.
_updateDocLastChangedAt()
onCompilingStateChanged = (compiling) ->
recalculateUncompiledChanges()
autoCompileListeners = []
toggleAutoCompile = (enabling) ->
if enabling
autoCompileListener = ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, OP_ACKNOWLEDGEMENT_TIMEOUT)
autoCompileListeners = [
ide.$scope.$on "doc:changed", onDocChanged
ide.$scope.$on "doc:saved", onDocSaved
$scope.$watch "pdf.compiling", onCompilingStateChanged
]
else
autoCompileListener() if autoCompileListener
autoCompileListener = null
for unbind in autoCompileListeners
unbind()
autoCompileListeners = []
$scope.autoCompileLintingError = false
$scope.autocompile_enabled = localStorage("autocompile_enabled:#{$scope.project_id}") or false
$scope.$watch "autocompile_enabled", (newValue, oldValue) ->
@ -428,6 +496,7 @@ define [
event_tracking.sendMBSampled "editor-recompile-sampled", options
$scope.lastStartedCompileAt = Date.now()
$scope.pdf.compiling = true
$scope.pdf.isAutoCompileOnLoad = options?.isAutoCompileOnLoad # initial autocompile
@ -460,7 +529,7 @@ define [
$scope.pdf.error = true
$scope.pdf.view = 'errors'
.finally () ->
$scope.recompiledAt = Date.now()
$scope.lastFinishedCompileAt = Date.now()
# This needs to be public.
ide.$scope.recompile = $scope.recompile

View file

@ -1,13 +1,34 @@
@stripe-width: 20px;
@keyframes pdf-toolbar-stripes {
from { background-position: 0 0; }
to { background-position: @stripe-width 0; }
}
.pdf .toolbar.toolbar-pdf when (@is-overleaf = true) {
.toolbar-small-mixin;
.toolbar-alt-mixin;
border-bottom: 0;
padding-right: 5px;
&.uncompiled-changes {
#gradient > .striped(@color: rgba(255,255,255,.10), @angle: -45deg);
background-size: @stripe-width @stripe-width;
.animation(pdf-toolbar-stripes 2s linear infinite);
}
.auto-compile-status {
color: white;
margin-right: (@line-height-computed / 2);
i {
color: @brand-danger;
}
}
}
.pdf .toolbar.toolbar-pdf when (@is-overleaf = false) {
.toolbar-tall-mixin;
padding: 0 (@line-height-computed / 2);
.auto-compile-status {
display: none;
}
}
.pdf {
@ -31,29 +52,30 @@
.btn-recompile-group when (@is-overleaf = true) {
align-self: stretch;
margin-right: 5px;
margin-right: 6px;
.btn-recompile {
height: 100%;
.btn-primary;
padding-top: 3px;
padding-bottom: 3px;
&:first-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&[disabled] {
background-color: mix(@btn-primary-bg, @toolbar-alt-bg-color, 65%);
.opacity(1.0);
}
}
}
.btn-recompile-group when (@is-overleaf = false) {
margin-right: (@line-height-computed / 2);
}
.btn-recompile when (@is-overleaf = true) {
height: 100%;
.btn-primary;
padding-top: 3px;
padding-bottom: 3px;
&:first-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
.btn-recompile {
.btn-info;
}
}
.btn-recompile when (@is-overleaf = false) {
.btn-info;
}
.btn-split-screen when (@is-overleaf = false) {
.fa {
display: none;
@ -146,7 +168,6 @@
width: 16px;
height: 16px;
border: 1px solid @gray-light;
margin-top: 5px;
}
i.full-screen {

View file

@ -25,7 +25,9 @@
.toolbar-right > a:not(.btn) {
display: inline-block;
color: @toolbar-icon-btn-color;
padding: 0 5px;
padding: 4px 2px;
line-height: 1;
height: 24px;
border-radius: @border-radius-small;
&.toolbar-header-back-projects {
padding: 5px 10px 4px;
@ -49,6 +51,10 @@
}
}
&.toolbar-pdf > a:not(.btn) {
margin-right: 3px;
}
.btn-full-height {
border: none;
border-radius: 0;

View file

@ -13,6 +13,7 @@
border-top: @caret-width-base solid;
border-right: @caret-width-base solid transparent;
border-left: @caret-width-base solid transparent;
margin-top: -@caret-width-base/2;
}
// The dropdown wrapper (div)