diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index a4f0837bc1..958f3d19d9 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -124,7 +124,7 @@ module.exports = class Router app.get '/Project/:Project_id/version', SecutiryManager.requestCanAccessProject, versioningController.listVersions app.get '/Project/:Project_id/version/:Version_id', SecutiryManager.requestCanAccessProject, versioningController.getVersion - app.get "/project/:Project_id/doc/:doc_id/updates", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi + app.get "/project/:Project_id/updates", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi app.get "/project/:Project_id/doc/:doc_id/diff", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi diff --git a/services/web/app/views/templates.jade b/services/web/app/views/templates.jade index 4ab4640e1f..1971a6c8c2 100644 --- a/services/web/app/views/templates.jade +++ b/services/web/app/views/templates.jade @@ -443,7 +443,8 @@ input(type="radio",name="toVersion").change-selector-to div(class='change-description') - div(class='change-date') {{date}} + div.changed-docs {{docs}} + div.change-date {{date}} div {{{users}}} div(class='restore') diff --git a/services/web/public/coffee/editor/Editor.coffee b/services/web/public/coffee/editor/Editor.coffee index c7eb19d9d8..f05f7ed222 100644 --- a/services/web/public/coffee/editor/Editor.coffee +++ b/services/web/public/coffee/editor/Editor.coffee @@ -35,6 +35,8 @@ define [ identifier: "editor" element: @editorPanel @initializeEditor() + @bindToFileTreeEvents() + @enable() @loadingIndicator = $(@templates.loadingIndicator) @editorPanel.find("#editor").append(@loadingIndicator) @leftPanel = @editorPanel.find("#leftEditorPanel") @@ -42,6 +44,11 @@ define [ @initSplitView() @switchToFlatView() + bindToFileTreeEvents: () -> + @ide.fileTreeManager.on "open:doc", (doc_id, options = {}) => + if @enabled + @openDoc doc_id, options + initSplitView: () -> splitter = @editorPanel.find("#editorSplitter") options = @@ -352,9 +359,8 @@ define [ getCurrentDocId: () -> @current_doc_id - show: () -> - $("#editor").show() - - hide: () -> - $("#editor").hide() + enable: () -> + @enabled = true + disable: () -> + @enabled = false diff --git a/services/web/public/coffee/file-tree/FileTreeManager.coffee b/services/web/public/coffee/file-tree/FileTreeManager.coffee index 8ffd20ac79..f78629f388 100644 --- a/services/web/public/coffee/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/file-tree/FileTreeManager.coffee @@ -76,7 +76,7 @@ define [ openDoc: (doc, line) -> doc_id = doc.id or doc - @ide.editor.openDoc(doc_id, line: line) + @trigger "open:doc", doc_id, line: line @selectEntity(doc_id) $.localStorage "doc.open_id.#{@project_id}", doc_id @@ -86,8 +86,7 @@ define [ @openDoc(doc_id, line) openFile: (file) -> - @ide.mainAreaManager.change('file') - @ide.fileViewManager.showFile(file) + @trigger "open:file", file @selectEntity(file.id) openFolder: (folder) -> diff --git a/services/web/public/coffee/file-view/FileViewManager.coffee b/services/web/public/coffee/file-view/FileViewManager.coffee index 35af265735..4f414493b9 100644 --- a/services/web/public/coffee/file-view/FileViewManager.coffee +++ b/services/web/public/coffee/file-view/FileViewManager.coffee @@ -13,5 +13,20 @@ define [ @ide.layoutManager.on "resize", () => @view.onResize() @view.onResize() + @bindToFileTreeEvents() + @enable() + + bindToFileTreeEvents: () -> + @ide.fileTreeManager.on "open:file", (file) => + if @enabled + @showFile(file) + showFile: (file) -> + @ide.mainAreaManager.change('file') @view.setModel(file) + + enable: () -> + @enabled = true + + disable: () -> + @enabled = false diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 5b6eb591a7..a844fb2a5b 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -106,12 +106,12 @@ define [ @sideBarView = new SideBarManager(@, $("#sections")) selectElement = @sideBarView.selectElement mainAreaManager = @mainAreaManager = new MainAreaManager(@, $("#content")) + @fileTreeManager = new FileTreeManager(@) @editor = new Editor(@) @pdfManager = new PdfManager(@) if @userSettings.autoComplete @autoCompleteManager = new AutoCompleteManager(@) @spellingManager = new SpellingManager(@) - @fileTreeManager = new FileTreeManager(@) @fileUploadManager = new FileUploadManager(@) @searchManager = new SearchManager(@) @cursorManager = new CursorManager(@) @@ -119,6 +119,8 @@ define [ @analyticsManager = new AnalyticsManager(@) if @userSettings.trackChanges @trackChangesManager = new TrackChangesManager(@) + else + @historyManager = new HistoryManager(@) @setLoadingMessage("Connecting") firstConnect = true @@ -186,7 +188,6 @@ define [ _.extend(Ide::, Backbone.Events) window.ide = ide = new Ide() - ide.historyManager = new HistoryManager ide ide.projectMembersManager = new ProjectMembersManager ide ide.settingsManager = new SettingsManager ide ide.helpManager = new HelpManager ide diff --git a/services/web/public/coffee/ide/TabManager.coffee b/services/web/public/coffee/ide/TabManager.coffee index a5b3748a5e..bd82376a38 100644 --- a/services/web/public/coffee/ide/TabManager.coffee +++ b/services/web/public/coffee/ide/TabManager.coffee @@ -12,26 +12,39 @@ define [ @state = "closed" $("#toolbar").on "mouseenter", () => @onMouseOver() $("#toolbar").on "mouseleave", (e) => @onMouseOut(e) + @tabs = [] addTab: (options) -> + @tabs.push options + tabEl = $(Mustache.to_html @templates.tab, options) tabEl.find("a").attr("href", "#" + options.id) - contentEl = $(Mustache.to_html @templates.content, options) - contentEl.append(options.content) + + if options.content? + contentEl = $(Mustache.to_html @templates.content, options) + contentEl.append(options.content) + $("#tab-content").append(contentEl) if options.active tabEl.addClass("active") - contentEl.addClass("active") + contentEl?.addClass("active") if options.after? tabEl.insertAfter($("##{options.after}-tab-li")) else $("#tabs").append(tabEl) - $("#tab-content").append(contentEl) + $("body").scrollTop(0) tabEl.on "shown", () => $("body").scrollTop(0) + options.onShown() if options.onShown? + for other_tab in @tabs + if other_tab.id != options.id and other_tab.active and other_tab.onHidden? + other_tab.onHidden() + other_tab.active = false + options.active = true + if options.lock @lockOpen() else @@ -40,6 +53,7 @@ define [ if options.contract @contract() + lockOpen: () -> @locked_open = true $("#toolbar").css({ diff --git a/services/web/public/coffee/track-changes/ChangeListView.coffee b/services/web/public/coffee/track-changes/ChangeListView.coffee index 11487a2e83..348b5fbe9b 100644 --- a/services/web/public/coffee/track-changes/ChangeListView.coffee +++ b/services/web/public/coffee/track-changes/ChangeListView.coffee @@ -112,7 +112,7 @@ define [ view.setHoverUnselected() triggerChangeDiff: () -> - @trigger "change_diff", @collection.models[@selectedFromIndex], @collection.models[@selectedToIndex] + @trigger "change_diff", @selectedFromIndex, @selectedToIndex listShorterThanContainer: -> @$el.height() > @$(".change-list").height() @@ -131,12 +131,12 @@ define [ callback(error) success: (collection, response) => @hideLoading() - if response.updates.length == @collection.batchSize - @loadUntilFull(callback) - else + if @collection.isAtEnd() @atEndOfCollection = true @showEmptyMessageIfCollectionEmpty() callback() + else + @loadUntilFull(callback) else callback() if callback? @@ -189,9 +189,16 @@ define [ hue: user.hue() name: user.name() } + docNames = [] + for doc in @model.get("docs") + if doc.entity? + docNames.push doc.entity.get("name") + else + docNames.push "deleted" data = { date: moment(parseInt(@model.get("end_ts"), 10)).calendar() users: userHtml.join("") + docs: docNames.join(", ") } @$el.html Mustache.to_html(@templates.item, data) @@ -236,20 +243,20 @@ define [ @$(".change-selector-to").prop("checked", checked) setSelected: (first, last) -> - @$el.addClass("selected") + @$el.addClass("selected-change") if first - @$el.addClass("selected-to") + @$el.addClass("selected-change-to") else - @$el.removeClass("selected-to") + @$el.removeClass("selected-change-to") if last - @$el.addClass("selected-from") + @$el.addClass("selected-change-from") else - @$el.removeClass("selected-from") + @$el.removeClass("selected-change-from") setUnselected: () -> - @$el.removeClass("selected-to") - @$el.removeClass("selected-from") - @$el.removeClass("selected") + @$el.removeClass("selected-change-to") + @$el.removeClass("selected-change-from") + @$el.removeClass("selected-change") setHoverSelected: (first, last) -> @$el.addClass("hover-selected") diff --git a/services/web/public/coffee/track-changes/TrackChangesManager.coffee b/services/web/public/coffee/track-changes/TrackChangesManager.coffee index 24079e6832..52a4f2a4fa 100644 --- a/services/web/public/coffee/track-changes/TrackChangesManager.coffee +++ b/services/web/public/coffee/track-changes/TrackChangesManager.coffee @@ -13,11 +13,17 @@ define [ constructor: (@ide) -> @project_id = window.userSettings.project_id @$el = $(@template) - $("#editorWrapper").append(@$el) - @hideEl() + @ide.mainAreaManager.addArea + identifier: "trackChanges" + element: @$el - @ide.editor.on "change:doc", () => - @hideEl() + @ide.tabManager.addTab + id: "history" + name: "History" + after: "code" + contract: true + onShown: () => @show() + onHidden: () => @hide() @ide.editor.on "resize", () => @diffView?.resize() @@ -26,20 +32,18 @@ define [ e.preventDefault @hide() - @ide.fileTreeManager.on "contextmenu:beforeshow", (entity, entries) => - if entity instanceof Doc - entries.push { - divider: true - }, { - text: "History" - onClick: () => - @show(entity.id) - } + @bindToFileTreeEvents() - show: (@doc_id) -> - @ide.fileTreeManager.selectEntity(@doc_id) + @disable() - @changes = new ChangeList([], doc_id: @doc_id, project_id: @project_id) + bindToFileTreeEvents: () -> + @ide.fileTreeManager.on "open:doc", (doc_id) => + if @enabled + @doc_id = doc_id + @updateDiff() + + show: () -> + @changes = new ChangeList([], project_id: @project_id, ide: @ide) @changeListView = new ChangeListView( collection : @changes, @@ -49,8 +53,8 @@ define [ @changeListView.loadUntilFull (error) => @autoSelectDiff() - @changeListView.on "change_diff", (fromModel, toModel) => - @showDiff(fromModel, toModel) + @changeListView.on "change_diff", (fromIndex, toIndex) => + @selectDocAndUpdateDiff(fromIndex, toIndex) @changeListView.on "restore", (change) => @restore(change) @@ -58,11 +62,17 @@ define [ if @diffView? @diffView.destroy() - @showEl() + @ide.mainAreaManager.change "trackChanges" + @ide.editor.disable() + @ide.fileViewManager.disable() + @enable() hide: () -> - @hideEl() + @ide.editor.enable() + @ide.fileViewManager.enable() + @disable() @ide.fileTreeManager.openDoc(@doc_id) + @ide.mainAreaManager.change "editor" autoSelectDiff: () -> if @changes.models.length == 0 @@ -81,15 +91,39 @@ define [ toChange = @changes.models[0] fromChange = @changes.models[fromIndex] - @showDiff(fromChange, toChange) @changeListView.setSelectionRange(fromIndex, 0) + @updateDiff() + + selectDocAndUpdateDiff: (fromIndex, toIndex) -> + doc_ids = [] + for change in @changes.models.slice(toIndex, fromIndex + 1) + for doc in change.get("docs") or [] + doc_ids.push doc.id if doc.id not in doc_ids + + if !@doc_id? or @doc_id not in doc_ids + @doc_id = doc_ids[0] + + @updateDiff() + + updateDiff: () -> + fromIndex = @changeListView.selectedFromIndex + toIndex = @changeListView.selectedToIndex + + if !toIndex? or !fromIndex? + console.log "No selection - what should we do!?" + return + + {from, to} = @_findDocVersionsRangeInSelection(@doc_id, fromIndex, toIndex) + + if !from? or !to? + console.log "No diff, should probably just show latest version" + return - showDiff: (fromModel, toModel) -> @diff = new Diff({ project_id: @project_id doc_id: @doc_id - from: fromModel.get("fromVersion") - to: toModel.get("toVersion") + from: from + to: to }) if @diffView? @@ -100,13 +134,25 @@ define [ ) @diff.fetch() - showEl: -> - @ide.editor.hide() - @$el.show() + @ide.fileTreeManager.selectEntity(@doc_id) - hideEl: () -> - @ide.editor.show() - @$el.hide() + + _findDocVersionsRangeInSelection: (doc_id, fromIndex, toIndex) -> + from = null + to = null + + for change in @changes.models.slice(toIndex, fromIndex + 1) + for doc in change.get("docs") + if doc.id == doc_id + if from? and to? + from = Math.min(from, doc.fromV) + to = Math.max(to, doc.toV) + else + from = doc.fromV + to = doc.toV + break + + return {from, to} restore: (change) -> name = @ide.fileTreeManager.getNameOfEntityId(@doc_id) @@ -141,4 +187,10 @@ define [ callback(error) } + enable: () -> + @enabled = true + + disable: () -> + @enabled = false + return TrackChangesManager diff --git a/services/web/public/coffee/track-changes/models/Change.coffee b/services/web/public/coffee/track-changes/models/Change.coffee index bd1913e67f..7e2d10315f 100644 --- a/services/web/public/coffee/track-changes/models/Change.coffee +++ b/services/web/public/coffee/track-changes/models/Change.coffee @@ -7,12 +7,20 @@ define [ model = { start_ts: change.meta.start_ts end_ts: change.meta.end_ts - fromVersion: change.fromV - toVersion: change.toV } model.users = [] for user in change.meta.users or [] model.users.push User.findOrBuild(user.id, user) if model.users.length == 0 model.users.push User.getAnonymousUser() + model.docs = [] + for doc_id, data of change.docs + model.docs.push + id: doc_id + fromV: data.fromV + toV: data.toV + # TODO: We should not use a global reference here, but + # it's hard to get @ide into Backbone at this point. + entity: ide.fileTreeManager.getEntity(doc_id) + return model \ No newline at end of file diff --git a/services/web/public/coffee/track-changes/models/ChangeList.coffee b/services/web/public/coffee/track-changes/models/ChangeList.coffee index 17902223d2..82fd84fef7 100644 --- a/services/web/public/coffee/track-changes/models/ChangeList.coffee +++ b/services/web/public/coffee/track-changes/models/ChangeList.coffee @@ -4,21 +4,29 @@ define [ ], (Change)-> ChangeList = Backbone.Collection.extend model: Change - batchSize: 25 + batchSize: 10 initialize: (models, @options) -> + @ide = @options.ide + @atEnd = false url: () -> - url = "/project/#{@options.project_id}/doc/#{@options.doc_id}/updates?limit=#{@batchSize}" - if @models.length > 0 - last = @models[@models.length - 1] - url += "&to=#{last.get("fromVersion") - 1}" + url = "/project/#{@options.project_id}/updates?min_count=#{@batchSize}" + if @nextBeforeTimestamp? + url += "&before=#{@nextBeforeTimestamp}" return url + isAtEnd: () -> @atEnd + parse: (json) -> + @nextBeforeTimestamp = json.nextBeforeTimestamp + @atEnd = !@nextBeforeTimestamp return json.updates fetchNextBatch: (options = {}) -> + if @isAtEnd() + options.success?(@) + return options.add = true @fetch options diff --git a/services/web/public/stylesheets/less/trackchanges.less b/services/web/public/stylesheets/less/trackchanges.less index 4c1afa70a5..a7d0455f2c 100644 --- a/services/web/public/stylesheets/less/trackchanges.less +++ b/services/web/public/stylesheets/less/trackchanges.less @@ -126,10 +126,14 @@ min-height: 38px; } .change-name { - font-size: 0.9em; + font-size: 11px; color: #666; text-transform: capitalize; } + .change-date { + margin-top: 2px; + font-size: 12px; + } .color-square { display: inline-block; height: 10px; @@ -137,6 +141,9 @@ margin-right: 4px; margin-bottom: -1px; } + .changed-docs { + font-weight: bold; + } .restore { a { display: block; @@ -153,7 +160,7 @@ li.loading-changes, li.empty-message { padding: 6px; } - li.selected { + li.selected-change { background-color: #eaeaea; .change-selectors { .range { @@ -161,7 +168,7 @@ } } } - li.selected-to { + li.selected-change-to { .change-selectors { .range { top: 10px; @@ -171,7 +178,7 @@ } } } - li.selected-from { + li.selected-change-from { .change-selectors { .range { bottom: 37px; @@ -223,7 +230,7 @@ } } } - li.selected-from.hover-selected-from { + li.selected-change-from.hover-selected-from { .change-selectors { .range { bottom: 37px;