diff --git a/apps/remix-ide/src/app/files/workspaceFileProvider.js b/apps/remix-ide/src/app/files/workspaceFileProvider.js index 4b0ac2af2e..8f6b190668 100644 --- a/apps/remix-ide/src/app/files/workspaceFileProvider.js +++ b/apps/remix-ide/src/app/files/workspaceFileProvider.js @@ -80,8 +80,16 @@ class WorkspaceFileProvider extends FileProvider { return path.replace(this.workspacesPath + '/' + this.workspace + '/', '') } - createWorkspace (name) { + async createWorkspace (name) { if (!name) name = 'default_workspace' + this.setWorkspace(name) + const workspacePath = 'browser/' + this.workspacesPath + '/' + name + const workspaceRootPath = 'browser/' + this.workspacesPath + const workspaceRootPathExists = await super.exists(workspaceRootPath) + const workspacePathExists = await super.exists(workspacePath) + + if (!workspaceRootPathExists) super.createDir(workspaceRootPath) + if (!workspacePathExists) super.createDir(workspacePath) this.event.emit('createWorkspace', name) } } diff --git a/libs/remix-ui/workspace/src/lib/actions/gist-handler.ts b/libs/remix-ui/workspace/src/lib/actions/gist-handler.ts new file mode 100644 index 0000000000..733ffede34 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/actions/gist-handler.ts @@ -0,0 +1,73 @@ +// var modalDialogCustom = require('../app/ui/modal-dialog-custom') +var request = require('request') + +// Allowing window to be overriden for testing +function GistHandler (_window) { + if (_window !== undefined) { + // modalDialogCustom = _window + } + + this.handleLoad = function (params, cb) { + if (!cb) cb = () => {} + var loadingFromGist = false + var gistId + if (params.gist === '') { + loadingFromGist = true + // modalDialogCustom.prompt('Load a Gist', 'Enter the ID of the Gist or URL you would like to load.', null, (target) => { + if (target !== '') { + gistId = getGistId(target) + if (gistId) { + cb(gistId) + } else { + // modalDialogCustom.alert('Gist load error', 'Error while loading gist. Please provide a valid Gist ID or URL.') + } + } + }) + return loadingFromGist + } else { + gistId = params.gist + loadingFromGist = !!gistId + } + if (loadingFromGist) { + cb(gistId) + } + return loadingFromGist + } + + function getGistId (str) { + var idr = /[0-9A-Fa-f]{8,}/ + var match = idr.exec(str) + return match ? match[0] : null + } + + this.loadFromGist = (params, fileManager) => { + const self = this + return self.handleLoad(params, function (gistId) { + request.get({ + url: `https://api.github.com/gists/${gistId}`, + json: true + }, async (error, response, data = {}) => { + if (error || !data.files) { + // modalDialogCustom.alert('Gist load error', error || data.message) + return + } + const obj = {} + Object.keys(data.files).forEach((element) => { + const path = element.replace(/\.\.\./g, '/') + + obj['/' + 'gist-' + gistId + '/' + path] = data.files[element] + }) + fileManager.setBatchFiles(obj, 'workspace', true, (errorLoadingFile) => { + if (!errorLoadingFile) { + const provider = fileManager.getProvider('workspace') + provider.lastLoadedGistId = gistId + } else { + // modalDialogCustom.alert('Gist load error', errorLoadingFile.message || errorLoadingFile) + } + }) + }) + }) + } +} + +module.exports = GistHandler diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 0f5aee08bd..8ede21ac4b 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -1,7 +1,7 @@ import { bufferToHex, keccakFromString } from 'ethereumjs-util' import { checkSpecialChars, checkSlash } from '../../../../../../apps/remix-ide/src/lib/helper' -const GistHandler = require('../../../../../../apps/remix-ide/src/lib/gist-handler') +// const GistHandler = require('../../../../../../apps/remix-ide/src/lib/gist-handler') const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params') const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples') let plugin @@ -13,35 +13,53 @@ const setCurrentWorkspace = (workspace: string) => { } } -const processCreateWorkspace = async (name: string) => { - const workspaceProvider = plugin.fileProviders.workspace - const browserProvider = plugin.fileProviders.browser - const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name - const workspaceRootPath = 'browser/' + workspaceProvider.workspacesPath - const workspaceRootPathExists = await browserProvider.exists(workspaceRootPath) - const workspacePathExists = await browserProvider.exists(workspacePath) - - if (!workspaceRootPathExists) browserProvider.createDir(workspaceRootPath) - if (!workspacePathExists) browserProvider.createDir(workspacePath) - workspaceProvider.setWorkspace(name) +const setWorkspaces = (workspaces: string[]) => { + return { + type: 'SET_WORKSPACES', + payload: workspaces + } } -const createWorkspace = async (workspaceName: string, setDefaults = true) => { +const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true, template: 'gist-template' | 'code-template' | 'default-template' = 'default-template') => { if (!workspaceName) throw new Error('workspace name cannot be empty') if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed') if (await workspaceExists(workspaceName)) throw new Error('workspace already exists') else { const workspaceProvider = plugin.fileProviders.workspace - await processCreateWorkspace(workspaceName) + await workspaceProvider.createWorkspace(workspaceName) if (setDefaults) { - // insert example contracts if there are no files to show - for (const file in examples) { - try { - await workspaceProvider.set(examples[file].name, examples[file].content) - } catch (error) { - console.error(error) - } + switch (template) { + case 'code-template': + // creates a new workspace code-sample and loads code from url params. + try { + const queryParams = new QueryParams() + const params = queryParams.get() + await plugin.fileProviders.worspace.createWorkspace(workspaceName) + const hash = bufferToHex(keccakFromString(params.code)) + const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' + const path = fileName + + await plugin.fileProviders.workspace.set(path, atob(params.code)) + await plugin.fileManager.openFile(fileName) + } catch (e) { + console.error(e) + } + break + case 'gist-template': + // creates a new workspace gist-sample and get the file from gist + break + case 'default-template': + // creates a new workspace and populates it with default project template. + // insert example contracts + for (const file in examples) { + try { + await workspaceProvider.set(examples[file].name, examples[file].content) + } catch (error) { + console.error(error) + } + } + break } } } @@ -58,56 +76,39 @@ const workspaceExists = async (name: string) => { export const initWorkspace = (filePanelPlugin) => async (dispatch: React.Dispatch) => { plugin = filePanelPlugin const queryParams = new QueryParams() - const gistHandler = new GistHandler() + // const gistHandler = new GistHandler() const params = queryParams.get() - let initialWorkspace = '' - // get the file from gist - let loadedFromGist = false + // let loadedFromGist = false + const workspaces = await getWorkspaces() || [] + + dispatch(setWorkspaces(workspaces)) + // if (params.gist) { + // initialWorkspace = 'gist-sample' + // await filePanelPlugin.fileProviders.workspace.createWorkspace(initialWorkspace) + // loadedFromGist = gistHandler.loadFromGist(params, plugin.fileManager) + // } + // if (loadedFromGist) { + // dispatch(setWorkspaces(workspaces)) + // dispatch(setCurrentWorkspace(initialWorkspace)) + // return + // } if (params.gist) { - await processCreateWorkspace('gist-sample') - initialWorkspace = 'gist-sample' - loadedFromGist = gistHandler.loadFromGist(params, plugin.fileManager) - } - if (loadedFromGist) return dispatch(setCurrentWorkspace(initialWorkspace)) - - if (params.code) { - try { - await processCreateWorkspace('code-sample') - const hash = bufferToHex(keccakFromString(params.code)) - const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' - const path = fileName - - await plugin.fileProviders.workspace.set(path, atob(params.code)) - initialWorkspace = 'code-sample' - await plugin.fileManager.openFile(fileName) - } catch (e) { - console.error(e) - } - return dispatch(setCurrentWorkspace(initialWorkspace)) - } - initialWorkspace = await new Promise((resolve, reject) => { - plugin.fileProviders.browser.resolveDirectory('/', async (error, filesList) => { - if (error) return reject(error) - if (Object.keys(filesList).length === 0) { - await createWorkspace('default_workspace') - return resolve('default_workspace') - } else { - plugin.fileProviders.browser.resolveDirectory('.workspaces', async (error, filesList) => { - if (error) return reject(error) - if (Object.keys(filesList).length > 0) { - const workspacePath = Object.keys(filesList)[0].split('/').filter(val => val) - const workspaceName = workspacePath[workspacePath.length - 1] - - plugin.fileProviders.workspace.setWorkspace(workspaceName) - return resolve(workspaceName) - } - return reject(new Error('Can\'t find available workspace.')) - }) + } else if (params.code) { + await createWorkspaceTemplate('code-sample', true, 'code-template') + dispatch(setCurrentWorkspace('code-sample')) + } else { + if (workspaces.length === 0) { + await createWorkspaceTemplate('default_workspace') + dispatch(setCurrentWorkspace('default_workspace')) + } else { + if (workspaces.length > 0) { + plugin.fileProviders.workspace.setWorkspace(workspaces[workspaces.length - 1]) + dispatch(setCurrentWorkspace(workspaces[workspaces.length - 1])) } - }) - }) + } + } // plugin.fileProviders.localhost.event.off('disconnected', localhostDisconnect) // plugin.fileProviders.localhost.event.on('disconnected', localhostDisconnect) @@ -127,5 +128,27 @@ export const initWorkspace = (filePanelPlugin) => async (dispatch: React.Dispatc // plugin.fileProviders.workspace.event.on('createWorkspace', (name) => { // createNewWorkspace(name) // }) - return dispatch(setCurrentWorkspace(initialWorkspace)) +} + +const getWorkspaces = async (): Promise | undefined => { + try { + const workspaces: string[] = await new Promise((resolve, reject) => { + const workspacesPath = plugin.fileProviders.workspace.workspacesPath + + plugin.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => { + if (error) { + console.error(error) + return reject(error) + } + resolve(Object.keys(items) + .filter((item) => items[item].isDirectory) + .map((folder) => folder.replace(workspacesPath + '/', ''))) + }) + }) + + return workspaces + } catch (e) { + // modalDialogCustom.alert('Workspaces have not been created on your system. Please use "Migrate old filesystem to workspace" on the home page to transfer your files or start by creating a new workspace in the File Explorers.') + console.log(e) + } } diff --git a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts index 81a31899a2..403bde8396 100644 --- a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts @@ -34,6 +34,16 @@ export const browserReducer = (state = browserInitialState, action: Action) => { } } } + + case 'SET_WORKSPACES': { + return { + ...state, + browser: { + ...state.browser, + workspaces: Array.isArray(action.payload) ? action.payload : state.browser.workspaces + } + } + } default: throw new Error() }