From 8b0702fab78d1c00fab6922e509ec75c5d7c4038 Mon Sep 17 00:00:00 2001 From: serapath Date: Fri, 12 Jan 2018 06:42:59 +0800 Subject: [PATCH] add support for large file hierarchies to file explorer --- src/app.js | 2 +- src/app/files/basicReadOnlyExplorer.js | 26 +++++----- src/app/files/browser-files.js | 34 ++++++------- src/app/files/file-explorer.js | 51 +++++++++++++++----- src/app/files/fileManager.js | 2 +- src/app/files/shared-folder.js | 67 +++++--------------------- 6 files changed, 80 insertions(+), 102 deletions(-) diff --git a/src/app.js b/src/app.js index 0659785e1b..f022d895dd 100644 --- a/src/app.js +++ b/src/app.js @@ -454,7 +454,7 @@ function run () { // insert ballot contract if there are no files available if (!loadingFromGist) { - filesProviders['browser'].resolveDirectory('', (error, filesList) => { + filesProviders['browser'].resolveDirectory('/', (error, filesList) => { if (error) console.error(error) if (Object.keys(filesList).length === 0) { if (!filesProviders['browser'].set(examples.ballot.name, examples.ballot.content)) { diff --git a/src/app/files/basicReadOnlyExplorer.js b/src/app/files/basicReadOnlyExplorer.js index cbf54a2255..d5a92bdae8 100644 --- a/src/app/files/basicReadOnlyExplorer.js +++ b/src/app/files/basicReadOnlyExplorer.js @@ -81,7 +81,18 @@ class BasicReadOnlyExplorer { // } // resolveDirectory (path, callback /* (error, filesList) => { } */) { - // path = '' + (path || '') + var self = this + if (path[0] === '/') path = path.substring(1) + var tree = {} + // This does not include '.remix.config', because it is filtered + // inside list(). + Object.keys(this.list()).forEach(function (path) { + hashmapize(tree, path, { + '/readonly': self.isReadOnly(path), + '/content': self.get(path) + }) + }) + return callback(null, tree[path] || {}) function hashmapize (obj, path, val) { var nodes = path.split('/') var i = 0 @@ -96,19 +107,6 @@ class BasicReadOnlyExplorer { obj[nodes[i]] = val } - - var tree = {} - - var self = this - // This does not include '.remix.config', because it is filtered - // inside list(). - Object.keys(this.list()).forEach(function (path) { - hashmapize(tree, path, { - '/readonly': self.isReadOnly(path), - '/content': self.get(path) - }) - }) - callback(null, tree) } removePrefix (path) { diff --git a/src/app/files/browser-files.js b/src/app/files/browser-files.js index ecc42a083b..ac0504e4aa 100644 --- a/src/app/files/browser-files.js +++ b/src/app/files/browser-files.js @@ -117,20 +117,10 @@ function Files (storage) { // this.resolveDirectory = function (path, callback) { var self = this - // path = '' + (path || '') - function hashmapize (obj, path, val) { - var nodes = path.split('/') - var i = 0 - for (; i < nodes.length - 1; i++) { - var node = nodes[i] - if (obj[node] === undefined) { - obj[node] = {} - } - obj = obj[node] - } - obj[nodes[i]] = val - } + if (path[0] === '/') path = path.substring(1) + if (!path) return callback(null, { [self.type]: { } }) var filesList = {} + var tree = {} // add r/w filesList to the list storage.keys().forEach((path) => { // NOTE: as a temporary measure do not show the config file @@ -142,16 +132,26 @@ function Files (storage) { Object.keys(readonly).forEach((path) => { filesList[self.type + '/' + path] = true }) - var tree = {} - // This does not include '.remix.config', because it is filtered - // inside list(). + Object.keys(filesList).forEach(function (path) { hashmapize(tree, path, { '/readonly': self.isReadOnly(path), '/content': self.get(path) }) }) - callback(null, tree) + return callback(null, tree[path] || {}) + function hashmapize (obj, path, val) { + var nodes = path.split('/') + var i = 0 + for (; i < nodes.length - 1; i++) { + var node = nodes[i] + if (obj[node] === undefined) { + obj[node] = {} + } + obj = obj[node] + } + obj[nodes[i]] = val + } } this.removePrefix = function (path) { diff --git a/src/app/files/file-explorer.js b/src/app/files/file-explorer.js index 2e2673895e..48c341995e 100755 --- a/src/app/files/file-explorer.js +++ b/src/app/files/file-explorer.js @@ -79,8 +79,9 @@ function fileExplorer (appAPI, files) { }) var fileEvents = files.event - var treeView = new Treeview({ - extractData: function (value, tree, key) { + + self.treeView = new Treeview({ + extractData: function extractData (value, tree, key) { var newValue = {} // var isReadOnly = false var isFile = false @@ -99,8 +100,9 @@ function fileExplorer (appAPI, files) { })) : undefined } }, - formatSelf: function (key, data, li) { - var isRoot = data.path.indexOf('/') === -1 + formatSelf: function formatSelf (key, data, li) { + var isRoot = !data.path.indexOf('browser') && data.path.length === 'browser'.length + isRoot = isRoot || !data.path.indexOf('localhost') && data.path.length === 'localhost'.length return yo`` + >${key.split('/').pop()}` + } + }) + + self.treeView.event.register('nodeClick', function (path, childrenContainer) { + if (!childrenContainer) return + if (childrenContainer.style.display === 'none') { + childrenContainer.innerHTML = '' + return } + files.resolveDirectory(path, (error, fileTree) => { + if (error) console.error(error) + if (!fileTree) return + var newTree = normalize(path, fileTree) + var tree = self.treeView.renderProperties(newTree, false) + childrenContainer.appendChild(tree) + }) }) - this.treeView = treeView + function normalize (path, filesList) { + var prefix = path.split('/')[0] + var newList = {} + Object.keys(filesList).forEach(key => { + newList[prefix + '/' + key] = filesList[key].isDirectory ? {} : { '/content': true } + }) + return newList + } var deleteButton = yo` @@ -138,10 +162,9 @@ function fileExplorer (appAPI, files) { var textUnderEdit = null var textInRename = false - var events = new EventManager() - this.events = events - var api = {} - api.addFile = function addFile (file) { + self.events = new EventManager() + self.api = {} + self.api.addFile = function addFile (file) { function loadFile () { var fileReader = new FileReader() fileReader.onload = function (event) { @@ -151,7 +174,7 @@ function fileExplorer (appAPI, files) { } var success = files.set(name, event.target.result) if (!success) modalDialogCustom.alert('Failed to create file ' + name) - else events.trigger('focus', [name]) + else self.events.trigger('focus', [name]) } fileReader.readAsText(file) } @@ -163,7 +186,6 @@ function fileExplorer (appAPI, files) { modalDialogCustom.confirm(null, `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() }) } } - this.api = api function focus (event) { event.cancelBubble = true @@ -175,7 +197,7 @@ function fileExplorer (appAPI, files) { var label = getLabelFrom(li) var filepath = label.dataset.path var isFile = label.className.indexOf('file') === 0 - if (isFile) events.trigger('focus', [filepath]) + if (isFile) self.events.trigger('focus', [filepath]) } function unfocus (el) { @@ -320,11 +342,13 @@ function fileExplorer (appAPI, files) { } function fileRemoved (filepath) { + // @TODO: only important if currently visible in TreeView var li = getElement(filepath) if (li) li.parentElement.removeChild(li) } function fileRenamed (oldName, newName, isFolder) { + // @TODO: only important if currently visible in TreeView var li = getElement(oldName) if (li) { oldName = oldName.split('/') @@ -346,6 +370,7 @@ function fileExplorer (appAPI, files) { } function fileAdded (filepath) { + // @TODO: only important if currently visible in TreeView self.files.resolveDirectory('./', (error, files) => { if (error) console.error(error) var element = self.treeView.render(files) diff --git a/src/app/files/fileManager.js b/src/app/files/fileManager.js index 29fb53f9ce..9a5875b650 100644 --- a/src/app/files/fileManager.js +++ b/src/app/files/fileManager.js @@ -122,7 +122,7 @@ class FileManager { switchFile (file) { var self = this if (!file) { - self.opt.filesProviders['browser'].resolveDirectory('', (error, filesTree) => { + self.opt.filesProviders['browser'].resolveDirectory('/', (error, filesTree) => { if (error) console.error(error) var fileList = Object.keys(flatten(filesTree)) if (fileList.length) { diff --git a/src/app/files/shared-folder.js b/src/app/files/shared-folder.js index 04bfc2f95e..6b82ffa349 100644 --- a/src/app/files/shared-folder.js +++ b/src/app/files/shared-folder.js @@ -2,52 +2,6 @@ var EventManager = require('remix-lib').EventManager var pathtool = require('path') -function buildList (self, path = '', callback) { - path = '' + (path || '') - self.remixd.dir(path, (error, filesList) => { - if (error) console.error(error) - var list = Object.keys(filesList) - var counter = list.length - var fileTree = {} - if (!counter) callback(null, fileTree) - for (var i = 0, name, len = counter; i < len; i++) { - name = list[i] - self.files[path] = path - if (filesList[name].isDirectory) { - setFolder(self, path, name, fileTree, finish) - } else { - setFileContent(self, path, name, fileTree, finish) - } - } - function finish (error) { - if (error) console.error(error) - counter-- - if (!counter) callback(null, fileTree) - } - }) -} -function setFolder (self, path, name, fileTree, done) { - buildList(self, name, (error, subFileTree) => { - if (error) console.error(error) - name = name.replace(path, '') - if (name[0] === '/') name = name.substring(1) - fileTree[name] = subFileTree - done(null) - }) -} -function setFileContent (self, path, name, fileTree, done) { - self.remixd.read(name, (error, result) => { - if (error) console.error(error) - name = name.replace(path, '') - if (name[0] === '/') name = name.substring(1) - fileTree[name] = { - '/content': result.content, - '/readonly': result.readonly - } - done(null) - }) -} - module.exports = class SharedFolder { constructor (remixd) { this.event = new EventManager() @@ -195,18 +149,19 @@ module.exports = class SharedFolder { // } // } // - resolveDirectory (path, callback) { - var self = this - path = '' + (path || '') - path = pathtool.join('./', path) - buildList(self, path, (error, fileTree) => { - if (error) return callback(error) - callback(null, { [self.type]: fileTree }) - }) - } removePrefix (path) { - return path.indexOf(this.type + '/') === 0 ? path.replace(this.type + '/', '') : path + path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path + if (path[0] === '/') return path.substring(1) + return path + } + + resolveDirectory (path, callback) { + var self = this + if (path[0] === '/') path = path.substring(1) + if (!path) return callback(null, { [self.type]: { } }) + path = self.removePrefix('' + (path || '')) + self.remixd.dir(path, callback) } }