From 9a2fd0e2b230b45697d59a2ac6aca2fe874d0d44 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Sat, 7 Oct 2017 00:18:20 +0100 Subject: [PATCH] Add initial version control sidebar with commit function --- Gruntfile.js | 1 + editor/js/keymap.json | 2 +- editor/js/ui/projects.js | 2 +- editor/js/ui/tab-versionControl.js | 436 ++++++++++++++++++ editor/sass/diff.scss | 1 + editor/sass/editor.scss | 2 +- editor/sass/mixins.scss | 47 +- editor/sass/palette.scss | 2 +- editor/sass/projects.scss | 102 +++- editor/sass/ui/common/stack.scss | 4 + package.json | 2 +- red/api/editor/projects/index.js | 74 ++- red/runtime/storage/localfilesystem/index.js | 2 +- .../storage/localfilesystem/library.js | 8 +- .../projects/defaultFileSet.js | 33 ++ .../localfilesystem/projects/git/index.js | 127 +++++ .../storage/localfilesystem/projects/index.js | 122 ++--- red/runtime/storage/localfilesystem/util.js | 1 - 18 files changed, 852 insertions(+), 116 deletions(-) create mode 100644 editor/js/ui/tab-versionControl.js create mode 100644 red/runtime/storage/localfilesystem/projects/defaultFileSet.js diff --git a/Gruntfile.js b/Gruntfile.js index 254585d7b..2cdac727c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -155,6 +155,7 @@ module.exports = function(grunt) { "editor/js/ui/userSettings.js", "editor/js/ui/projects.js", "editor/js/ui/projectSettings.js", + "editor/js/ui/tab-versionControl.js", "editor/js/ui/touch/radialMenu.js" ], dest: "public/red/red.js" diff --git a/editor/js/keymap.json b/editor/js/keymap.json index 5fa045ab6..4b7eb6b35 100644 --- a/editor/js/keymap.json +++ b/editor/js/keymap.json @@ -17,7 +17,7 @@ "ctrl-alt-n": "core:new-project", "ctrl-alt-o": "core:open-project", - "ctrl-g p": "core:show-projects-tab" + "ctrl-g v": "core:show-version-control-tab" }, "workspace": { "backspace": "core:delete-selection", diff --git a/editor/js/ui/projects.js b/editor/js/ui/projects.js index a78d094ec..4f5f1dd9f 100644 --- a/editor/js/ui/projects.js +++ b/editor/js/ui/projects.js @@ -482,7 +482,7 @@ RED.projects = (function() { RED.actions.add("core:open-project",RED.projects.selectProject); RED.projects.settings.init({sendRequest:sendRequest}); - + RED.sidebar.versionControl.init({sendRequest:sendRequest}); initScreens(); // initSidebar(); } diff --git a/editor/js/ui/tab-versionControl.js b/editor/js/ui/tab-versionControl.js new file mode 100644 index 000000000..4dc68ee3c --- /dev/null +++ b/editor/js/ui/tab-versionControl.js @@ -0,0 +1,436 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ +RED.sidebar.versionControl = (function() { + + var content; + var sections; + + var allChanges = {}; + + var unstagedChangesList; + var stageAllButton; + var stagedChangesList; + var unstageAllButton; + var unstagedChanges; + var stagedChanges; + var bulkChangeSpinner; + var commitButton; + + // TODO: DRY projectSummary.js + function addSpinnerOverlay(container) { + var spinner = $('
').appendTo(container); + return spinner; + } + function createChangeEntry(row, entry, status, unstaged) { + row.addClass("sidebar-version-control-change-entry"); + var container = $('
').appendTo(row); + var icon = $('').appendTo(container); + var label = $('').appendTo(container); + + var bg = $('
').appendTo(row); + $('').appendTo(bg); + $('') + .appendTo(bg) + .click(function(evt) { + evt.preventDefault(); + var activeProject = RED.projects.getActiveProject(); + entry.spinner = addSpinnerOverlay(row).addClass('projects-version-control-spinner-sidebar'); + utils.sendRequest({ + url: "projects/"+activeProject.name+"/stage/"+encodeURIComponent(entry.file), + type: unstaged?"POST":"DELETE", + responses: { + 0: function(error) { + console.log(error); + // done(error,null); + }, + 200: function(data) { + refreshFiles(data); + }, + 400: { + 'unexpected_error': function(error) { + console.log(error); + // done(error,null); + } + }, + } + },{}); + }); + entry["update"+(unstaged?"Unstaged":"Staged")] = function(entry,status) { + container.removeClass(); + var iconClass = ""; + if (status === 'A') { + container.addClass("node-diff-added"); + iconClass = "fa-plus-square"; + } else if (status === '?') { + container.addClass("node-diff-unchanged"); + iconClass = "fa-question-circle-o"; + } else if (status === 'D') { + container.addClass("node-diff-deleted"); + iconClass = "fa-minus-square"; + } else if (status === 'M') { + container.addClass("node-diff-changed"); + iconClass = "fa-square"; + } else if (status === 'R') { + container.addClass("node-diff-changed"); + iconClass = "fa-toggle-right"; + } else { + iconClass = "fa-exclamation-triangle" + } + label.empty(); + $('').text(entry.file.replace(/\\(.)/g,"$1")).appendTo(label); + + if (entry.oldName) { + $('').prependTo(label); + $('').text(entry.oldName.replace(/\\(.)/g,"$1")).prependTo(label); + // label.text(entry.oldName+" -> "+entry.file); + } + // console.log(entry.file,status,iconClass); + + icon.removeClass(); + icon.addClass("fa "+iconClass); + if (entry.spinner) { + entry.spinner.remove(); + delete entry.spinner; + } + } + entry["update"+(unstaged?"Unstaged":"Staged")](entry, status); + } + var utils; + function init(_utils) { + utils = _utils; + + RED.actions.add("core:show-version-control-tab",show); + + content = $('
', {class:"sidebar-version-control"}); + var stackContainer = $("
",{class:"sidebar-version-control-stack"}).appendTo(content); + sections = RED.stack.create({ + container: stackContainer, + fill: true, + singleExpanded: true + }); + + var localChanges = sections.add({ + title: "Local Changes", + collapsible: true + }); + localChanges.expand(); + localChanges.content.css({height:"100%"}); + + var bg = $('
').appendTo(localChanges.header); + $('') + .appendTo(bg) + .click(function(evt) { + evt.preventDefault(); + refresh(true); + }) + + + var unstagedContent = $('').appendTo(localChanges.content); + var header = $('').appendTo(unstagedContent); + stageAllButton = $('') + .appendTo(header) + .click(function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + var toStage = Object.keys(allChanges).filter(function(fn) { + return allChanges[fn].treeStatus !== ' '; + }); + updateBulk(toStage,true); + }); + unstagedChangesList = $("
    ",{style:"position: absolute; top: 30px; bottom: 0; right:0; left:0;"}).appendTo(unstagedContent); + unstagedChangesList.editableList({ + addButton: false, + scrollOnAdd: false, + addItem: function(row,index,entry) { + createChangeEntry(row,entry,entry.treeStatus,true); + }, + sort: function(A,B) { + if (A.treeStatus === '?' && B.treeStatus !== '?') { + return 1; + } else if (A.treeStatus !== '?' && B.treeStatus === '?') { + return -1; + } + return A.file.localeCompare(B.file); + } + + }) + + var stagedContent = $('').appendTo(localChanges.content); + + var header = $('').appendTo(stagedContent); + + bg = $('
    ').appendTo(header); + commitButton = $('') + .appendTo(bg) + .click(function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + commitMessage.val(""); + submitCommitButton.attr("disabled",true); + unstagedContent.css("height","30px"); + stagedContent.css("height","calc(100% - 30px - 175px)"); + commitBox.css("height","175px"); + stageAllButton.attr("disabled",true); + unstageAllButton.attr("disabled",true); + commitButton.attr("disabled",true); + commitMessage.focus(); + }); + unstageAllButton = $('') + .appendTo(bg) + .click(function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + var toUnstage = Object.keys(allChanges).filter(function(fn) { + return allChanges[fn].indexStatus !== ' ' && allChanges[fn].indexStatus !== '?'; + }); + updateBulk(toUnstage,false); + + }); + + + stagedChangesList = $("
      ",{style:"position: absolute; top: 30px; bottom: 0; right:0; left:0;"}).appendTo(stagedContent); + stagedChangesList.editableList({ + addButton: false, + scrollOnAdd: false, + addItem: function(row,index,entry) { + createChangeEntry(row,entry,entry.indexStatus,false); + }, + sort: function(A,B) { + return A.file.localeCompare(B.file); + } + }) + + commitBox = $('').appendTo(localChanges.content); + + var commitMessage = $('