Merge pull request #2374 from ethereum/kuku

FE refactoring: added Discard changes for external folder. removed readonly.
pull/5370/head
yann300 5 years ago committed by GitHub
commit 22f931748d
  1. 10
      src/app/compiler/compiler-imports.js
  2. 97
      src/app/files/file-explorer.js
  3. 6
      src/app/files/fileManager.js
  4. 77
      src/app/files/fileProvider.js
  5. 8
      src/app/files/remixDProvider.js
  6. 4
      src/app/tabs/compile-tab.js
  7. 2
      src/app/tabs/compileTab/compileTab.js
  8. 2
      src/app/ui/contextMenu.js
  9. 2
      src/app/ui/landing-page/landing-page.js

@ -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)

@ -190,21 +190,56 @@ 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) {
@ -212,29 +247,20 @@ function fileExplorer (localRegistry, files, menuItems) {
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)
})
@ -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?`, () => {
@ -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)
@ -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 + ') ...')
@ -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)
@ -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 {
@ -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) => {

@ -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) })
@ -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', '')

@ -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) {
@ -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)
@ -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
@ -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

@ -113,10 +113,6 @@ module.exports = class RemixDProvider {
return true
}
addReadOnly (path, content) {
return false
}
isReadOnly (path) {
return this._readOnlyMode || this._readOnlyFiles[path] === 1
}
@ -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)

@ -313,7 +313,7 @@ class CompileTab extends ViewPlugin {
modalDialogCustom.alert(yo`<span>Metadata published successfully.<br> <pre>${result}</pre> </span>`)
}
}, (item) => { // triggered each time there's a new verified publish (means hash correspond)
this.fileProvider.addReadOnly('swarm/' + item.hash, item.content)
this.fileProvider.addExternal('swarm/' + item.hash, item.content)
})
} else {
publishOnIpfs(contract, this.fileManager, function (err, uploaded) {
@ -330,7 +330,7 @@ class CompileTab extends ViewPlugin {
modalDialogCustom.alert(yo`<span>Metadata published successfully.<br> <pre>${result}</pre> </span>`)
}
}, (item) => { // triggered each time there's a new verified publish (means hash correspond)
this.fileProvider.addReadOnly('ipfs/' + item.hash, item.content)
this.fileProvider.addExternal('ipfs/' + item.hash, item.content)
})
}
}

@ -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)
})

@ -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'

@ -106,7 +106,7 @@ export class LandingPage extends ViewPlugin {
if (error) {
modalDialogCustom.alert(error)
} else {
fileProviders['browser'].addReadOnly(type + '/' + cleanUrl, content, url)
fileProviders['browser'].addExternal(type + '/' + cleanUrl, content, url)
this.verticalIcons.select('fileExplorers')
}
}

Loading…
Cancel
Save