Load ranges from docupdater and toggle track changes when possible

This commit is contained in:
James Allen
2016-12-08 14:10:30 +00:00
parent 1830d41eba
commit 1d426e538f
9 changed files with 150 additions and 94 deletions

View File

@@ -52,8 +52,8 @@ div.full-size(
review-panel="reviewPanel",
events-bridge="reviewPanelEventsBridge"
track-changes-enabled="trackChangesFeatureFlag",
track-new-changes= "reviewPanel.trackNewChanges",
changes-tracker="reviewPanel.changesTracker",
track-changes= "editor.trackChanges",
ranges-tracker="reviewPanel.rangesTracker",
doc-id="editor.open_doc_id"
)

View File

@@ -1,10 +1,12 @@
#review-panel
.review-panel-toolbar
span.review-panel-toolbar-label(ng-click="reviewPanel.trackNewChanges = true;", ng-if="reviewPanel.trackNewChanges === false") Track Changes is
span.review-panel-toolbar-label(ng-click="editor.wantTrackChanges = true;", ng-if="editor.wantTrackChanges === false") Track Changes is
strong off
span.review-panel-toolbar-label(ng-click="reviewPanel.trackNewChanges = false;", ng-if="reviewPanel.trackNewChanges === true") Track Changes is
span.review-panel-toolbar-label(ng-click="editor.wantTrackChanges = false;", ng-if="editor.wantTrackChanges === true") Track Changes is
strong on
review-panel-toggle(ng-model="reviewPanel.trackNewChanges")
review-panel-toggle(ng-if="editor.wantTrackChanges == editor.trackChanges", ng-model="editor.wantTrackChanges")
span.review-panel-toolbar-spinner(ng-if="editor.wantTrackChanges != editor.trackChanges")
i.fa.fa-spin.fa-spinner
.rp-entry-list(
review-panel-sorted

View File

@@ -77,6 +77,9 @@ define [
hasBufferedOps: () ->
@doc?.hasBufferedOps()
setTrackingChanges: (track_changes) ->
@doc.track_changes = track_changes
_bindToSocketEvents: () ->
@_onUpdateAppliedHandler = (update) => @_onUpdateApplied(update)
@@ -239,16 +242,19 @@ define [
_joinDoc: (callback = (error) ->) ->
if @doc?
@ide.socket.emit 'joinDoc', @doc_id, @doc.getVersion(), (error, docLines, version, updates) =>
@ide.socket.emit 'joinDoc', @doc_id, @doc.getVersion(), (error, docLines, version, updates, ranges) =>
return callback(error) if error?
@joined = true
@doc.catchUp( updates )
# TODO: Worry about whether these ranges are consistent with the doc still
@opening_ranges = ranges
callback()
else
@ide.socket.emit 'joinDoc', @doc_id, (error, docLines, version) =>
@ide.socket.emit 'joinDoc', @doc_id, (error, docLines, version, updates, ranges) =>
return callback(error) if error?
@joined = true
@doc = new ShareJsDoc @doc_id, docLines, version, @ide.socket
@opening_ranges = ranges
@_bindToShareJsDocEvents()
callback()

View File

@@ -10,6 +10,8 @@ define [
open_doc_id: null
open_doc_name: null
opening: true
trackChanges: false
wantTrackChanges: false
}
@$scope.$on "entity:selected", (event, entity) =>
@@ -31,6 +33,10 @@ define [
@$scope.$on "flush-changes", () =>
Document.flushAll()
@$scope.$watch "editor.wantTrackChanges", (value) =>
return if !value?
@_syncTrackChangesState(@$scope.editor.sharejs_doc)
autoOpenDoc: () ->
open_doc_id =
@@ -83,6 +89,8 @@ define [
"Sorry, something went wrong opening this document. Please try again."
)
return
@_syncTrackChangesState(sharejs_doc)
@$scope.$broadcast "doc:opened"
@@ -144,3 +152,28 @@ define [
stopIgnoringExternalUpdates: () ->
@_ignoreExternalUpdates = false
_syncTimeout: null
_syncTrackChangesState: (doc) ->
return if !doc?
if @_syncTimeout?
clearTimeout @_syncTimeout
@_syncTimeout = null
want = @$scope.editor.wantTrackChanges
have = @$scope.editor.trackChanges
if want == have
return
console.log "Trying to set track changes to:", want
do tryToggle = () =>
saved = !doc.getInflightOp()? and !doc.getPendingOp()?
if saved
console.log "SUCCESS, changing value", want
doc.setTrackingChanges(want)
@$scope.$apply () =>
@$scope.editor.trackChanges = want
else
console.log "Still in flight, will try soon"
@_syncTimeout = setTimeout tryToggle, 100

View File

@@ -9,21 +9,9 @@ define [
# Dencode any binary bits of data
# See http://ecmanaut.blogspot.co.uk/2006/07/encoding-decoding-utf8-in-javascript.html
@type = "text"
docLines = for line in docLines
if line.text?
@type = "json"
line.text = decodeURIComponent(escape(line.text))
else
@type = "text"
line = decodeURIComponent(escape(line))
line
if @type == "text"
snapshot = docLines.join("\n")
else if @type == "json"
snapshot = { lines: docLines }
else
throw new Error("Unknown type: #{@type}")
docLines = (decodeURIComponent(escape(line)) for line in docLines)
snapshot = docLines.join("\n")
@track_changes = false
@connection = {
send: (update) =>
@@ -34,6 +22,9 @@ define [
if window.dropUpdates? and Math.random() < window.dropUpdates
sl_console.log "Simulating a lost update", update
return
if @track_changes
update.meta ?= {}
update.meta.tc = 1
@socket.emit "applyOtUpdate", @doc_id, update, (error) =>
return @_handleError(error) if error?
state: "ok"

View File

@@ -54,9 +54,9 @@ define [
syntaxValidation: "="
reviewPanel: "="
eventsBridge: "="
trackNewChanges: "="
trackChanges: "="
trackChangesEnabled: "="
changesTracker: "="
rangesTracker: "="
docId: "="
}
link: (scope, element, attrs) ->

View File

@@ -10,15 +10,22 @@ define [
constructor: (@$scope, @editor, @element) ->
window.trackChangesManager ?= @
@$scope.$watch "changesTracker", (changesTracker) =>
return if !changesTracker?
@disconnectFromChangesTracker()
@changesTracker = changesTracker
@connectToChangesTracker()
@$scope.$watch "rangesTracker", (rangesTracker) =>
return if !rangesTracker?
@disconnectFromRangesTracker()
@rangesTracker = rangesTracker
@connectToRangesTracker()
@$scope.$watch "trackNewChanges", (track_new_changes) =>
return if !track_new_changes?
@changesTracker?.track_changes = track_new_changes
@$scope.$watch "trackChanges", (track_changes) =>
return if !track_changes?
@rangesTracker?.track_changes = track_changes
@$scope.$watch "sharejsDoc", (doc) =>
return if !doc?
if doc.opening_ranges?.changes?
@rangesTracker.changes = doc.opening_ranges.changes
if doc.opening_ranges?.comments?
@rangesTracker.comments = doc.opening_ranges.comments
@$scope.$on "comment:add", (e, comment) =>
@addCommentToSelection(comment)
@@ -75,12 +82,12 @@ define [
else
user_id = window.user.id
was_tracking = @changesTracker.track_changes
was_tracking = @rangesTracker.track_changes
if @dont_track_next_update
@changesTracker.track_changes = false
@rangesTracker.track_changes = false
@dont_track_next_update = false
@applyChange(e, { user_id })
@changesTracker.track_changes = was_tracking
@rangesTracker.track_changes = was_tracking
# TODO: Just for debugging, remove before going live.
setTimeout () =>
@@ -111,68 +118,68 @@ define [
else
unbindFromAce()
disconnectFromChangesTracker: () ->
disconnectFromRangesTracker: () ->
@changeIdToMarkerIdMap = {}
if @changesTracker?
@changesTracker.off "insert:added"
@changesTracker.off "insert:removed"
@changesTracker.off "delete:added"
@changesTracker.off "delete:removed"
@changesTracker.off "changes:moved"
@changesTracker.off "comment:added"
@changesTracker.off "comment:moved"
@changesTracker.off "comment:removed"
@changesTracker.off "comment:resolved"
@changesTracker.off "comment:unresolved"
if @rangesTracker?
@rangesTracker.off "insert:added"
@rangesTracker.off "insert:removed"
@rangesTracker.off "delete:added"
@rangesTracker.off "delete:removed"
@rangesTracker.off "changes:moved"
@rangesTracker.off "comment:added"
@rangesTracker.off "comment:moved"
@rangesTracker.off "comment:removed"
@rangesTracker.off "comment:resolved"
@rangesTracker.off "comment:unresolved"
connectToChangesTracker: () ->
@changesTracker.track_changes = @$scope.trackNewChanges
connectToRangesTracker: () ->
@rangesTracker.track_changes = @$scope.trackChanges
@changesTracker.on "insert:added", (change) =>
@rangesTracker.on "insert:added", (change) =>
sl_console.log "[insert:added]", change
@_onInsertAdded(change)
@changesTracker.on "insert:removed", (change) =>
@rangesTracker.on "insert:removed", (change) =>
sl_console.log "[insert:removed]", change
@_onInsertRemoved(change)
@changesTracker.on "delete:added", (change) =>
@rangesTracker.on "delete:added", (change) =>
sl_console.log "[delete:added]", change
@_onDeleteAdded(change)
@changesTracker.on "delete:removed", (change) =>
@rangesTracker.on "delete:removed", (change) =>
sl_console.log "[delete:removed]", change
@_onDeleteRemoved(change)
@changesTracker.on "changes:moved", (changes) =>
@rangesTracker.on "changes:moved", (changes) =>
sl_console.log "[changes:moved]", changes
@_onChangesMoved(changes)
@changesTracker.on "comment:added", (comment) =>
@rangesTracker.on "comment:added", (comment) =>
sl_console.log "[comment:added]", comment
@_onCommentAdded(comment)
@changesTracker.on "comment:moved", (comment) =>
@rangesTracker.on "comment:moved", (comment) =>
sl_console.log "[comment:moved]", comment
@_onCommentMoved(comment)
@changesTracker.on "comment:removed", (comment) =>
@rangesTracker.on "comment:removed", (comment) =>
sl_console.log "[comment:removed]", comment
@_onCommentRemoved(comment)
@changesTracker.on "comment:resolved", (comment) =>
@rangesTracker.on "comment:resolved", (comment) =>
sl_console.log "[comment:resolved]", comment
@_onCommentRemoved(comment)
@changesTracker.on "comment:unresolved", (comment) =>
@rangesTracker.on "comment:unresolved", (comment) =>
sl_console.log "[comment:unresolved]", comment
@_onCommentAdded(comment)
redrawAnnotations: () ->
for change in @changesTracker.changes
for change in @rangesTracker.changes
if change.op.i?
@_onInsertAdded(change)
else if change.op.d?
@_onDeleteAdded(change)
for comment in @changesTracker.comments
for comment in @rangesTracker.comments
@_onCommentAdded(comment)
addComment: (offset, length, content) ->
@changesTracker.addComment offset, length, {
@rangesTracker.addComment offset, length, {
thread: [{
content: content
user_id: window.user_id
@@ -192,12 +199,12 @@ define [
@editor.selection.selectLine()
acceptChangeId: (change_id) ->
@changesTracker.removeChangeId(change_id)
@rangesTracker.removeChangeId(change_id)
rejectChangeId: (change_id) ->
change = @changesTracker.getChange(change_id)
change = @rangesTracker.getChange(change_id)
return if !change?
@changesTracker.removeChangeId(change_id)
@rangesTracker.removeChangeId(change_id)
@dont_track_next_update = true
session = @editor.getSession()
if change.op.d?
@@ -215,15 +222,15 @@ define [
throw new Error("unknown change: #{JSON.stringify(change)}")
removeCommentId: (comment_id) ->
@changesTracker.removeCommentId(comment_id)
@rangesTracker.removeCommentId(comment_id)
resolveCommentId: (comment_id, user_id) ->
@changesTracker.resolveCommentId(comment_id, {
@rangesTracker.resolveCommentId(comment_id, {
user_id, ts: new Date()
})
unresolveCommentId: (comment_id) ->
@changesTracker.unresolveCommentId(comment_id)
@rangesTracker.unresolveCommentId(comment_id)
checkMapping: () ->
session = @editor.getSession()
@@ -234,7 +241,7 @@ define [
markers[marker_id] = marker
expected_markers = []
for change in @changesTracker.changes
for change in @rangesTracker.changes
if @changeIdToMarkerIdMap[change.id]?
op = change.op
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id]
@@ -246,7 +253,7 @@ define [
expected_markers.push { marker_id: background_marker_id, start, end }
expected_markers.push { marker_id: callout_marker_id, start, end: start }
for comment in @changesTracker.comments
for comment in @rangesTracker.comments
if @changeIdToMarkerIdMap[comment.id]?
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[comment.id]
start = @_shareJsOffsetToAcePosition(comment.offset)
@@ -269,7 +276,7 @@ define [
applyChange: (delta, metadata) ->
op = @_aceChangeToShareJs(delta)
@changesTracker.applyOp(op, metadata)
@rangesTracker.applyOp(op, metadata)
updateFocus: () ->
selection = @editor.getSelectionRange()

View File

@@ -1,7 +1,5 @@
define [
"utils/EventEmitter"
], (EventEmitter) ->
class ChangesTracker extends EventEmitter
load = (EventEmitter) ->
class RangesTracker extends EventEmitter
# The purpose of this class is to track a set of inserts and deletes to a document, like
# track changes in Word. We store these as a set of ShareJs style ranges:
# {i: "foo", p: 42} # Insert 'foo' at offset 42
@@ -36,7 +34,7 @@ define [
# * Deletes by another user will consume deletes by the first user
# * Inserts by another user will not combine with inserts by the first user. If they are in the
# middle of a previous insert by the first user, the original insert will be split into two.
constructor: () ->
constructor: (@changes = [], @comments = []) ->
# Change objects have the following structure:
# {
# id: ... # Uniquely generated by us
@@ -48,8 +46,6 @@ define [
#
# Ids are used to uniquely identify a change, e.g. for updating it in the database, or keeping in
# sync with Ace ranges.
@changes = []
@comments = []
@id = 0
addComment: (offset, length, metadata) ->
@@ -375,7 +371,23 @@ define [
@emit "changes:moved", moved_changes
_newId: () ->
(@id++).toString()
# Generate a Mongo ObjectId
# Reference: https://github.com/dreampulse/ObjectId.js/blob/master/src/main/javascript/Objectid.js
@_pid ?= Math.floor(Math.random() * (32767))
@_machine ?= Math.floor(Math.random() * (16777216))
timestamp = Math.floor(new Date().valueOf() / 1000)
@_increment ?= 0
@_increment++
timestamp = timestamp.toString(16)
machine = @_machine.toString(16)
pid = @_pid.toString(16)
increment = @_increment.toString(16)
return '00000000'.substr(0, 8 - timestamp.length) + timestamp +
'000000'.substr(0, 6 - machine.length) + machine +
'0000'.substr(0, 4 - pid.length) + pid +
'000000'.substr(0, 6 - increment.length) + increment;
_addOp: (op, metadata) ->
change = {
@@ -453,3 +465,9 @@ define [
else # Only update to the current change if we haven't removed it.
previous_change = change
return { moved_changes, remove_changes }
if define?
define ["utils/EventEmitter"], load
else
EventEmitter = require("events").EventEmitter
module.exports = load(EventEmitter)

View File

@@ -2,8 +2,8 @@ define [
"base",
"utils/EventEmitter"
"ide/colors/ColorManager"
"ide/review-panel/ChangesTracker"
], (App, EventEmitter, ColorManager, ChangesTracker) ->
"ide/review-panel/RangesTracker"
], (App, EventEmitter, ColorManager, RangesTracker) ->
App.controller "ReviewPanelController", ($scope, $element, ide, $timeout) ->
$reviewPanelEl = $element.find "#review-panel"
@@ -13,7 +13,6 @@ define [
$scope.reviewPanel =
entries: {}
trackNewChanges: false
hasEntries: false
subView: $scope.SubViews.CUR_FILE
openSubView: $scope.SubViews.CUR_FILE
@@ -24,15 +23,15 @@ define [
$scope.reviewPanelEventsBridge = new EventEmitter()
changesTrackers = {}
rangesTrackers = {}
getDocEntries = (doc_id) ->
$scope.reviewPanel.entries[doc_id] ?= {}
return $scope.reviewPanel.entries[doc_id]
getChangeTracker = (doc_id) ->
changesTrackers[doc_id] ?= new ChangesTracker()
return changesTrackers[doc_id]
rangesTrackers[doc_id] ?= new RangesTracker()
return rangesTrackers[doc_id]
# TODO Just for prototyping purposes; remove afterwards.
mockedUserId = 'mock_user_id_1'
@@ -115,12 +114,12 @@ define [
ide.$scope.$on "file-tree:initialized", () ->
ide.fileTreeManager.forEachEntity (entity) ->
if mock_changes[entity.name]?
changesTracker = getChangeTracker(entity.id)
rangesTracker = getChangeTracker(entity.id)
for change in mock_changes[entity.name].changes
changesTracker._addOp change.op, change.metadata
rangesTracker._addOp change.op, change.metadata
for comment in mock_changes[entity.name].comments
changesTracker.addComment comment.offset, comment.length, comment.metadata
for doc_id, changesTracker of changesTrackers
rangesTracker.addComment comment.offset, comment.length, comment.metadata
for doc_id, rangesTracker of rangesTrackers
updateEntries(doc_id)
scrollbar = {}
@@ -150,8 +149,8 @@ define [
$scope.$watch "editor.open_doc_id", (open_doc_id) ->
return if !open_doc_id?
changesTrackers[open_doc_id] ?= new ChangesTracker()
$scope.reviewPanel.changesTracker = changesTrackers[open_doc_id]
rangesTrackers[open_doc_id] ?= new RangesTracker()
$scope.reviewPanel.rangesTracker = rangesTrackers[open_doc_id]
$scope.$watch (() ->
entries = $scope.reviewPanel.entries[$scope.editor.open_doc_id] or {}
@@ -166,14 +165,14 @@ define [
$scope.$broadcast "review-panel:layout"
updateEntries = (doc_id) ->
changesTracker = getChangeTracker(doc_id)
rangesTracker = getChangeTracker(doc_id)
entries = getDocEntries(doc_id)
# Assume we'll delete everything until we see it, then we'll remove it from this object
delete_changes = {}
delete_changes[change_id] = true for change_id, change of entries
for change in changesTracker.changes
for change in rangesTracker.changes
delete delete_changes[change.id]
entries[change.id] ?= {}
@@ -189,7 +188,7 @@ define [
for key, value of new_entry
entries[change.id][key] = value
for comment in changesTracker.comments
for comment in rangesTracker.comments
delete delete_changes[comment.id]
entries[comment.id] ?= {}
new_entry = {