diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 3b357b4d9..df386ad80 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -29,6 +29,10 @@ angular.module('app.frontend') this.postThemeToExternalEditor(); }.bind(this)) + $rootScope.$on("sync:taking-too-long", function(){ + this.syncTakingTooLong = true; + }.bind(this)); + window.addEventListener("message", function(event){ if(event.data.status) { this.postNoteToExternalEditor(); @@ -178,6 +182,7 @@ angular.module('app.frontend') var note = this.note; note.dummy = false; this.save()(note, function(success){ + this.syncTakingTooLong = false; if(success) { if(statusTimeout) $timeout.cancel(statusTimeout); statusTimeout = $timeout(function(){ diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index 7c47b8ef9..4389350e5 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -1,11 +1,12 @@ class SyncManager { - constructor($rootScope, modelManager, authManager, dbManager, httpManager) { + constructor($rootScope, modelManager, authManager, dbManager, httpManager, $interval) { this.$rootScope = $rootScope; this.httpManager = httpManager; this.modelManager = modelManager; this.authManager = authManager; this.dbManager = dbManager; + this.$interval = $interval; this.syncStatus = {}; } @@ -122,18 +123,40 @@ class SyncManager { } } + beginCheckingIfSyncIsTakingTooLong() { + this.syncStatus.checker = this.$interval(function(){ + // check to see if the ongoing sync is taking too long, alert the user + var secondsPassed = (new Date() - this.syncStatus.syncStart) / 1000; + var warningThreshold = 5; // seconds + if(secondsPassed > warningThreshold) { + this.$rootScope.$broadcast("sync:taking-too-long"); + this.stopCheckingIfSyncIsTakingTooLong(); + } + }.bind(this), 500) + } + + stopCheckingIfSyncIsTakingTooLong() { + this.$interval.cancel(this.syncStatus.checker); + } + sync(callback, options = {}) { + var allDirtyItems = this.modelManager.getDirtyItems(); + if(this.syncStatus.syncOpInProgress) { this.repeatOnCompletion = true; if(callback) { this.queuedCallbacks.push(callback); } + + // write to local storage nonetheless, since some users may see several second delay in server response. + // if they close the browser before the ongoing sync request completes, local changes will be lost if we dont save here + this.writeItemsToLocalStorage(allDirtyItems, false, null); + console.log("Sync op in progress; returning."); return; } - var allDirtyItems = this.modelManager.getDirtyItems(); // we want to write all dirty items to disk only if the user is offline, or if the sync op fails // if the sync op succeeds, these items will be written to disk by handling the "saved_items" response from the server @@ -146,6 +169,8 @@ class SyncManager { var isContinuationSync = this.syncStatus.needsMoreSync; this.syncStatus.syncOpInProgress = true; + this.syncStatus.syncStart = new Date(); + this.beginCheckingIfSyncIsTakingTooLong(); let submitLimit = 100; var subItems = allDirtyItems.slice(0, submitLimit); @@ -172,6 +197,10 @@ class SyncManager { params.sync_token = this.syncToken; params.cursor_token = this.cursorToken; + var onSyncCompletion = function(response) { + this.stopCheckingIfSyncIsTakingTooLong(); + }.bind(this); + var onSyncSuccess = function(response) { this.modelManager.clearDirtyItems(subItems); this.syncStatus.error = null; @@ -197,6 +226,8 @@ class SyncManager { this.syncToken = response.sync_token; this.cursorToken = response.cursor_token; + onSyncCompletion(response); + if(this.cursorToken || this.syncStatus.needsMoreSync) { setTimeout(function () { this.sync(callback, options); @@ -229,6 +260,8 @@ class SyncManager { this.syncStatus.error = error; this.writeItemsToLocalStorage(allDirtyItems, false, null); + onSyncCompletion(response); + this.$rootScope.$broadcast("sync:error", error); this.callQueuedCallbacksAndCurrent(callback, {error: "Sync error"}); diff --git a/app/assets/stylesheets/app/_standard.scss b/app/assets/stylesheets/app/_standard.scss index 80734ae68..aee73adf3 100644 --- a/app/assets/stylesheets/app/_standard.scss +++ b/app/assets/stylesheets/app/_standard.scss @@ -145,6 +145,10 @@ color: red !important; } +.orange { + color: orange !important; +} + .bold { font-weight: bold !important; } diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 42ec42fc9..e14eaf3b9 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -4,7 +4,9 @@ %input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)", "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", "select-on-click" => "true"} - #save-status{"ng-class" => "{'red bold': ctrl.saveError}", "ng-bind-html" => "ctrl.noteStatus"} + + #save-status{"ng-class" => "{'red bold': ctrl.saveError, 'orange bold': ctrl.syncTakingTooLong}", "ng-bind-html" => "ctrl.noteStatus"} + .editor-tags %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"}