From af85c83877258ec64e3d9b6c4af24feb51d5d32b Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 17 Apr 2015 11:22:26 +0100 Subject: [PATCH] Buffer updates when only a single user is editing a document Add in 5 second delay between flushing updates when only a single user is editing a document. As soon as an update is received from another user we switch to sending updates immediately again so there is no latency between collaborators. The logic applies to individual docs, so two users can be editing different docs and will still buffer updates since they will not affect each other. --- services/web/app/views/project/editor.jade | 2 +- services/web/public/coffee/ide/editor/Document.coffee | 7 +++++++ .../web/public/coffee/ide/editor/EditorManager.coffee | 3 +++ .../web/public/coffee/ide/editor/ShareJsDoc.coffee | 6 ++++++ .../coffee/ide/editor/sharejs/vendor/client/doc.coffee | 10 +++++++++- .../coffee/ide/pdf/controllers/PdfController.coffee | 2 ++ 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.jade index 303bf9afb0..23abd96759 100644 --- a/services/web/app/views/project/editor.jade +++ b/services/web/app/views/project/editor.jade @@ -29,7 +29,7 @@ block content strong #{translate("reconnecting")}... .div(ng-controller="SavingNotificationController") - .alert.alert-warning.small( ng-repeat="(doc_id, state) in docSavingStatus" ng-if="state.unsavedSeconds > 3") #{translate("saving_notification_with_seconds", {docname:"{{ state.doc.name }}", seconds:"{{ state.unsavedSeconds }}"})} + .alert.alert-warning.small( ng-repeat="(doc_id, state) in docSavingStatus" ng-if="state.unsavedSeconds > 8") #{translate("saving_notification_with_seconds", {docname:"{{ state.doc.name }}", seconds:"{{ state.unsavedSeconds }}"})} include ./editor/left-menu diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index deb8f5047c..561c51b133 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -14,6 +14,10 @@ define [ return true if doc.hasBufferedOps() return false + @flushAll: () -> + for doc_id, doc of @openDocs + doc.flush() + constructor: (@ide, @doc_id) -> @connected = @ide.socket.socket.connected @joined = false @@ -109,6 +113,9 @@ define [ else @_leaveDoc(callback) + flush: () -> + @doc?.flushPendingOps() + pollSavedStatus: () -> # returns false if doc has ops waiting to be acknowledged or # sent that haven't changed since the last time we checked. diff --git a/services/web/public/coffee/ide/editor/EditorManager.coffee b/services/web/public/coffee/ide/editor/EditorManager.coffee index 38927806d6..47f6a15fe5 100644 --- a/services/web/public/coffee/ide/editor/EditorManager.coffee +++ b/services/web/public/coffee/ide/editor/EditorManager.coffee @@ -28,6 +28,9 @@ define [ initialized = true @autoOpenDoc() + @$scope.$on "flush-changes", () => + Document.flushAll() + autoOpenDoc: () -> open_doc_id = @ide.localStorage("doc.open_id.#{@$scope.project_id}") or diff --git a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee index 0894bbff7e..f4a0d83a88 100644 --- a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee +++ b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee @@ -2,6 +2,8 @@ define [ "utils/EventEmitter" "libs/sharejs" ], (EventEmitter, ShareJs) -> + SINGLE_USER_FLUSH_DELAY = 5000 #ms + class ShareJsDoc extends EventEmitter constructor: (@doc_id, docLines, version, @socket) -> # Dencode any binary bits of data @@ -33,11 +35,15 @@ define [ @_doc = new ShareJs.Doc @connection, @doc_id, type: @type + @_doc.setFlushDelay(SINGLE_USER_FLUSH_DELAY) @_doc.on "change", () => @trigger "change" @_doc.on "acknowledge", () => @trigger "acknowledge" @_doc.on "remoteop", () => + # As soon as we're working with a collaborator, start sending + # ops as quickly as possible for low latency. + @_doc.setFlushDelay(0) @trigger "remoteop" @_bindToDocChanges(@_doc) diff --git a/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee b/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee index a8cb7be3c1..c74adad50e 100644 --- a/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee +++ b/services/web/public/coffee/ide/editor/sharejs/vendor/client/doc.coffee @@ -247,6 +247,9 @@ class Doc # Only one op can be in-flight at a time, so if an op is already on its way then # this method does nothing. flush: => + delete @flushTimeout + #console.log "CALLED FLUSH" + return unless @connection.state == 'ok' and @inflightOp == null and @pendingOp != null # Rotate null -> pending -> inflight @@ -256,6 +259,7 @@ class Doc @pendingOp = null @pendingCallbacks = [] + #console.log "SENDING OP TO SERVER", @inflightOp, @version @connection.send {doc:@name, op:@inflightOp, v:@version} # Submit an op to the server. The op maybe held for a little while before being sent, as only one @@ -277,7 +281,11 @@ class Doc # A timeout is used so if the user sends multiple ops at the same time, they'll be composed # & sent together. - setTimeout @flush, 0 + if !@flushTimeout? + @flushTimeout = setTimeout @flush, @_flushDelay || 0 + + setFlushDelay: (delay) => + @_flushDelay = delay shout: (msg) => # Meta ops don't have to queue, they can go direct. Good/bad idea? diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index f7e4f91082..3aaf6e7600 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -123,6 +123,8 @@ define [ return if $scope.pdf.compiling $scope.pdf.compiling = true + ide.$scope.$broadcast("flush-changes") + if !options.isAutoCompile compileCount++ if compileCount == 1