Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FE refactoring: added Discard changes for external folder. removed readonly. #2374

Merged
merged 9 commits into from
Sep 30, 2019
Merged
10 changes: 9 additions & 1 deletion src/app/compiler/compiler-imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,18 @@ module.exports = class CompilerImports extends Plugin {
})
}

import (url, loadingCb, cb) {
import (url, force, loadingCb, cb) {
if (typeof force !== 'boolean') {
let temp = loadingCb
loadingCb = force
cb = temp
force = false
}
if (!loadingCb) loadingCb = () => {}
if (!cb) cb = () => {}

var self = this
if (force) delete this.previouslyHandled[url]
var imported = this.previouslyHandled[url]
if (imported) {
return cb(null, imported.content, imported.cleanUrl, imported.type, url)
Expand Down
97 changes: 57 additions & 40 deletions src/app/files/file-explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,51 +190,77 @@ function fileExplorer (localRegistry, files, menuItems) {
}
})

/**
* Extracts first two folders as a subpath from the path.
**/
function extractExternalFolder (path) {
const firstSlIndex = path.indexOf('/', 1)
if (firstSlIndex === -1) return ''
const secondSlIndex = path.indexOf('/', firstSlIndex + 1)
if (secondSlIndex === -1) return ''
return path.substring(0, secondSlIndex)
}

self.treeView.event.register('nodeRightClick', function (key, data, label, event) {
if (self.files.readonly) return
if (key === self.files.type) return
MENU_HANDLE && MENU_HANDLE.hide(null, true)
MENU_HANDLE = contextMenu(event, {
'Rename': () => {
if (self.files.readonly) { return tooltip('cannot rename folder. ' + self.files.type + ' is a read only explorer') }
let actions = {}
const provider = self._deps.fileManager.fileProviderOf(key)
if (provider.isExternalFolder(key)) {
actions['Discard changes'] = () => {
modalDialogCustom.confirm(
'Discard changes',
'Are you sure you want to discard all your changes?',
() => { files.discardChanges(key) },
() => {}
)
}
} else {
const folderPath = extractExternalFolder(key)
if (folderPath === 'browser/gists') {
actions['Push changes to gist'] = () => {
const id = key.substr(key.lastIndexOf('/') + 1, key.length - 1)
modalDialogCustom.confirm(
'Push back to Gist',
'Are you sure you want to push all your changes back to Gist?',
() => { self.toGist(id) },
() => {}
)
}
}
actions['Rename'] = () => {
if (self.files.isReadOnly(key)) { return tooltip('cannot rename folder. ' + self.files.type + ' is a read only explorer') }
var name = label.querySelector('span[data-path="' + key + '"]')
if (name) editModeOn(name)
},
'Delete': () => {
if (self.files.readonly) { return tooltip('cannot delete folder. ' + self.files.type + ' is a read only explorer') }
modalDialogCustom.confirm('Confirm to delete a folder', 'Are you sure you want to delete this folder?', () => { files.remove(key) }, () => {})
}
})
}
actions['Delete'] = () => {
if (self.files.isReadOnly(key)) { return tooltip('cannot delete folder. ' + self.files.type + ' is a read only explorer') }
modalDialogCustom.confirm('Confirm to delete a folder', 'Are you sure you want to delete this folder?', () => { files.remove(key) }, () => {})
}
MENU_HANDLE = contextMenu(event, actions)
})

self.treeView.event.register('leafRightClick', function (key, data, label, event) {
if (key === self.files.type) return
MENU_HANDLE && MENU_HANDLE.hide(null, true)
let actions = {}
const provider = self._deps.fileManager.fileProviderOf(key)
if (!provider.isReadOnly(key)) {
if (!provider.isExternalFolder(key)) {
actions['Rename'] = () => {
if (self.files.readonly) { return tooltip('cannot rename file. ' + self.files.type + ' is a read only explorer') }
if (self.files.isReadOnly(key)) { return tooltip('cannot rename file. ' + self.files.type + ' is a read only explorer') }
var name = label.querySelector('span[data-path="' + key + '"]')
if (name) editModeOn(name)
}
actions['Delete'] = () => {
if (self.files.readonly) { return tooltip('cannot delete file. ' + self.files.type + ' is a read only explorer') }
if (self.files.isReadOnly(key)) { return tooltip('cannot delete file. ' + self.files.type + ' is a read only explorer') }
modalDialogCustom.confirm(
'Delete a file', 'Are you sure you want to delete this file?',
() => { files.remove(key) },
() => {}
)
}
} else {
actions['Delete from remix'] = () => {
modalDialogCustom.confirm(
'Delete from remix',
'Are you sure you want to delete this file from remix?',
() => { files.remove(key) },
() => {}
)
}
}
MENU_HANDLE = contextMenu(event, actions)
})
Expand Down Expand Up @@ -403,6 +429,7 @@ fileExplorer.prototype.toGist = function (id) {
let proccedResult = function (error, data) {
if (error) {
modalDialogCustom.alert('Failed to manage gist: ' + error)
console.log('Failed to manage gist: ' + error)
} else {
if (data.html_url) {
modalDialogCustom.confirm('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
Expand All @@ -414,7 +441,7 @@ fileExplorer.prototype.toGist = function (id) {
}
}

this.packageFiles(this.files, (error, packaged) => {
this.packageFiles(this.files, 'browser/gists/' + id, (error, packaged) => {
if (error) {
console.log(error)
modalDialogCustom.alert('Failed to create gist: ' + error)
Expand Down Expand Up @@ -445,7 +472,9 @@ fileExplorer.prototype.toGist = function (id) {
}), this.files.origGistFiles)
// adding new files
updatedFileList.forEach((file) => {
allItems[file] = packaged[file]
const _items = file.split('/')
const _fileName = _items[_items.length - 1]
allItems[_fileName] = packaged[file]
})

tooltip('Saving gist (' + id + ') ...')
Expand Down Expand Up @@ -479,21 +508,19 @@ fileExplorer.prototype.toGist = function (id) {
}

// return all the files, except the temporary/readonly ones..
fileExplorer.prototype.packageFiles = function (filesProvider, callback) {
fileExplorer.prototype.packageFiles = function (filesProvider, directory, callback) {
var ret = {}
filesProvider.resolveDirectory('/', (error, files) => {
filesProvider.resolveDirectory(directory, (error, files) => {
if (error) callback(error)
else {
async.eachSeries(Object.keys(files), (path, cb) => {
filesProvider.get(path, (error, content) => {
if (error) return cb(error)
if (/^\s+$/.test(content) || !content.length) {
content = '// this line is added to create a gist. Empty file is not allowed.'
}
if (error) cb(error)
else {
ret[path] = { content }
cb()
}
ret[path] = { content }
cb()
})
}, (error) => {
callback(error, ret)
Expand All @@ -515,7 +542,7 @@ fileExplorer.prototype.copyFiles = function () {
)
function doCopy (target) {
// package only files from the browser storage.
self.packageFiles(self.files, (error, packaged) => {
self.packageFiles(self.files, '/', (error, packaged) => {
if (error) {
console.log(error)
} else {
Expand All @@ -532,16 +559,6 @@ fileExplorer.prototype.copyFiles = function () {
}
}

// ------------------ gist publish --------------
fileExplorer.prototype.updateGist = function () {
const gistId = this.files.id
if (!gistId) {
tooltip('no gist content is currently loaded.')
} else {
this.toGist(gistId)
}
}

fileExplorer.prototype.createNewFile = function () {
let self = this
modalDialogCustom.prompt('Create new file', 'File Path (Untitled.sol, Folder1/Untitled.sol)', 'Untitled.sol', (input) => {
Expand Down
6 changes: 5 additions & 1 deletion src/app/files/fileManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class FileManager extends Plugin {
localhostExplorer: this._components.registry.get('fileproviders/localhost').api,
filesProviders: this._components.registry.get('fileproviders').api
}

this._deps.browserExplorer.event.register('fileChanged', (path) => { this.fileChangedEvent(path) })
this._deps.browserExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.localhostExplorer.event.register('fileRenamed', (oldName, newName, isFolder) => { this.fileRenamedEvent(oldName, newName, isFolder) })
this._deps.browserExplorer.event.register('fileRemoved', (path) => { this.fileRemovedEvent(path) })
Expand All @@ -58,6 +58,10 @@ class FileManager extends Plugin {
this._deps.localhostExplorer.event.register('closed', (event) => { this.removeTabsOf(this._deps.localhostExplorer) })
}

fileChangedEvent (path) {
this.syncEditor(path)
}

fileRenamedEvent (oldName, newName, isFolder) {
if (!isFolder) {
this._deps.config.set('currentFile', '')
Expand Down
77 changes: 56 additions & 21 deletions src/app/files/fileProvider.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,59 @@
'use strict'

var EventManager = require('../../lib/events')
const CompilerImport = require('../compiler/compiler-imports')
const EventManager = require('../../lib/events')
const modalDialogCustom = require('../ui/modal-dialog-custom')
const tooltip = require('../ui/tooltip')
const remixLib = require('remix-lib')
const Storage = remixLib.Storage

class FileProvider {
constructor (name) {
this.event = new EventManager()
this.type = name
this.normalizedNames = {} // contains the raw url associated with the displayed path
this.readonlyItems = ['browser']
this.providerExternalsStorage = new Storage('providerExternals:')
this.externalFolders = [this.type + '/swarm', this.type + '/ipfs', this.type + '/github', this.type + '/gist', this.type + '/https']
}

addNormalizedName (path, url) {
this.providerExternalsStorage.set(this.type + '/' + path, url)
}

removeNormalizedName (path) {
this.providerExternalsStorage.remove(path)
}

normalizedNameExists (path) {
return this.providerExternalsStorage.exists(path)
}

getNormalizedName (path) {
return this.providerExternalsStorage.get(path)
}

isExternalFolder (path) {
return this.externalFolders.includes(path)
}

discardChanges (path) {
this.remove(path)
const compilerImport = new CompilerImport()
this.providerExternalsStorage.keys().map(value => {
if (value.indexOf(path) === 0) {
compilerImport.import(
this.getNormalizedName(value),
true,
(loadingMsg) => { tooltip(loadingMsg) },
(error, content, cleanUrl, type, url) => {
if (error) {
modalDialogCustom.alert(error)
} else {
this.addExternal(type + '/' + cleanUrl, content, url)
}
}
)
}
})
}

exists (path, cb) {
Expand All @@ -25,7 +71,7 @@ class FileProvider {

get (path, cb) {
cb = cb || function () {}
if (this.normalizedNames[path]) path = this.normalizedNames[path] // ensure we actually use the normalized path from here
if (this.normalizedNameExists(path)) path = this.getNormalizedName(path) // ensure we actually use the normalized path from here
var unprefixedpath = this.removePrefix(path)
var exists = window.remixFileSystem.existsSync(unprefixedpath)
if (!exists) return cb(null, null)
Expand All @@ -36,12 +82,10 @@ class FileProvider {

set (path, content, cb) {
cb = cb || function () {}
if (this.isReadOnly(path)) {
cb(new Error('It is not possible to modify a readonly item'))
return false
}
var unprefixedpath = this.removePrefix(path)
var exists = window.remixFileSystem.existsSync(unprefixedpath)
if (exists && window.remixFileSystem.readFileSync(unprefixedpath, 'utf8') === content) return true

if (!exists && unprefixedpath.indexOf('/') !== -1) {
const paths = unprefixedpath.split('/')
paths.pop() // last element should the filename
Expand Down Expand Up @@ -70,26 +114,17 @@ class FileProvider {
return true
}

addReadOnly (path, content, url) {
this.readonlyItems.push(this.type + '/' + path)
if (!url) this.normalizedNames[url] = path
// this will not add a folder as readonly but keep the original url to be able to restore it later
addExternal (path, content, url) {
if (url) this.addNormalizedName(path, url)
return this.set(path, content)
}

isReadOnly (path) {
return this.readonlyItems.includes(path)
}

_removeFromReadonlyList (path) {
const indexToRemove = this.readonlyItems.indexOf(path)
if (indexToRemove !== -1) {
this.readonlyItems.splice(indexToRemove, 1)
}
return false
}

remove (path) {
this._removeFromReadonlyList(path)

var unprefixedpath = this.removePrefix(path)
if (!this._exists(unprefixedpath)) {
return false
Expand Down
8 changes: 4 additions & 4 deletions src/app/files/remixDProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,6 @@ module.exports = class RemixDProvider {
return true
}

addReadOnly (path, content) {
return false
}

isReadOnly (path) {
return this._readOnlyMode || this._readOnlyFiles[path] === 1
}
Expand Down Expand Up @@ -154,6 +150,10 @@ module.exports = class RemixDProvider {
return true
}

isExternalFolder (path) {
return false
}

removePrefix (path) {
path = path.indexOf(this.type) === 0 ? path.replace(this.type, '') : path
if (path[0] === '/') return path.substring(1)
Expand Down
4 changes: 2 additions & 2 deletions src/app/tabs/compile-tab.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/app/tabs/compileTab/compileTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class CompileTab {
if (error) return cb(error)

if (this.fileProvider) {
this.fileProvider.addReadOnly(cleanUrl, content, url)
this.fileProvider.addExternal(cleanUrl, content, url)
}
cb(null, content)
})
Expand Down
2 changes: 1 addition & 1 deletion src/app/ui/contextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module.exports = (event, items) => {
current.onclick = () => { hide(null, true); items[item]() }
return current
})
var container = yo`<div class="${css.container} bg-light"><ul id='menuitems'>${menu}</ul></div>`
var container = yo`<div class="p-1 ${css.container} bg-light"><ul id='menuitems'>${menu}</ul></div>`
container.style.left = event.pageX + 'px'
container.style.top = event.pageY + 'px'
container.style.display = 'block'
Expand Down
Loading