From b01d87d7c643d7aca638119257c4cdaab81b53e8 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 24 Feb 2021 17:22:28 +0100 Subject: [PATCH 01/26] move workspace to a react component --- apps/remix-ide/src/app.js | 1 - apps/remix-ide/src/app/panels/file-panel.js | 352 +++----------- .../app/panels/styles/file-panel-styles.css | 59 --- libs/remix-ui/workspace/.babelrc | 4 + libs/remix-ui/workspace/.eslintrc | 19 + libs/remix-ui/workspace/README.md | 7 + libs/remix-ui/workspace/src/index.ts | 1 + .../workspace/src/lib/remix-ui-workspace.css | 40 +- .../workspace/src/lib/remix-ui-workspace.tsx | 432 ++++++++++++++++++ libs/remix-ui/workspace/tsconfig.json | 16 + libs/remix-ui/workspace/tsconfig.lib.json | 13 + nx.json | 3 + tsconfig.json | 7 +- workspace.json | 22 +- 14 files changed, 592 insertions(+), 384 deletions(-) delete mode 100644 apps/remix-ide/src/app/panels/styles/file-panel-styles.css create mode 100644 libs/remix-ui/workspace/.babelrc create mode 100644 libs/remix-ui/workspace/.eslintrc create mode 100644 libs/remix-ui/workspace/README.md create mode 100644 libs/remix-ui/workspace/src/index.ts rename apps/remix-ide/src/app/panels/styles/file-panel-styles.js => libs/remix-ui/workspace/src/lib/remix-ui-workspace.css (62%) create mode 100644 libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx create mode 100644 libs/remix-ui/workspace/tsconfig.json create mode 100644 libs/remix-ui/workspace/tsconfig.lib.json diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index f4f0f02eb0..52c194e04a 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -486,6 +486,5 @@ Please make a backup of your contracts and start using http://remix.ethereum.org migrateToWorkspace(fileManager) - filePanel.initWorkspace() if (params.embed) framingService.embed() } diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 7f9596b066..fd86d35ffa 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -3,10 +3,7 @@ import { ViewPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line import ReactDOM from 'react-dom' -import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line -import './styles/file-panel-styles.css' -var ethutil = require('ethereumjs-util') -var yo = require('yo-yo') +import { Workspace } from '@remix-ui/workspace' // eslint-disable-line var EventManager = require('../../lib/events') var { RemixdHandle } = require('../files/remixd-handle.js') var { GitHandle } = require('../files/git-handle.js') @@ -14,9 +11,6 @@ var globalRegistry = require('../../global/registry') var examples = require('../editor/examples') var GistHandler = require('../../lib/gist-handler') var QueryParams = require('../../lib/query-params') -const modalDialog = require('../ui/modal-dialog-custom') - -var canUpload = window.File || window.FileReader || window.FileList || window.Blob /* Overview of APIs: @@ -51,328 +45,86 @@ const profile = { module.exports = class Filepanel extends ViewPlugin { constructor (appManager) { super(profile) + this.event = new EventManager() this._components = {} this._components.registry = globalRegistry this._deps = { fileProviders: this._components.registry.get('fileproviders').api, - fileManager: this._components.registry.get('filemanager').api, - config: this._components.registry.get('config').api - } - this.LOCALHOST = ' - connect to localhost - ' - this.NO_WORKSPACE = ' - none - ' - this.hideRemixdExplorer = true - this.remixdExplorer = { - hide: () => { - if (this.currentWorkspace === this.LOCALHOST) this.setWorkspace(this.NO_WORKSPACE) - this._deps.fileManager.setMode('browser') - this.hideRemixdExplorer = true - this.renderComponent() - }, - show: () => { - this._deps.fileManager.setMode('localhost') - this.hideRemixdExplorer = false - this.renderComponent() - } + fileManager: this._components.registry.get('filemanager').api } - this.reset = false - this.registeredMenuItems = [] - this.displayNewFile = false - this.uploadFileEvent = null - this.el = yo` -
-
- ` - + + this.el = document.createElement('div') + this.el.setAttribute('id', 'fileExplorerView') + this.remixdHandle = new RemixdHandle(this.remixdExplorer, this._deps.fileProviders.localhost, appManager) this.gitHandle = new GitHandle() - - this.event = new EventManager() - this._deps.fileProviders.localhost.event.register('connecting', (event) => { - }) - - this._deps.fileProviders.localhost.event.register('connected', (event) => { - this.remixdExplorer.show() - }) - - this._deps.fileProviders.localhost.event.register('errored', (event) => { - this.remixdExplorer.hide() - }) - - this._deps.fileProviders.localhost.event.register('closed', (event) => { - this.remixdExplorer.hide() - }) - - this.currentWorkspace = null - - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, fileTree) => { - if (error) return console.error(error) - this.setWorkspace(Object.keys(fileTree)[0].replace(workspacesPath + '/', '')) - }) - this.renderComponent() - } - - async initWorkspace () { - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - const queryParams = new QueryParams() - const params = queryParams.get() - // get the file from gist - const gistHandler = new GistHandler() - const loadedFromGist = gistHandler.loadFromGist(params, this._deps.fileManager) - if (loadedFromGist) return - if (params.code) { - try { - await this._deps.fileManager.createWorkspace('code-sample') - var hash = ethutil.bufferToHex(ethutil.keccak(params.code)) - const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' - const path = 'browser/' + workspacesPath + '/code-sample/' + fileName - await this._deps.fileManager.writeFile(path, atob(params.code)) - this.setWorkspace('code-sample') - await this._deps.fileManager.openFile(path) - } catch (e) { - console.error(e) - } - return - } - // insert example contracts if there are no files to show - this._deps.fileProviders.browser.resolveDirectory('/', async (error, filesList) => { - if (error) console.error(error) - if (Object.keys(filesList).length === 0) { - for (const file in examples) { - await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + examples[file].name, examples[file].content) - } - this.setWorkspace('default_workspace') - } - }) - } - - async refreshWorkspacesList () { - if (!document.getElementById('workspacesSelect')) return - const workspaces = await this.getWorkspaces() - workspaces.push(this.LOCALHOST) - workspaces.push(this.NO_WORKSPACE) + + this.request = {} ReactDOM.render( - ( - workspaces - .map((folder) => { - return - })), document.getElementById('workspacesSelect') - ) - } - - resetFocus (value) { - this.reset = value - this.renderComponent() + + , this.el) } - createNewFile () { - this.displayNewFile = true - this.renderComponent() - } - - resetNewFile () { - this.displayNewFile = false - this.renderComponent() + render () { + return this.el } - uploadFile (target) { - this.uploadFileEvent = target - this.renderComponent() + async getCurrentWorkspace () { + return await this.request.getWorkspaces() } - resetUploadFile () { - this.uploadFileEvent = null - this.renderComponent() + async getWorkspaces () { + return await this.request.getWorkspaces() } - render () { - return this.el + async createNewFile () { + return await this.request.createNewFile() } - getWorkspaces () { - return new Promise((resolve, reject) => { - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => { - if (error) return reject(error) - resolve(Object.keys(items) - .filter((item) => items[item].isDirectory) - .map((folder) => folder.replace(workspacesPath + '/', ''))) - }) - }) - } + async uploadFile () { + return await this.request.uploadFile() + } - getCurrentWorkspace () { - return this.currentWorkspace + async createWorkspace () { + return await this.request.createWorkspace() } - async setWorkspace (name) { + /** these are called by the react component, action is already finished whent it's called */ + async setWorkspace (workspace) { this._deps.fileManager.removeTabsOf(this._deps.fileProviders.workspace) - this.currentWorkspace = name - if (name === this.LOCALHOST) { - this._deps.fileProviders.workspace.clearWorkspace() + if (workspace.isLocalhost) { this.call('manager', 'activatePlugin', 'remixd') - } else if (name === this.NO_WORKSPACE) { - this._deps.fileProviders.workspace.clearWorkspace() - } else { - this._deps.fileProviders.workspace.setWorkspace(name) - } - if (name !== this.LOCALHOST && await this.call('manager', 'isActive', 'remixd')) { + } else if (await this.call('manager', 'isActive', 'remixd')) { this.call('manager', 'deactivatePlugin', 'remixd') } - this.renderComponent() - this.emit('setWorkspace', { name }) + this.emit('setWorkspace', workspace) } - - /** - * - * @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] } - * @param callback (...args) => void - */ - registerContextMenuItem (item) { - if (!item) throw new Error('Invalid register context menu argument') - if (!item.name || !item.id) throw new Error('Item name and id is mandatory') - if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided') - - this.registeredMenuItems = [...this.registeredMenuItems, item] - this.renderComponent() - } - - renameWorkspace () { - modalDialog.prompt('Rename Workspace', 'Please choose a name for the workspace', this.currentWorkspace, async (value) => { - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - await this._deps.fileManager.rename('browser/' + workspacesPath + '/' + this.currentWorkspace, 'browser/' + workspacesPath + '/' + value) - this.setWorkspace(value) - this.emit('renameWorkspace', { name: value }) - }) + + renameWorkspace (workspace) { + this.emit('renameWorkspace', workspace) } - createWorkspace () { - return new Promise((resolve, reject) => { - const workspace = `workspace_${Date.now()}` - modalDialog.prompt('New Workspace', 'Please choose a name for the workspace', workspace, (value) => { - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - this._deps.fileProviders.browser.createDir(workspacesPath + '/' + value, async () => { - this.setWorkspace(value) - for (const file in examples) { - await this._deps.fileManager.writeFile(`${examples[file].name}`, examples[file].content) - } - resolve(value) - }) - }, () => reject(new Error('workspace creation rejected by user'))) - }) + deleteWorkspace (workspace) { + this.emit('deleteWorkspace', workspace) } - deleteCurrentWorkspace () { - if (!this.currentWorkspace) return - modalDialog.confirm('Delete Workspace', 'Please confirm workspace deletion', () => { - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - this._deps.fileProviders.browser.remove(workspacesPath + '/' + this.currentWorkspace) - const name = this.currentWorkspace - this.currentWorkspace = null - this.setWorkspace(this.NO_WORKSPACE) - this.renderComponent() - this.emit('deleteWorkspace', { name }) - }) - } - - renderComponent () { - ReactDOM.render( -
-
this.resetFocus(true)}> -
-
-
- - - { - e.stopPropagation() - this.createWorkspace() - }} - className='far fa-plus-square remixui_menuicon' - title='Create a new Workspace'> - - - - - -
-
-
-
-
-
- { this.hideRemixdExplorer && this.currentWorkspace && this.currentWorkspace !== this.NO_WORKSPACE && - - } -
-
- { !this.hideRemixdExplorer && - - } -
-
- { false && - } -
-
-
-
-
- , this.el) - setTimeout(() => { - this.refreshWorkspacesList() - }, 500) + createWorkspace (workspace) { + this.emit('createWorkspace', workspace) } + /** end section */ } diff --git a/apps/remix-ide/src/app/panels/styles/file-panel-styles.css b/apps/remix-ide/src/app/panels/styles/file-panel-styles.css deleted file mode 100644 index 8744e6a18d..0000000000 --- a/apps/remix-ide/src/app/panels/styles/file-panel-styles.css +++ /dev/null @@ -1,59 +0,0 @@ -.remixui_container { - display : flex; - flex-direction : row; - width : 100%; - height : 100%; - box-sizing : border-box; -} -.remixui_fileexplorer { - display : flex; - flex-direction : column; - position : relative; - width : 100%; - padding-left : 6px; - padding-top : 6px; -} -.remixui_fileExplorerTree { - cursor : default; -} -.remixui_gist { - padding : 10px; -} -.remixui_gist i { - cursor : pointer; -} -.remixui_gist i:hover { - color : orange; -} -.remixui_connectToLocalhost { - padding : 10px; -} -.remixui_connectToLocalhost i { - cursor : pointer; -} -.remixui_connectToLocalhost i:hover { - color : var(--secondary) -} -.remixui_uploadFile { - padding : 10px; -} -.remixui_uploadFile label:hover { - color : var(--secondary) -} -.remixui_uploadFile label { - cursor : pointer; -} -.remixui_treeview { - overflow-y : auto; -} -.remixui_dialog { - display: flex; - flex-direction: column; -} -.remixui_dialogParagraph { - margin-bottom: 2em; - word-break: break-word; -} -.remixui_menuicon { - padding-right : 10px; -} diff --git a/libs/remix-ui/workspace/.babelrc b/libs/remix-ui/workspace/.babelrc new file mode 100644 index 0000000000..09d67939cc --- /dev/null +++ b/libs/remix-ui/workspace/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@nrwl/react/babel"], + "plugins": [] +} diff --git a/libs/remix-ui/workspace/.eslintrc b/libs/remix-ui/workspace/.eslintrc new file mode 100644 index 0000000000..dae5c6feeb --- /dev/null +++ b/libs/remix-ui/workspace/.eslintrc @@ -0,0 +1,19 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": "../../../.eslintrc", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "error" + } +} diff --git a/libs/remix-ui/workspace/README.md b/libs/remix-ui/workspace/README.md new file mode 100644 index 0000000000..61389a2bf8 --- /dev/null +++ b/libs/remix-ui/workspace/README.md @@ -0,0 +1,7 @@ +# remix-ui-workspace + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test remix-ui-workspace` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/remix-ui/workspace/src/index.ts b/libs/remix-ui/workspace/src/index.ts new file mode 100644 index 0000000000..b6c5ded2a1 --- /dev/null +++ b/libs/remix-ui/workspace/src/index.ts @@ -0,0 +1 @@ +export * from './lib/remix-ui-workspace'; diff --git a/apps/remix-ide/src/app/panels/styles/file-panel-styles.js b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.css similarity index 62% rename from apps/remix-ide/src/app/panels/styles/file-panel-styles.js rename to libs/remix-ui/workspace/src/lib/remix-ui-workspace.css index 4db78296b9..f190b84fac 100644 --- a/apps/remix-ide/src/app/panels/styles/file-panel-styles.js +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.css @@ -1,14 +1,11 @@ -var csjs = require('csjs-inject') - -var css = csjs` - .container { +.remixui_container { display : flex; flex-direction : row; width : 100%; height : 100%; box-sizing : border-box; } - .fileexplorer { + .remixui_fileexplorer { display : flex; flex-direction : column; position : relative; @@ -16,47 +13,48 @@ var css = csjs` padding-left : 6px; padding-top : 6px; } - .fileExplorerTree { + .remixui_fileExplorerTree { cursor : default; } - .gist { + .remixui_gist { padding : 10px; } - .gist i { + .remixui_gist i { cursor : pointer; } - .gist i:hover { + .remixui_gist i:hover { color : orange; } - .connectToLocalhost { + .remixui_connectToLocalhost { padding : 10px; } - .connectToLocalhost i { + .remixui_connectToLocalhost i { cursor : pointer; } - .connectToLocalhost i:hover { + .remixui_connectToLocalhost i:hover { color : var(--secondary) } - .uploadFile { + .remixui_uploadFile { padding : 10px; } - .uploadFile label:hover { + .remixui_uploadFile label:hover { color : var(--secondary) } - .uploadFile label { + .remixui_uploadFile label { cursor : pointer; } - .treeview { + .remixui_treeview { overflow-y : auto; } - .dialog { + .remixui_dialog { display: flex; flex-direction: column; } - .dialogParagraph { + .remixui_dialogParagraph { margin-bottom: 2em; word-break: break-word; } -` - -module.exports = css + .remixui_menuicon { + padding-right : 10px; + } + \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx new file mode 100644 index 0000000000..03ec84888f --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -0,0 +1,432 @@ +import React, { useState, useEffect } from 'react'; +import ethutil from 'ethereumjs-util' +import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line +import './remix-ui-workspace.css'; +import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line + +type CodeExamples = { + [key: string]: { name: string, content: string } +}; +/* eslint-disable-next-line */ +export interface WorkspaceProps { + setWorkspace: ({ name: string, isLocalhost: boolean }) => void, + renameWorkspace: ({ name: string }) => void, + createWorkspace: ({ name: string }) => void, + deleteWorkspace: ({ name: string }) => void, + workspace: any // workspace provider, + browser: any // browser provider + localhost: any // localhost provider + fileManager : any + examples: CodeExamples, + queryParams: any // URL parameters + gistHandler: any // handle load gist + registry: any // registry + plugin: any // plugin call and resetFocus + request: any // api request +} + +var canUpload = window.File || window.FileReader || window.FileList || window.Blob + +export const Workspace = (props: WorkspaceProps) => { + const LOCALHOST = ' - connect to localhost - ' + const NO_WORKSPACE = ' - none - ' + + props.request.createWorkspace = () => { + createWorkspace() + } + + props.request.getWorkspaces = () => { + return getWorkspaces() + } + + props.request.createNewFile = () => { + setState(prevState => { + return { ...prevState, displayNewFile: true } + }) + } + + props.request.uploadFile = (target) => { + setState(prevState => { + return { ...prevState, uploadFileEvent: target } + }) + } + + props.request.getCurrentWorkspace = () => { + return state.currentWorkspace + } + + useEffect(() => { + initWorkspace() + }, []) + + const handleHideCreateModal = () => { + setState(prevState => { + state.createModal.hide = true + return { ...prevState, ...state.createModal } + }) + } + + const handleHideDeleteModal = () => { + setState(prevState => { + state.deleteModal.hide = true + return { ...prevState, ...state.deleteModal } + }) + } + + const handleHideRenameModal = () => { + setState(prevState => { + state.renameModal.hide = true + return { ...prevState, ...state.renameModal } + }) + } + + const createWorkspace = () => { + setState(prevState => { + state.createModal.hide = false + return { ...prevState, ...state.createModal } + }) + } + + const renameCurrentWorkspace = () => { + setState(prevState => { + state.renameModal.hide = false + return { ...prevState, ...state.renameModal } + }) + } + + const deleteCurrentWorkspace = () => { + setState(prevState => { + state.deleteModal.hide = false + return { ...prevState, ...state.deleteModal } + }) + } + + const [state, setState] = useState({ + workspaces: [], + reset: false, + currentWorkspace: NO_WORKSPACE, + hideRemixdExplorer: true, + registeredMenuItems: [], + displayNewFile: false, + externalUploads: null, + uploadFileEvent: null, + renameModal: { + id: 'renameWorkspace', + hide: true, + title: 'Rename Workspace', + message: 'Please choose a name for the workspace', + ok: { + label: '', + fn: () => onFinishRenameWorkspace() + }, + cancel: { + label: '', + fn: () => {} + }, + handleHide: handleHideRenameModal + }, + deleteModal: { + id: 'deleteWorkspace', + hide: true, + title: 'Remove Workspace', + message: 'Please confirm workspace deletion', + ok: { + label: '', + fn: () => onFinishDeleteWorkspace() + }, + cancel: { + label: '', + fn: () => {} + }, + handleHide: handleHideDeleteModal + }, + createModal: { + id: 'createWorkspace', + hide: true, + title: 'Create Workspace', + message: 'Please choose a name for the workspace', + ok: { + label: '', + fn: () => onFinishCreateWorkspace() + }, + cancel: { + label: '', + fn: () => {} + }, + handleHide: handleHideCreateModal + } + }) + + let worspaceNewName // used for renaming and creation + + const onFinishRenameWorkspace = async () => { + const workspacesPath = props.workspace.workspacesPath + await props.fileManager.rename('browser/' + workspacesPath + '/' + state.currentWorkspace, 'browser/' + workspacesPath + '/' + worspaceNewName) + setWorkspace(worspaceNewName) + worspaceNewName = '' + props.renameWorkspace({ name: state.currentWorkspace }) + } + + const onFinishCreateWorkspace = async () => { + const workspacesPath = props.workspace.workspacesPath + props.browser.createDir(workspacesPath + '/' + worspaceNewName, async () => { + setWorkspace(worspaceNewName) + for (const file in props.examples) { + await props.fileManager.writeFile(`${props.examples[file].name}`, props.examples[file].content) + } + worspaceNewName = '' + props.createWorkspace({ name: state.currentWorkspace }) + }) + } + + const onFinishDeleteWorkspace = async () => { + const workspacesPath = props.workspace.workspacesPath + props.browser.remove(workspacesPath + '/' + state.currentWorkspace) + const name = state.currentWorkspace + setWorkspace(NO_WORKSPACE) + props.deleteWorkspace({ name }) + } + + const resetFocus = (reset) => { + /*setState(prevState => { + return { ...prevState, reset } + })*/ + } + + const setWorkspace = async (name) => { + if (name === LOCALHOST) { + props.workspace.clearWorkspace() + } else if (name === NO_WORKSPACE) { + props.workspace.clearWorkspace() + } else { + props.workspace.setWorkspace(name) + } + const workspaces = await getWorkspaces() + setState(prevState => { + return { ...prevState, workspaces, currentWorkspace: name } + }) + props.setWorkspace({ name, isLocalhost: name === LOCALHOST }) + } + + const initWorkspace = async () => { + const workspacesPath = props.workspace.workspacesPath + const params = props.queryParams.get() + // get the file from gist + const loadedFromGist = props.gistHandler.loadFromGist(params, props.fileManager) + if (loadedFromGist) return + if (params.code) { + try { + await props.fileManager.createWorkspace('code-sample') + var hash = ethutil.bufferToHex(ethutil.keccak(params.code)) + const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' + const path = 'browser/' + workspacesPath + '/code-sample/' + fileName + await props.fileManager.writeFile(path, atob(params.code)) + setWorkspace('code-sample') + await props.fileManager.openFile(path) + } catch (e) { + console.error(e) + } + return + } + // insert example contracts if there are no files to show + props.browser.resolveDirectory('/', async (error, filesList) => { + if (error) console.error(error) + if (Object.keys(filesList).length === 0) { + for (const file in props.examples) { + await props.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + props.examples[file].name, props.examples[file].content) + } + setWorkspace('default_workspace') + } else { + // we've already got some workspaces + const workspaces = await getWorkspaces() + if (workspaces.length) setWorkspace(workspaces[0]) + else setWorkspace(NO_WORKSPACE) + } + }) + } + + const getWorkspaces = (): any => { + return new Promise((resolve, reject) => { + const workspacesPath = props.workspace.workspacesPath + props.browser.resolveDirectory('/' + workspacesPath, (error, items) => { + if (error) return reject(error) + resolve(Object.keys(items) + .filter((item) => items[item].isDirectory) + .map((folder) => folder.replace(workspacesPath + '/', ''))) + }) + }) + } + + const remixdExplorer = { + hide: () => { + if (state.currentWorkspace === LOCALHOST) setWorkspace(NO_WORKSPACE) + props.fileManager.setMode('browser') + setState(prevState => { + return { ...prevState, hideRemixdExplorer: true } + }) + }, + show: () => { + props.fileManager.setMode('localhost') + setState(prevState => { + return { ...prevState, hideRemixdExplorer: false } + }) + } + } + + props.localhost.event.register('connecting', (event) => {}) + + props.localhost.event.register('connected', (event) => { + remixdExplorer.show() + }) + + props.localhost.event.register('errored', (event) => { + remixdExplorer.hide() + }) + + props.localhost.event.register('closed', (event) => { + remixdExplorer.hide() + }) + + props.plugin.resetFocus = () => { + setState(prevState => { + return { ...prevState, reset: true } + }) + } + + return ( +
+ + { worspaceNewName = e.target.value } }/> + + + { worspaceNewName = e.target.value } }/> + + + +
resetFocus(true)}> +
+
+
+ + + { + e.stopPropagation() + createWorkspace() + }} + className='far fa-plus-square remixui_menuicon' + title='Create a new Workspace'> + + + + + +
+
+
+
+
+
+ { state.hideRemixdExplorer && state.currentWorkspace && state.currentWorkspace !== NO_WORKSPACE && + + } +
+
+ { !state.hideRemixdExplorer && + + } +
+
+ { false && + } +
+
+
+
+
+ ); +}; + +export default Workspace; diff --git a/libs/remix-ui/workspace/tsconfig.json b/libs/remix-ui/workspace/tsconfig.json new file mode 100644 index 0000000000..6b65264565 --- /dev/null +++ b/libs/remix-ui/workspace/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "react", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/libs/remix-ui/workspace/tsconfig.lib.json b/libs/remix-ui/workspace/tsconfig.lib.json new file mode 100644 index 0000000000..b560bc4dec --- /dev/null +++ b/libs/remix-ui/workspace/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/nx.json b/nx.json index 2d9caab10b..5cf047d9cc 100644 --- a/nx.json +++ b/nx.json @@ -92,6 +92,9 @@ }, "debugger": { "tags": [] + }, + "remix-ui-workspace": { + "tags": [] } } } diff --git a/tsconfig.json b/tsconfig.json index 5a873c9f97..c2a383e9de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,9 @@ "@remix-project/remix-astwalker": ["dist/libs/remix-astwalker/index.js"], "@remix-project/remix-debug": ["dist/libs/remix-debug/src/index.js"], "@remix-project/remix-lib": ["dist/libs/remix-lib/src/index.js"], - "@remix-project/remix-simulator": ["dist/libs/remix-simulator/src/index.js"], + "@remix-project/remix-simulator": [ + "dist/libs/remix-simulator/src/index.js" + ], "@remix-project/remix-solidity": ["dist/libs/remix-solidity/index.js"], "@remix-project/remix-tests": ["dist/libs/remix-tests/src/index.js"], "@remix-project/remix-url-resolver": [ @@ -35,7 +37,8 @@ "@remix-project/remix-solidity-ts": ["libs/remix-solidity/src/index.ts"], "@remix-ui/modal-dialog": ["libs/remix-ui/modal-dialog/src/index.ts"], "@remix-ui/toaster": ["libs/remix-ui/toaster/src/index.ts"], - "@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"] + "@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"], + "@remix-ui/workspace": ["libs/remix-ui/workspace/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] diff --git a/workspace.json b/workspace.json index 3b1e4d5880..10793e13b3 100644 --- a/workspace.json +++ b/workspace.json @@ -347,7 +347,11 @@ "linter": "eslint", "config": "libs/remix-tests/.eslintrc", "tsConfig": ["libs/remix-tests/tsconfig.lib.json"], - "exclude": ["**/node_modules/**", "libs/remix-tests/tests/**/*", "**/dist/**"] + "exclude": [ + "**/node_modules/**", + "libs/remix-tests/tests/**/*", + "**/dist/**" + ] } }, "test": { @@ -705,6 +709,22 @@ } } } + }, + "remix-ui-workspace": { + "root": "libs/remix-ui/workspace", + "sourceRoot": "libs/remix-ui/workspace/src", + "projectType": "library", + "schematics": {}, + "architect": { + "lint": { + "builder": "@nrwl/linter:lint", + "options": { + "linter": "eslint", + "tsConfig": ["libs/remix-ui/workspace/tsconfig.lib.json"], + "exclude": ["**/node_modules/**", "!libs/remix-ui/workspace/**/*"] + } + } + } } }, "cli": { From a06fc63f0c79207e9c6ef814d7ea8881bdfd9f3a Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Wed, 24 Feb 2021 18:51:54 +0100 Subject: [PATCH 02/26] Fixed modal blur handler. --- .../modal-dialog/src/lib/remix-ui-modal-dialog.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx index 410cb768e3..2caa41219d 100644 --- a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx +++ b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx @@ -40,6 +40,13 @@ export const ModalDialog = (props: ModalDialogProps) => { handleHide() } + const handleBlur = (e) => { + if (!e.currentTarget.contains(e.relatedTarget)) { + e.stopPropagation() + handleHide() + } + } + return (
{ >
{ - e.stopPropagation() - handleHide() - }} + onBlur={handleBlur} ref={modal} tabIndex={-1} className={'modal-content remixModalContent ' + (props.modalClass ? props.modalClass : '')} From 73f9d6a65d51aab7ac20899145bd72d07ae78198 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Wed, 24 Feb 2021 20:07:27 +0100 Subject: [PATCH 03/26] Catch error for inital file loading --- libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 03ec84888f..a36da6e50d 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -26,8 +26,6 @@ export interface WorkspaceProps { } var canUpload = window.File || window.FileReader || window.FileList || window.Blob - -export const Workspace = (props: WorkspaceProps) => { const LOCALHOST = ' - connect to localhost - ' const NO_WORKSPACE = ' - none - ' @@ -233,12 +231,17 @@ export const Workspace = (props: WorkspaceProps) => { if (error) console.error(error) if (Object.keys(filesList).length === 0) { for (const file in props.examples) { + try { await props.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + props.examples[file].name, props.examples[file].content) + } catch (error) { + console.error(error) + } } setWorkspace('default_workspace') } else { // we've already got some workspaces const workspaces = await getWorkspaces() + if (workspaces.length) setWorkspace(workspaces[0]) else setWorkspace(NO_WORKSPACE) } From 6771fd7b552a6ddaba135d40a4a6855ff356ae23 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Wed, 24 Feb 2021 20:09:40 +0100 Subject: [PATCH 04/26] Fix error in previous commit --- libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index a36da6e50d..9930f4f8f1 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -26,6 +26,7 @@ export interface WorkspaceProps { } var canUpload = window.File || window.FileReader || window.FileList || window.Blob +export const Workspace = (props: WorkspaceProps) => { const LOCALHOST = ' - connect to localhost - ' const NO_WORKSPACE = ' - none - ' From c8a621b522f0f8a5204ba7543689e40df12c4f07 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 25 Feb 2021 11:53:02 +0100 Subject: [PATCH 05/26] rearangement --- apps/remix-ide/src/app/panels/file-panel.js | 37 +++- .../workspace/src/lib/remix-ui-workspace.tsx | 199 ++++++++++-------- 2 files changed, 140 insertions(+), 96 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index fd86d35ffa..6fd79c40ed 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -58,14 +58,23 @@ module.exports = class Filepanel extends ViewPlugin { this.remixdHandle = new RemixdHandle(this.remixdExplorer, this._deps.fileProviders.localhost, appManager) this.gitHandle = new GitHandle() - + this.registeredMenuItems = [] this.request = {} + + this.renderComponent() + } + + render () { + return this.el + } + + renderComponent() { ReactDOM.render( , this.el) } - render () { - return this.el + /** + * @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] } + * @param callback (...args) => void + */ + registerContextMenuItem (item) { + if (!item) throw new Error('Invalid register context menu argument') + if (!item.name || !item.id) throw new Error('Item name and id is mandatory') + if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided') + + this.registeredMenuItems = [...this.registeredMenuItems, item] + this.renderComponent() } async getCurrentWorkspace () { @@ -115,15 +134,15 @@ module.exports = class Filepanel extends ViewPlugin { this.emit('setWorkspace', workspace) } - renameWorkspace (workspace) { + workspaceRenamed (workspace) { this.emit('renameWorkspace', workspace) } - deleteWorkspace (workspace) { + workspaceDeleted (workspace) { this.emit('deleteWorkspace', workspace) } - createWorkspace (workspace) { + workspaceCreated (workspace) { this.emit('createWorkspace', workspace) } /** end section */ diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 9930f4f8f1..c2e1042efb 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import ethutil from 'ethereumjs-util' import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line import './remix-ui-workspace.css'; @@ -10,9 +10,9 @@ type CodeExamples = { /* eslint-disable-next-line */ export interface WorkspaceProps { setWorkspace: ({ name: string, isLocalhost: boolean }) => void, - renameWorkspace: ({ name: string }) => void, - createWorkspace: ({ name: string }) => void, - deleteWorkspace: ({ name: string }) => void, + workspaceRenamed: ({ name: string }) => void, + workspaceCreated: ({ name: string }) => void, + workspaceDeleted: ({ name: string }) => void, workspace: any // workspace provider, browser: any // browser provider localhost: any // localhost provider @@ -22,7 +22,8 @@ export interface WorkspaceProps { gistHandler: any // handle load gist registry: any // registry plugin: any // plugin call and resetFocus - request: any // api request + request: any // api request, + registeredMenuItems: [] // menu items } var canUpload = window.File || window.FileReader || window.FileList || window.Blob @@ -30,8 +31,23 @@ export const Workspace = (props: WorkspaceProps) => { const LOCALHOST = ' - connect to localhost - ' const NO_WORKSPACE = ' - none - ' + /* extends the parent 'plugin' with some function needed by the file explorer */ + props.plugin.resetFocus = () => { + setState(prevState => { + return { ...prevState, reset: true } + }) + } + + props.plugin.resetNewFile = () => { + setState(prevState => { + return { ...prevState, displayNewFile: !state.displayNewFile } + }) + } + /**** ****/ + + /* implement an external API, consumed by the parent */ props.request.createWorkspace = () => { - createWorkspace() + return createWorkspace() } props.request.getWorkspaces = () => { @@ -39,9 +55,7 @@ export const Workspace = (props: WorkspaceProps) => { } props.request.createNewFile = () => { - setState(prevState => { - return { ...prevState, displayNewFile: true } - }) + props.plugin.resetNewFile() } props.request.uploadFile = (target) => { @@ -53,59 +67,15 @@ export const Workspace = (props: WorkspaceProps) => { props.request.getCurrentWorkspace = () => { return state.currentWorkspace } + /**** ****/ - useEffect(() => { - initWorkspace() - }, []) - - const handleHideCreateModal = () => { - setState(prevState => { - state.createModal.hide = true - return { ...prevState, ...state.createModal } - }) - } - - const handleHideDeleteModal = () => { - setState(prevState => { - state.deleteModal.hide = true - return { ...prevState, ...state.deleteModal } - }) - } - - const handleHideRenameModal = () => { - setState(prevState => { - state.renameModal.hide = true - return { ...prevState, ...state.renameModal } - }) - } - - const createWorkspace = () => { - setState(prevState => { - state.createModal.hide = false - return { ...prevState, ...state.createModal } - }) - } - - const renameCurrentWorkspace = () => { - setState(prevState => { - state.renameModal.hide = false - return { ...prevState, ...state.renameModal } - }) - } - - const deleteCurrentWorkspace = () => { - setState(prevState => { - state.deleteModal.hide = false - return { ...prevState, ...state.deleteModal } - }) - } + useEffect(() => { initWorkspace() }, []) const [state, setState] = useState({ workspaces: [], reset: false, currentWorkspace: NO_WORKSPACE, hideRemixdExplorer: true, - registeredMenuItems: [], displayNewFile: false, externalUploads: null, uploadFileEvent: null, @@ -122,7 +92,12 @@ export const Workspace = (props: WorkspaceProps) => { label: '', fn: () => {} }, - handleHide: handleHideRenameModal + handleHide: () => { + setState(prevState => { + state.renameModal.hide = true + return { ...prevState, ...state.renameModal } + }) + } }, deleteModal: { id: 'deleteWorkspace', @@ -137,7 +112,12 @@ export const Workspace = (props: WorkspaceProps) => { label: '', fn: () => {} }, - handleHide: handleHideDeleteModal + handleHide: () => { + setState(prevState => { + state.deleteModal.hide = true + return { ...prevState, ...state.deleteModal } + }) + } }, createModal: { id: 'createWorkspace', @@ -152,29 +132,79 @@ export const Workspace = (props: WorkspaceProps) => { label: '', fn: () => {} }, - handleHide: handleHideCreateModal + handleHide: () => { + setState(prevState => { + state.createModal.hide = true + return { ...prevState, ...state.createModal } + }) + } } }) - let worspaceNewName // used for renaming and creation + /* workspace creation, renaming and deletion */ + + const modal = (title: string, message: string, ok: { label: string, fn: () => void }, cancel: { label: string, fn: () => void }) => { + setState(prevState => { + return { + ...prevState, + modals: [...prevState.modals, + { + message, + title, + ok, + cancel, + handleHide: handleHideModal + }] + } + }) + } + + const workspaceRenameInput = useRef() + const workspaceCreateInput = useRef() + + const createWorkspace = () => { + modal() + setState(prevState => { + state.createModal.hide = false + return { ...prevState, ...state.createModal } + }) + } + + const renameCurrentWorkspace = () => { + setState(prevState => { + state.renameModal.hide = false + return { ...prevState, ...state.renameModal } + }) + } + const deleteCurrentWorkspace = () => { + setState(prevState => { + state.deleteModal.hide = false + return { ...prevState, ...state.deleteModal } + }) + } + const onFinishRenameWorkspace = async () => { + if (workspaceRenameInput.current === undefined) return + // @ts-ignore: Object is possibly 'null'. + const workspaceName = workspaceRenameInput.current.value const workspacesPath = props.workspace.workspacesPath - await props.fileManager.rename('browser/' + workspacesPath + '/' + state.currentWorkspace, 'browser/' + workspacesPath + '/' + worspaceNewName) - setWorkspace(worspaceNewName) - worspaceNewName = '' - props.renameWorkspace({ name: state.currentWorkspace }) + await props.fileManager.rename('browser/' + workspacesPath + '/' + state.currentWorkspace, 'browser/' + workspacesPath + '/' + workspaceName) + setWorkspace(workspaceName) + props.workspaceRenamed({ name: state.currentWorkspace }) } const onFinishCreateWorkspace = async () => { + if (workspaceCreateInput.current === undefined) return + // @ts-ignore: Object is possibly 'null'. + const workspaceName = workspaceCreateInput.current.value const workspacesPath = props.workspace.workspacesPath - props.browser.createDir(workspacesPath + '/' + worspaceNewName, async () => { - setWorkspace(worspaceNewName) + props.browser.createDir(workspacesPath + '/' + workspaceName, async () => { + setWorkspace(workspaceName) for (const file in props.examples) { await props.fileManager.writeFile(`${props.examples[file].name}`, props.examples[file].content) } - worspaceNewName = '' - props.createWorkspace({ name: state.currentWorkspace }) + props.workspaceCreated({ name: state.currentWorkspace }) }) } @@ -183,13 +213,14 @@ export const Workspace = (props: WorkspaceProps) => { props.browser.remove(workspacesPath + '/' + state.currentWorkspace) const name = state.currentWorkspace setWorkspace(NO_WORKSPACE) - props.deleteWorkspace({ name }) + props.workspaceDeleted({ name }) } + /**** ****/ const resetFocus = (reset) => { - /*setState(prevState => { + setState(prevState => { return { ...prevState, reset } - })*/ + }) } const setWorkspace = async (name) => { @@ -233,7 +264,7 @@ export const Workspace = (props: WorkspaceProps) => { if (Object.keys(filesList).length === 0) { for (const file in props.examples) { try { - await props.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + props.examples[file].name, props.examples[file].content) + await props.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + props.examples[file].name, props.examples[file].content) } catch (error) { console.error(error) } @@ -290,34 +321,28 @@ export const Workspace = (props: WorkspaceProps) => { props.localhost.event.register('closed', (event) => { remixdExplorer.hide() }) - - props.plugin.resetFocus = () => { - setState(prevState => { - return { ...prevState, reset: true } - }) - } return (
- { worspaceNewName = e.target.value } }/> + handleHide={ state.renameModal.handleHide }> + { state.renameModal.message } + - { worspaceNewName = e.target.value } }/> + handleHide={ state.createModal.handleHide }> + { state.createModal.message } + { hide={ state.deleteModal.hide } ok={ state.deleteModal.ok } cancel={ state.deleteModal.cancel } - handleHide={ handleHideDeleteModal }> + handleHide={ state.deleteModal.handleHide }>
resetFocus(true)}>
@@ -393,7 +418,7 @@ export const Workspace = (props: WorkspaceProps) => { menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']} plugin={props.plugin} focusRoot={state.reset} - contextMenuItems={state.registeredMenuItems} + contextMenuItems={props.registeredMenuItems} displayInput={state.displayNewFile} externalUploads={state.uploadFileEvent} /> @@ -408,7 +433,7 @@ export const Workspace = (props: WorkspaceProps) => { menuItems={['createNewFile', 'createNewFolder']} plugin={props.plugin} focusRoot={state.reset} - contextMenuItems={state.registeredMenuItems} + contextMenuItems={props.registeredMenuItems} /> }
@@ -420,7 +445,7 @@ export const Workspace = (props: WorkspaceProps) => { menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']} plugin={props.plugin} focusRoot={state.reset} - contextMenuItems={state.registeredMenuItems} + contextMenuItems={props.registeredMenuItems} displayInput={state.displayNewFile} externalUploads={state.uploadFileEvent} /> From f5e489f95d93c9d6e41d3288b6de54a2082886e2 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 25 Feb 2021 12:29:44 +0100 Subject: [PATCH 06/26] Move getWorkspaces api to file-panel --- apps/remix-ide/src/app/panels/file-panel.js | 17 ++++- .../workspace/src/lib/remix-ui-workspace.tsx | 75 +++++++++---------- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 6fd79c40ed..30d4100dd5 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -60,8 +60,7 @@ module.exports = class Filepanel extends ViewPlugin { this.gitHandle = new GitHandle() this.registeredMenuItems = [] this.request = {} - - this.renderComponent() + this.getWorkspaces() } render () { @@ -85,6 +84,7 @@ module.exports = class Filepanel extends ViewPlugin { registry={this._components.registry} plugin={this} request={this.request} + workspaces={this.workspaces} registeredMenuItems={this.registeredMenuItems} /> , this.el) @@ -108,7 +108,18 @@ module.exports = class Filepanel extends ViewPlugin { } async getWorkspaces () { - return await this.request.getWorkspaces() + const result = new Promise((resolve, reject) => { + const workspacesPath = this._deps.fileProviders.workspace.workspacesPath + this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => { + if (error) return reject(error) + resolve(Object.keys(items) + .filter((item) => items[item].isDirectory) + .map((folder) => folder.replace(workspacesPath + '/', ''))) + }) + }) + this.workspaces = await result + this.renderComponent() + return this.workspaces } async createNewFile () { diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index c2e1042efb..a25dbbc261 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -23,6 +23,7 @@ export interface WorkspaceProps { registry: any // registry plugin: any // plugin call and resetFocus request: any // api request, + workspaces: any, registeredMenuItems: [] // menu items } @@ -43,16 +44,15 @@ export const Workspace = (props: WorkspaceProps) => { return { ...prevState, displayNewFile: !state.displayNewFile } }) } - /**** ****/ /* implement an external API, consumed by the parent */ props.request.createWorkspace = () => { return createWorkspace() } - props.request.getWorkspaces = () => { - return getWorkspaces() - } + // props.request.getWorkspaces = () => { + // return getWorkspaces() + // } props.request.createNewFile = () => { props.plugin.resetNewFile() @@ -67,10 +67,21 @@ export const Workspace = (props: WorkspaceProps) => { props.request.getCurrentWorkspace = () => { return state.currentWorkspace } - /**** ****/ useEffect(() => { initWorkspace() }, []) + useEffect(() => { + const getWorkspaces = async () => { + if (props.workspaces && Array.isArray(props.workspaces)) { + setState(prevState => { + return { ...prevState, workspaces: props.workspaces } + }) + } + } + + getWorkspaces() + }, [props.workspaces]) + const [state, setState] = useState({ workspaces: [], reset: false, @@ -143,27 +154,26 @@ export const Workspace = (props: WorkspaceProps) => { /* workspace creation, renaming and deletion */ - const modal = (title: string, message: string, ok: { label: string, fn: () => void }, cancel: { label: string, fn: () => void }) => { - setState(prevState => { - return { - ...prevState, - modals: [...prevState.modals, - { - message, - title, - ok, - cancel, - handleHide: handleHideModal - }] - } - }) - } + // const modal = (title: string, message: string, ok: { label: string, fn: () => void }, cancel: { label: string, fn: () => void }) => { + // setState(prevState => { + // return { + // ...prevState, + // modals: [...prevState.modals, + // { + // message, + // title, + // ok, + // cancel, + // handleHide: handleHideModal + // }] + // } + // }) + // } const workspaceRenameInput = useRef() const workspaceCreateInput = useRef() const createWorkspace = () => { - modal() setState(prevState => { state.createModal.hide = false return { ...prevState, ...state.createModal } @@ -223,7 +233,7 @@ export const Workspace = (props: WorkspaceProps) => { }) } - const setWorkspace = async (name) => { + const setWorkspace = async (name) => { if (name === LOCALHOST) { props.workspace.clearWorkspace() } else if (name === NO_WORKSPACE) { @@ -231,9 +241,9 @@ export const Workspace = (props: WorkspaceProps) => { } else { props.workspace.setWorkspace(name) } - const workspaces = await getWorkspaces() + props.plugin.getWorkspaces() setState(prevState => { - return { ...prevState, workspaces, currentWorkspace: name } + return { ...prevState, currentWorkspace: name } }) props.setWorkspace({ name, isLocalhost: name === LOCALHOST }) } @@ -272,26 +282,11 @@ export const Workspace = (props: WorkspaceProps) => { setWorkspace('default_workspace') } else { // we've already got some workspaces - const workspaces = await getWorkspaces() - - if (workspaces.length) setWorkspace(workspaces[0]) - else setWorkspace(NO_WORKSPACE) + setWorkspace(NO_WORKSPACE) } }) } - const getWorkspaces = (): any => { - return new Promise((resolve, reject) => { - const workspacesPath = props.workspace.workspacesPath - props.browser.resolveDirectory('/' + workspacesPath, (error, items) => { - if (error) return reject(error) - resolve(Object.keys(items) - .filter((item) => items[item].isDirectory) - .map((folder) => folder.replace(workspacesPath + '/', ''))) - }) - }) - } - const remixdExplorer = { hide: () => { if (state.currentWorkspace === LOCALHOST) setWorkspace(NO_WORKSPACE) From 0cb43cbfc8b16f8593ddc9122f35ba23521b1a00 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 25 Feb 2021 14:43:43 +0100 Subject: [PATCH 07/26] fix modals --- .../workspace/src/lib/remix-ui-workspace.tsx | 125 +++++++++++------- 1 file changed, 74 insertions(+), 51 deletions(-) diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index a25dbbc261..4218873078 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -97,18 +97,13 @@ export const Workspace = (props: WorkspaceProps) => { message: 'Please choose a name for the workspace', ok: { label: '', - fn: () => onFinishRenameWorkspace() + fn: () => {} }, cancel: { label: '', fn: () => {} }, - handleHide: () => { - setState(prevState => { - state.renameModal.hide = true - return { ...prevState, ...state.renameModal } - }) - } + handleHide: null }, deleteModal: { id: 'deleteWorkspace', @@ -117,18 +112,13 @@ export const Workspace = (props: WorkspaceProps) => { message: 'Please confirm workspace deletion', ok: { label: '', - fn: () => onFinishDeleteWorkspace() + fn: () => {} }, cancel: { label: '', fn: () => {} }, - handleHide: () => { - setState(prevState => { - state.deleteModal.hide = true - return { ...prevState, ...state.deleteModal } - }) - } + handleHide: null }, createModal: { id: 'createWorkspace', @@ -137,63 +127,96 @@ export const Workspace = (props: WorkspaceProps) => { message: 'Please choose a name for the workspace', ok: { label: '', - fn: () => onFinishCreateWorkspace() + fn: () => {} }, cancel: { label: '', fn: () => {} }, - handleHide: () => { - setState(prevState => { - state.createModal.hide = true - return { ...prevState, ...state.createModal } - }) - } + handleHide: null } }) /* workspace creation, renaming and deletion */ - // const modal = (title: string, message: string, ok: { label: string, fn: () => void }, cancel: { label: string, fn: () => void }) => { - // setState(prevState => { - // return { - // ...prevState, - // modals: [...prevState.modals, - // { - // message, - // title, - // ok, - // cancel, - // handleHide: handleHideModal - // }] - // } - // }) - // } - - const workspaceRenameInput = useRef() - const workspaceCreateInput = useRef() - - const createWorkspace = () => { + const renameCurrentWorkspace = () => { setState(prevState => { - state.createModal.hide = false - return { ...prevState, ...state.createModal } + return { + ...prevState, + renameModal: { + ...prevState.renameModal, + hide: false, + ok: { + label: '', + fn: () => onFinishRenameWorkspace() + }, + cancel: { + label: '', + fn: () => {} + }, + handleHide: () => { + setState(prevState => { + return { ...prevState, renameModal: {...prevState.renameModal, hide: true } } + }) + } + }, + } }) } - const renameCurrentWorkspace = () => { + const createWorkspace = () => { setState(prevState => { - state.renameModal.hide = false - return { ...prevState, ...state.renameModal } + return { + ...prevState, + createModal: { + ...prevState.createModal, + hide: false, + ok: { + label: '', + fn: () => onFinishCreateWorkspace() + }, + cancel: { + label: '', + fn: () => {} + }, + handleHide: () => { + setState(prevState => { + return { ...prevState, createModal: {...prevState.createModal, hide: true } } + }) + } + } + } }) - } + } const deleteCurrentWorkspace = () => { setState(prevState => { - state.deleteModal.hide = false - return { ...prevState, ...state.deleteModal } + return { + ...prevState, + deleteModal: { + ...prevState.deleteModal, + hide: false, + ok: { + label: '', + fn: () => onFinishDeleteWorkspace() + }, + cancel: { + label: '', + fn: () => {} + }, + handleHide: () => { + setState(prevState => { + return { ...prevState, deleteModal: {...prevState.deleteModal, hide: true } } + }) + } + }, + } }) - } - + } + + const workspaceRenameInput = useRef() + const workspaceCreateInput = useRef() + const onFinishRenameWorkspace = async () => { if (workspaceRenameInput.current === undefined) return // @ts-ignore: Object is possibly 'null'. From c05a2da1f432a958ad47724095e381e63a920aee Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 25 Feb 2021 14:51:20 +0100 Subject: [PATCH 08/26] Get workspaces after creation --- apps/remix-ide/src/app/panels/file-panel.js | 8 +++++-- .../workspace/src/lib/remix-ui-workspace.tsx | 21 ++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 30d4100dd5..fe6b08f7d4 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -60,7 +60,8 @@ module.exports = class Filepanel extends ViewPlugin { this.gitHandle = new GitHandle() this.registeredMenuItems = [] this.request = {} - this.getWorkspaces() + this.workspaces = [] + this.renderComponent() } render () { @@ -111,7 +112,10 @@ module.exports = class Filepanel extends ViewPlugin { const result = new Promise((resolve, reject) => { const workspacesPath = this._deps.fileProviders.workspace.workspacesPath this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => { - if (error) return reject(error) + if (error) { + console.error(error) + return reject(error) + } resolve(Object.keys(items) .filter((item) => items[item].isDirectory) .map((folder) => folder.replace(workspacesPath + '/', ''))) diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 4218873078..48101c7564 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -73,9 +73,17 @@ export const Workspace = (props: WorkspaceProps) => { useEffect(() => { const getWorkspaces = async () => { if (props.workspaces && Array.isArray(props.workspaces)) { - setState(prevState => { - return { ...prevState, workspaces: props.workspaces } - }) + if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) { + props.workspace.setWorkspace(props.workspaces[0]) + setState(prevState => { + return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.workspaces[0] } + }) + } else { + props.workspace.clearWorkspace() + setState(prevState => { + return { ...prevState, workspaces: props.workspaces, currentWorkspace: NO_WORKSPACE } + }) + } } } @@ -302,10 +310,7 @@ export const Workspace = (props: WorkspaceProps) => { console.error(error) } } - setWorkspace('default_workspace') - } else { - // we've already got some workspaces - setWorkspace(NO_WORKSPACE) + props.plugin.getWorkspaces() } }) } @@ -420,7 +425,7 @@ export const Workspace = (props: WorkspaceProps) => { }) } - + { state.workspaces.length <= 0 && }
From 532a96334d7eb5c15ba734ed8cb07237ea81e04a Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 25 Feb 2021 20:46:18 +0100 Subject: [PATCH 09/26] Refactor modals --- apps/remix-ide/src/app/panels/file-panel.js | 52 +++- .../workspace/src/lib/remix-ui-workspace.tsx | 246 ++++++------------ 2 files changed, 125 insertions(+), 173 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index fe6b08f7d4..ad7d468a80 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -4,6 +4,7 @@ import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line import ReactDOM from 'react-dom' import { Workspace } from '@remix-ui/workspace' // eslint-disable-line +import ethutil from 'ethereumjs-util' var EventManager = require('../../lib/events') var { RemixdHandle } = require('../files/remixd-handle.js') var { GitHandle } = require('../files/git-handle.js') @@ -61,7 +62,7 @@ module.exports = class Filepanel extends ViewPlugin { this.registeredMenuItems = [] this.request = {} this.workspaces = [] - this.renderComponent() + this.initWorkspace() } render () { @@ -70,7 +71,7 @@ module.exports = class Filepanel extends ViewPlugin { renderComponent() { ReactDOM.render( - - , this.el) + , this.el) } /** @@ -126,6 +126,45 @@ module.exports = class Filepanel extends ViewPlugin { return this.workspaces } + async initWorkspace () { + const queryParams = new QueryParams() + const gistHandler = new GistHandler() + const workspacesPath = this._deps.fileProviders.workspace.workspacesPath + const params = queryParams.get() + // get the file from gist + const loadedFromGist = gistHandler.loadFromGist(params, this._deps.fileManager) + + if (loadedFromGist) return + if (params.code) { + try { + await this._deps.fileManager.createWorkspace('code-sample') + var hash = ethutil.bufferToHex(ethutil.keccak(params.code)) + const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' + const path = 'browser/' + workspacesPath + '/code-sample/' + fileName + await this._deps.fileManager.writeFile(path, atob(params.code)) + this.setWorkspace({ name: 'code-sample', isLocalhost: false }) + await this._deps.fileManager.openFile(path) + } catch (e) { + console.error(e) + } + return + } + // insert example contracts if there are no files to show + this._deps.fileProviders.browser.resolveDirectory('/', async (error, filesList) => { + if (error) console.error(error) + if (Object.keys(filesList).length === 0) { + for (const file in examples) { + try { + await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + examples[file].name, examples[file].content) + } catch (error) { + console.error(error) + } + } + } + this.getWorkspaces() + }) + } + async createNewFile () { return await this.request.createNewFile() } @@ -146,6 +185,7 @@ module.exports = class Filepanel extends ViewPlugin { } else if (await this.call('manager', 'isActive', 'remixd')) { this.call('manager', 'deactivatePlugin', 'remixd') } + this.setWorkspaceName = workspace.name this.emit('setWorkspace', workspace) } diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 48101c7564..23c4b368b0 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect, useRef } from 'react'; -import ethutil from 'ethereumjs-util' import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line import './remix-ui-workspace.css'; import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line @@ -18,12 +17,11 @@ export interface WorkspaceProps { localhost: any // localhost provider fileManager : any examples: CodeExamples, - queryParams: any // URL parameters - gistHandler: any // handle load gist registry: any // registry plugin: any // plugin call and resetFocus request: any // api request, workspaces: any, + setWorkspaceName: string, registeredMenuItems: [] // menu items } @@ -68,8 +66,6 @@ export const Workspace = (props: WorkspaceProps) => { return state.currentWorkspace } - useEffect(() => { initWorkspace() }, []) - useEffect(() => { const getWorkspaces = async () => { if (props.workspaces && Array.isArray(props.workspaces)) { @@ -90,6 +86,12 @@ export const Workspace = (props: WorkspaceProps) => { getWorkspaces() }, [props.workspaces]) + useEffect(() => { + if (props.setWorkspaceName && (props.setWorkspaceName !== state.currentWorkspace)) { + setWorkspace(props.setWorkspaceName) + } + }, [props.setWorkspaceName]) + const [state, setState] = useState({ workspaces: [], reset: false, @@ -98,41 +100,10 @@ export const Workspace = (props: WorkspaceProps) => { displayNewFile: false, externalUploads: null, uploadFileEvent: null, - renameModal: { - id: 'renameWorkspace', - hide: true, - title: 'Rename Workspace', - message: 'Please choose a name for the workspace', - ok: { - label: '', - fn: () => {} - }, - cancel: { - label: '', - fn: () => {} - }, - handleHide: null - }, - deleteModal: { - id: 'deleteWorkspace', - hide: true, - title: 'Remove Workspace', - message: 'Please confirm workspace deletion', - ok: { - label: '', - fn: () => {} - }, - cancel: { - label: '', - fn: () => {} - }, - handleHide: null - }, - createModal: { - id: 'createWorkspace', + modal: { hide: true, - title: 'Create Workspace', - message: 'Please choose a name for the workspace', + title: '', + message: null, ok: { label: '', fn: () => {} @@ -148,77 +119,32 @@ export const Workspace = (props: WorkspaceProps) => { /* workspace creation, renaming and deletion */ const renameCurrentWorkspace = () => { - setState(prevState => { - return { - ...prevState, - renameModal: { - ...prevState.renameModal, - hide: false, - ok: { - label: '', - fn: () => onFinishRenameWorkspace() - }, - cancel: { - label: '', - fn: () => {} - }, - handleHide: () => { - setState(prevState => { - return { ...prevState, renameModal: {...prevState.renameModal, hide: true } } - }) - } - }, - } + modal('Rename Workspace', renameModalMessage(), { + label: 'OK', + fn: onFinishRenameWorkspace + }, { + label: '', + fn: () => {} }) } const createWorkspace = () => { - setState(prevState => { - return { - ...prevState, - createModal: { - ...prevState.createModal, - hide: false, - ok: { - label: '', - fn: () => onFinishCreateWorkspace() - }, - cancel: { - label: '', - fn: () => {} - }, - handleHide: () => { - setState(prevState => { - return { ...prevState, createModal: {...prevState.createModal, hide: true } } - }) - } - } - } + modal('Create Workspace', createModalMessage(), { + label: 'OK', + fn: onFinishCreateWorkspace + }, { + label: '', + fn: () => {} }) } const deleteCurrentWorkspace = () => { - setState(prevState => { - return { - ...prevState, - deleteModal: { - ...prevState.deleteModal, - hide: false, - ok: { - label: '', - fn: () => onFinishDeleteWorkspace() - }, - cancel: { - label: '', - fn: () => {} - }, - handleHide: () => { - setState(prevState => { - return { ...prevState, deleteModal: {...prevState.deleteModal, hide: true } } - }) - } - }, - } + modal('Remove Workspace', 'Please choose a name for the workspace', { + label: 'OK', + fn: onFinishDeleteWorkspace + }, { + label: '', + fn: () => {} }) } @@ -236,7 +162,7 @@ export const Workspace = (props: WorkspaceProps) => { } const onFinishCreateWorkspace = async () => { - if (workspaceCreateInput.current === undefined) return + if (!workspaceCreateInput.current) return // @ts-ignore: Object is possibly 'null'. const workspaceName = workspaceCreateInput.current.value const workspacesPath = props.workspace.workspacesPath @@ -279,42 +205,6 @@ export const Workspace = (props: WorkspaceProps) => { props.setWorkspace({ name, isLocalhost: name === LOCALHOST }) } - const initWorkspace = async () => { - const workspacesPath = props.workspace.workspacesPath - const params = props.queryParams.get() - // get the file from gist - const loadedFromGist = props.gistHandler.loadFromGist(params, props.fileManager) - if (loadedFromGist) return - if (params.code) { - try { - await props.fileManager.createWorkspace('code-sample') - var hash = ethutil.bufferToHex(ethutil.keccak(params.code)) - const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' - const path = 'browser/' + workspacesPath + '/code-sample/' + fileName - await props.fileManager.writeFile(path, atob(params.code)) - setWorkspace('code-sample') - await props.fileManager.openFile(path) - } catch (e) { - console.error(e) - } - return - } - // insert example contracts if there are no files to show - props.browser.resolveDirectory('/', async (error, filesList) => { - if (error) console.error(error) - if (Object.keys(filesList).length === 0) { - for (const file in props.examples) { - try { - await props.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + props.examples[file].name, props.examples[file].content) - } catch (error) { - console.error(error) - } - } - props.plugin.getWorkspaces() - } - }) - } - const remixdExplorer = { hide: () => { if (state.currentWorkspace === LOCALHOST) setWorkspace(NO_WORKSPACE) @@ -344,37 +234,59 @@ export const Workspace = (props: WorkspaceProps) => { props.localhost.event.register('closed', (event) => { remixdExplorer.hide() }) + + const handleHideModal = () => { + setState(prevState => { + return { ...prevState, modal: { ...state.modal, hide: true } } + }) + } + + const modal = async (title: string, message: string | JSX.Element, ok: { label: string, fn: () => void }, cancel: { label: string, fn: () => void }) => { + await setState(prevState => { + return { + ...prevState, + modal: { + ...prevState.modal, + hide: false, + message, + title, + ok, + cancel, + handleHide: handleHideModal + } + } + }) + } + + const createModalMessage = () => { + return ( + <> + { state.modal.message } + + + ) + } + + const renameModalMessage = () => { + return ( + <> + { state.modal.message } + + + ) + } return (
- - { state.renameModal.message } - - - - { state.createModal.message } - - - + + { (typeof state.modal.message !== 'string') && state.modal.message }
resetFocus(true)}>
From 5c0a1b200e075e2424b40e410c3a1d347407866c Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Fri, 26 Feb 2021 17:04:56 +0100 Subject: [PATCH 10/26] Fix workspace creation and switching --- apps/remix-ide/src/app/panels/file-panel.js | 3 +- .../workspace/src/lib/remix-ui-workspace.tsx | 34 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index ad7d468a80..ffb37f3b2f 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -85,7 +85,6 @@ module.exports = class Filepanel extends ViewPlugin { request={this.request} examples={examples} workspaces={this.workspaces} - setWorkspaceName={this.setWorkspaceName} registeredMenuItems={this.registeredMenuItems} /> , this.el) @@ -111,6 +110,7 @@ module.exports = class Filepanel extends ViewPlugin { async getWorkspaces () { const result = new Promise((resolve, reject) => { const workspacesPath = this._deps.fileProviders.workspace.workspacesPath + this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => { if (error) { console.error(error) @@ -185,7 +185,6 @@ module.exports = class Filepanel extends ViewPlugin { } else if (await this.call('manager', 'isActive', 'remixd')) { this.call('manager', 'deactivatePlugin', 'remixd') } - this.setWorkspaceName = workspace.name this.emit('setWorkspace', workspace) } diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 23c4b368b0..10b663ee75 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -21,7 +21,6 @@ export interface WorkspaceProps { plugin: any // plugin call and resetFocus request: any // api request, workspaces: any, - setWorkspaceName: string, registeredMenuItems: [] // menu items } @@ -48,10 +47,6 @@ export const Workspace = (props: WorkspaceProps) => { return createWorkspace() } - // props.request.getWorkspaces = () => { - // return getWorkspaces() - // } - props.request.createNewFile = () => { props.plugin.resetNewFile() } @@ -75,9 +70,8 @@ export const Workspace = (props: WorkspaceProps) => { return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.workspaces[0] } }) } else { - props.workspace.clearWorkspace() setState(prevState => { - return { ...prevState, workspaces: props.workspaces, currentWorkspace: NO_WORKSPACE } + return { ...prevState, workspaces: props.workspaces } }) } } @@ -86,12 +80,6 @@ export const Workspace = (props: WorkspaceProps) => { getWorkspaces() }, [props.workspaces]) - useEffect(() => { - if (props.setWorkspaceName && (props.setWorkspaceName !== state.currentWorkspace)) { - setWorkspace(props.setWorkspaceName) - } - }, [props.setWorkspaceName]) - const [state, setState] = useState({ workspaces: [], reset: false, @@ -162,16 +150,20 @@ export const Workspace = (props: WorkspaceProps) => { } const onFinishCreateWorkspace = async () => { - if (!workspaceCreateInput.current) return + if (workspaceCreateInput.current === undefined) return // @ts-ignore: Object is possibly 'null'. const workspaceName = workspaceCreateInput.current.value const workspacesPath = props.workspace.workspacesPath + props.browser.createDir(workspacesPath + '/' + workspaceName, async () => { - setWorkspace(workspaceName) for (const file in props.examples) { - await props.fileManager.writeFile(`${props.examples[file].name}`, props.examples[file].content) + try { + await props.fileManager.writeFile('browser/' + workspacesPath + '/' + workspaceName + '/' + props.examples[file].name, props.examples[file].content) + } catch (error) { + console.error(error) + } } - props.workspaceCreated({ name: state.currentWorkspace }) + props.plugin.getWorkspaces() }) } @@ -237,7 +229,7 @@ export const Workspace = (props: WorkspaceProps) => { const handleHideModal = () => { setState(prevState => { - return { ...prevState, modal: { ...state.modal, hide: true } } + return { ...prevState, modal: { ...state.modal, hide: true, message: null } } }) } @@ -275,6 +267,12 @@ export const Workspace = (props: WorkspaceProps) => { ) } + + // const handleWorkspaceSelect = (e) => { + // const value = e.target.value + + // setWorkspace(value) + // } return (
From b4b6d61bd5cb9cb539f74c56caeabf0a53bf82e5 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 10:19:11 +0100 Subject: [PATCH 11/26] setWorkspace upon creation --- libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 10b663ee75..45e3cd7976 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -156,14 +156,14 @@ export const Workspace = (props: WorkspaceProps) => { const workspacesPath = props.workspace.workspacesPath props.browser.createDir(workspacesPath + '/' + workspaceName, async () => { + await setWorkspace(workspaceName) for (const file in props.examples) { try { - await props.fileManager.writeFile('browser/' + workspacesPath + '/' + workspaceName + '/' + props.examples[file].name, props.examples[file].content) + await props.fileManager.writeFile(props.examples[file].name, props.examples[file].content) } catch (error) { console.error(error) } } - props.plugin.getWorkspaces() }) } From d722a23ef94840cea0be09eaec2f6212ff03fc6f Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 10:39:14 +0100 Subject: [PATCH 12/26] move createWorkspace to filePanel.js --- apps/remix-ide/src/app/files/fileManager.js | 6 ++++ apps/remix-ide/src/app/panels/file-panel.js | 24 ++++++++------- .../workspace/src/lib/remix-ui-workspace.tsx | 29 ++++++++++++------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index a9bbc771da..cdd78ebf60 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -589,6 +589,12 @@ class FileManager extends Plugin { if (!this.exists(workspaceRootPath)) await this.mkdir(workspaceRootPath) if (!this.exists(workspacePath)) await this.mkdir(workspacePath) } + + async workspaceExists (name) { + const workspaceProvider = this._deps.filesProviders.workspace + const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name + return this.exists(workspacePath) + } } module.exports = FileManager diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index ffb37f3b2f..2ea2771a4c 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -72,6 +72,7 @@ module.exports = class Filepanel extends ViewPlugin { renderComponent() { ReactDOM.render( { if (error) console.error(error) if (Object.keys(filesList).length === 0) { - for (const file in examples) { - try { - await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + examples[file].name, examples[file].content) - } catch (error) { - console.error(error) - } - } + await this.createWorkspace('default_workspace') } this.getWorkspaces() }) @@ -173,9 +168,18 @@ module.exports = class Filepanel extends ViewPlugin { return await this.request.uploadFile() } - async createWorkspace () { - return await this.request.createWorkspace() - } + async createWorkspace (workspaceName) { + if (await this._deps.fileManager.workspaceExists(workspaceName)) throw new Error('workspace already exists') + const workspacesPath = this._deps.fileProviders.workspace.workspacesPath + await this._deps.fileManager.createWorkspace(workspaceName) + for (const file in examples) { + try { + await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/' + workspaceName + '/' + examples[file].name, examples[file].content) + } catch (error) { + console.error(error) + } + } + } /** these are called by the react component, action is already finished whent it's called */ async setWorkspace (workspace) { diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 45e3cd7976..27bcdd3e00 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -9,6 +9,7 @@ type CodeExamples = { /* eslint-disable-next-line */ export interface WorkspaceProps { setWorkspace: ({ name: string, isLocalhost: boolean }) => void, + createWorkspace: (name: string) => void, workspaceRenamed: ({ name: string }) => void, workspaceCreated: ({ name: string }) => void, workspaceDeleted: ({ name: string }) => void, @@ -136,6 +137,16 @@ export const Workspace = (props: WorkspaceProps) => { }) } + const modalMessage = (title: string, body: string) => { + modal(title, body, { + label: 'OK', + fn: () => {} + }, { + label: null, + fn: null + }) + } + const workspaceRenameInput = useRef() const workspaceCreateInput = useRef() @@ -153,18 +164,14 @@ export const Workspace = (props: WorkspaceProps) => { if (workspaceCreateInput.current === undefined) return // @ts-ignore: Object is possibly 'null'. const workspaceName = workspaceCreateInput.current.value - const workspacesPath = props.workspace.workspacesPath - props.browser.createDir(workspacesPath + '/' + workspaceName, async () => { - await setWorkspace(workspaceName) - for (const file in props.examples) { - try { - await props.fileManager.writeFile(props.examples[file].name, props.examples[file].content) - } catch (error) { - console.error(error) - } - } - }) + try { + await props.createWorkspace(workspaceName) + } catch (e) { + modalMessage('Workspace Creation', e.message) + console.error(e) + } + await setWorkspace(workspaceName) } const onFinishDeleteWorkspace = async () => { From c8b5a93b630b0c6c36064c2fb65ce01a5df667f3 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 10:41:09 +0100 Subject: [PATCH 13/26] linting --- apps/remix-ide/src/app/files/fileManager.js | 2 +- apps/remix-ide/src/app/panels/file-panel.js | 34 ++++++++++----------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index cdd78ebf60..32d4ed96c4 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -593,7 +593,7 @@ class FileManager extends Plugin { async workspaceExists (name) { const workspaceProvider = this._deps.filesProviders.workspace const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name - return this.exists(workspacePath) + return this.exists(workspacePath) } } diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 2ea2771a4c..2c228010b8 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -46,17 +46,17 @@ const profile = { module.exports = class Filepanel extends ViewPlugin { constructor (appManager) { super(profile) - this.event = new EventManager() + this.event = new EventManager() this._components = {} this._components.registry = globalRegistry this._deps = { fileProviders: this._components.registry.get('fileproviders').api, fileManager: this._components.registry.get('filemanager').api } - + this.el = document.createElement('div') this.el.setAttribute('id', 'fileExplorerView') - + this.remixdHandle = new RemixdHandle(this.remixdExplorer, this._deps.fileProviders.localhost, appManager) this.gitHandle = new GitHandle() this.registeredMenuItems = [] @@ -69,7 +69,7 @@ module.exports = class Filepanel extends ViewPlugin { return this.el } - renderComponent() { + renderComponent () { ReactDOM.render( void - */ - registerContextMenuItem (item) { - if (!item) throw new Error('Invalid register context menu argument') - if (!item.name || !item.id) throw new Error('Item name and id is mandatory') - if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided') + /** + * @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] } + * @param callback (...args) => void + */ + registerContextMenuItem (item) { + if (!item) throw new Error('Invalid register context menu argument') + if (!item.name || !item.id) throw new Error('Item name and id is mandatory') + if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided') this.registeredMenuItems = [...this.registeredMenuItems, item] this.renderComponent() @@ -166,12 +166,12 @@ module.exports = class Filepanel extends ViewPlugin { async uploadFile () { return await this.request.uploadFile() - } + } async createWorkspace (workspaceName) { if (await this._deps.fileManager.workspaceExists(workspaceName)) throw new Error('workspace already exists') const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - await this._deps.fileManager.createWorkspace(workspaceName) + await this._deps.fileManager.createWorkspace(workspaceName) for (const file in examples) { try { await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/' + workspaceName + '/' + examples[file].name, examples[file].content) @@ -179,7 +179,7 @@ module.exports = class Filepanel extends ViewPlugin { console.error(error) } } - } + } /** these are called by the react component, action is already finished whent it's called */ async setWorkspace (workspace) { @@ -191,7 +191,7 @@ module.exports = class Filepanel extends ViewPlugin { } this.emit('setWorkspace', workspace) } - + workspaceRenamed (workspace) { this.emit('renameWorkspace', workspace) } @@ -203,5 +203,5 @@ module.exports = class Filepanel extends ViewPlugin { workspaceCreated (workspace) { this.emit('createWorkspace', workspace) } - /** end section */ + /** end section */ } From 45bb2bbb9b04e8e8e375157e41d4320d32d6a823 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 12:04:30 +0100 Subject: [PATCH 14/26] add initialWorkspace property to react component --- apps/remix-ide/src/app/panels/file-panel.js | 10 ++++++---- .../workspace/src/lib/remix-ui-workspace.tsx | 12 +++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 2c228010b8..942ea684af 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -62,10 +62,11 @@ module.exports = class Filepanel extends ViewPlugin { this.registeredMenuItems = [] this.request = {} this.workspaces = [] - this.initWorkspace() + this.initialWorkspace = null } render () { + this.initWorkspace().then(() => this.getWorkspaces()).catch(console.error) return this.el } @@ -84,9 +85,9 @@ module.exports = class Filepanel extends ViewPlugin { registry={this._components.registry} plugin={this} request={this.request} - examples={examples} workspaces={this.workspaces} registeredMenuItems={this.registeredMenuItems} + initialWorkspace={this.initialWorkspace} /> , this.el) } @@ -139,12 +140,13 @@ module.exports = class Filepanel extends ViewPlugin { if (params.code) { try { await this._deps.fileManager.createWorkspace('code-sample') + this._deps.fileProviders.workspace.setWorkspace('code-sample') var hash = ethutil.bufferToHex(ethutil.keccak(params.code)) const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' const path = 'browser/' + workspacesPath + '/code-sample/' + fileName await this._deps.fileManager.writeFile(path, atob(params.code)) - this.setWorkspace({ name: 'code-sample', isLocalhost: false }) - await this._deps.fileManager.openFile(path) + this.initialWorkspace = 'code-sample' + await this._deps.fileManager.openFile(fileName) } catch (e) { console.error(e) } diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 27bcdd3e00..f39433ff9a 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -3,9 +3,6 @@ import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line import './remix-ui-workspace.css'; import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line -type CodeExamples = { - [key: string]: { name: string, content: string } -}; /* eslint-disable-next-line */ export interface WorkspaceProps { setWorkspace: ({ name: string, isLocalhost: boolean }) => void, @@ -17,12 +14,12 @@ export interface WorkspaceProps { browser: any // browser provider localhost: any // localhost provider fileManager : any - examples: CodeExamples, registry: any // registry plugin: any // plugin call and resetFocus request: any // api request, workspaces: any, registeredMenuItems: [] // menu items + initialWorkspace: string } var canUpload = window.File || window.FileReader || window.FileList || window.Blob @@ -65,7 +62,12 @@ export const Workspace = (props: WorkspaceProps) => { useEffect(() => { const getWorkspaces = async () => { if (props.workspaces && Array.isArray(props.workspaces)) { - if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) { + if (props.initialWorkspace) { + props.workspace.setWorkspace(props.initialWorkspace) + setState(prevState => { + return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.initialWorkspace } + }) + } else if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) { props.workspace.setWorkspace(props.workspaces[0]) setState(prevState => { return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.workspaces[0] } From 11fab5fb57f7cc99dfdcaf5076960fd77db9edd1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 12:04:57 +0100 Subject: [PATCH 15/26] fix ethutil reference --- apps/remix-ide/src/app/panels/file-panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 942ea684af..dbd1a48533 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -4,7 +4,7 @@ import * as packageJson from '../../../../../package.json' import React from 'react' // eslint-disable-line import ReactDOM from 'react-dom' import { Workspace } from '@remix-ui/workspace' // eslint-disable-line -import ethutil from 'ethereumjs-util' +import * as ethutil from 'ethereumjs-util' var EventManager = require('../../lib/events') var { RemixdHandle } = require('../files/remixd-handle.js') var { GitHandle } = require('../files/git-handle.js') From 2a1593582042d48d952c4de66f100149b541e441 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 12:05:29 +0100 Subject: [PATCH 16/26] activate home in a single call --- apps/remix-ide/src/app.js | 3 ++- apps/remix-ide/src/app/files/fileManager.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 52c194e04a..7502e3541e 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -444,7 +444,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org await appManager.activatePlugin(['contentImport', 'theme', 'editor', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) await appManager.activatePlugin(['mainPanel', 'menuicons']) await appManager.activatePlugin(['sidePanel']) // activating host plugin separately - await appManager.activatePlugin(['home', 'hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'terminal', 'fetchAndCompile']) + await appManager.activatePlugin(['home']) + await appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'fileExplorers', 'settings', 'contextualListener', 'terminal', 'fetchAndCompile']) const queryParams = new QueryParams() const params = queryParams.get() diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index 32d4ed96c4..6f971f3331 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -49,6 +49,10 @@ class FileManager extends Plugin { this.init() } + getOpenedFiles () { + return this.openedFiles + } + setMode (mode) { this.mode = mode } From c9f8962a828ea55b861bb30ae85eb57634515ccd Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 12:06:22 +0100 Subject: [PATCH 17/26] linting --- apps/remix-ide/src/app/panels/file-panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index dbd1a48533..15bf3777a8 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -66,7 +66,7 @@ module.exports = class Filepanel extends ViewPlugin { } render () { - this.initWorkspace().then(() => this.getWorkspaces()).catch(console.error) + this.initWorkspace().then(() => this.getWorkspaces()).catch(console.error) return this.el } From 748d8b3e5869e86f982baf6366e4056481c5b3b2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 12:35:51 +0100 Subject: [PATCH 18/26] fix e2e tests, targeting the modal actions --- apps/remix-ide-e2e/src/commands/removeFile.ts | 2 +- apps/remix-ide-e2e/src/tests/fileExplorer.test.ts | 9 ++++----- apps/remix-ide-e2e/src/tests/gist.test.ts | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/remix-ide-e2e/src/commands/removeFile.ts b/apps/remix-ide-e2e/src/commands/removeFile.ts index 91ba3d2216..d8e129ccdf 100644 --- a/apps/remix-ide-e2e/src/commands/removeFile.ts +++ b/apps/remix-ide-e2e/src/commands/removeFile.ts @@ -39,7 +39,7 @@ function removeFile (browser: NightwatchBrowser, path: string, done: VoidFunctio .pause(2000) .perform(() => { console.log(path, 'to remove') - browser.waitForElementVisible('.modal-ok') + browser.waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .click('.modal-ok') .waitForElementNotPresent('[data-path="' + path + '"]') done() diff --git a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts index 93b7d8d055..744dd8c620 100644 --- a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts +++ b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts @@ -43,8 +43,7 @@ module.exports = { .rightClick('[data-path="5_Renamed_Contract.sol"]') .click('*[id="menuitemdelete"]') .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') - .pause(2000) - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"') }, @@ -75,7 +74,7 @@ module.exports = { .click('*[id="menuitemdelete"]') .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') .pause(2000) - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemBrowser_E2E_Tests"]') }, @@ -88,11 +87,11 @@ module.exports = { .click('*[data-id="fileExplorerNewFilepublishToGist"]') .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') .pause(2000) - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .pause(2000) .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') .pause(2000) - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .pause(2000) .perform((done) => { if (runtimeBrowser === 'chrome') { diff --git a/apps/remix-ide-e2e/src/tests/gist.test.ts b/apps/remix-ide-e2e/src/tests/gist.test.ts index 4b6e191ada..4ebcb17e22 100644 --- a/apps/remix-ide-e2e/src/tests/gist.test.ts +++ b/apps/remix-ide-e2e/src/tests/gist.test.ts @@ -38,7 +38,7 @@ module.exports = { .click('*[data-id="fileExplorerNewFilepublishToGist"]') .pause(2000) .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .pause(10000) .getText('[data-id="default_workspaceModalDialogModalBody-react"]', (result) => { console.log(result) @@ -99,7 +99,7 @@ module.exports = { .click('*[data-id="fileExplorerNewFilepublishToGist"]') .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') .pause(2000) - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .pause(10000) .getText('[data-id="default_workspaceModalDialogModalBody-react"]', (result) => { browser.assert.ok(result.value === 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.', 'Assert failed. Gist token error message not displayed.') From 8b7fd69f431b2f2bf7ccce22b066d026f073acba Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 13:26:59 +0100 Subject: [PATCH 19/26] fix e2e --- apps/remix-ide-e2e/src/tests/fileExplorer.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts index 744dd8c620..91bda71ee9 100644 --- a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts +++ b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts @@ -43,7 +43,7 @@ module.exports = { .rightClick('[data-path="5_Renamed_Contract.sol"]') .click('*[id="menuitemdelete"]') .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') - .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') + .click('*[data-id="default_workspace-modal-footer-ok-react"]') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"') }, From 6993c90dc594e9d662e3f4479f249ee864eafd09 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 15:05:56 +0100 Subject: [PATCH 20/26] fix remixd provider --- .../remix-ide/src/app/files/remixDProvider.js | 6 +++++ apps/remix-ide/src/app/files/remixd-handle.js | 8 ++---- apps/remix-ide/src/app/panels/file-panel.js | 4 +-- .../workspace/src/lib/remix-ui-workspace.tsx | 26 ++++++++----------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/apps/remix-ide/src/app/files/remixDProvider.js b/apps/remix-ide/src/app/files/remixDProvider.js index 5f01a4673f..5c8963baac 100644 --- a/apps/remix-ide/src/app/files/remixDProvider.js +++ b/apps/remix-ide/src/app/files/remixDProvider.js @@ -54,6 +54,11 @@ module.exports = class RemixDProvider { close (cb) { this._isReady = false cb() + this.event.trigger('disconnected') + } + + preInit () { + this._registerEvent() } init (cb) { @@ -63,6 +68,7 @@ module.exports = class RemixDProvider { this._isReady = true this._readOnlyMode = result this._registerEvent() + this.event.trigger('connected') cb && cb() }).catch((error) => { cb && cb(error) diff --git a/apps/remix-ide/src/app/files/remixd-handle.js b/apps/remix-ide/src/app/files/remixd-handle.js index e65c9c404d..b068c0475d 100644 --- a/apps/remix-ide/src/app/files/remixd-handle.js +++ b/apps/remix-ide/src/app/files/remixd-handle.js @@ -30,15 +30,13 @@ const profile = { } export class RemixdHandle extends WebsocketPlugin { - constructor (fileSystemExplorer, locahostProvider, appManager) { + constructor (locahostProvider, appManager) { super(profile) - this.fileSystemExplorer = fileSystemExplorer this.locahostProvider = locahostProvider this.appManager = appManager } deactivate () { - this.fileSystemExplorer.hide() if (super.socket) super.deactivate() this.call('manager', 'deactivatePlugin', 'git') this.locahostProvider.close((error) => { @@ -82,9 +80,7 @@ export class RemixdHandle extends WebsocketPlugin { this.canceled() } }, 3000) - this.locahostProvider.init(() => { - this.fileSystemExplorer.show() - }) + this.locahostProvider.init(() => {}) this.call('manager', 'activatePlugin', 'git') } } diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 15bf3777a8..8bf74a6756 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -57,7 +57,7 @@ module.exports = class Filepanel extends ViewPlugin { this.el = document.createElement('div') this.el.setAttribute('id', 'fileExplorerView') - this.remixdHandle = new RemixdHandle(this.remixdExplorer, this._deps.fileProviders.localhost, appManager) + this.remixdHandle = new RemixdHandle(this._deps.fileProviders.localhost, appManager) this.gitHandle = new GitHandle() this.registeredMenuItems = [] this.request = {} @@ -106,7 +106,7 @@ module.exports = class Filepanel extends ViewPlugin { } async getCurrentWorkspace () { - return await this.request.getWorkspaces() + return await this.request.getCurrentWorkspace() } async getWorkspaces () { diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index f39433ff9a..fe8a2befbd 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -83,6 +83,16 @@ export const Workspace = (props: WorkspaceProps) => { getWorkspaces() }, [props.workspaces]) + useEffect(() => { + props.localhost.event.register('connected', (event) => { + remixdExplorer.show() + }) + + props.localhost.event.register('disconnected', (event) => { + remixdExplorer.hide() + }) + }, []) + const [state, setState] = useState({ workspaces: [], reset: false, @@ -222,20 +232,6 @@ export const Workspace = (props: WorkspaceProps) => { } } - props.localhost.event.register('connecting', (event) => {}) - - props.localhost.event.register('connected', (event) => { - remixdExplorer.show() - }) - - props.localhost.event.register('errored', (event) => { - remixdExplorer.hide() - }) - - props.localhost.event.register('closed', (event) => { - remixdExplorer.hide() - }) - const handleHideModal = () => { setState(prevState => { return { ...prevState, modal: { ...state.modal, hide: true, message: null } } @@ -352,7 +348,7 @@ export const Workspace = (props: WorkspaceProps) => {
- { state.hideRemixdExplorer && state.currentWorkspace && state.currentWorkspace !== NO_WORKSPACE && + { state.hideRemixdExplorer && state.currentWorkspace && state.currentWorkspace !== NO_WORKSPACE && state.currentWorkspace !== LOCALHOST && Date: Mon, 1 Mar 2021 15:18:36 +0100 Subject: [PATCH 21/26] fix e2e test --- apps/remix-ide-e2e/src/commands/removeFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/commands/removeFile.ts b/apps/remix-ide-e2e/src/commands/removeFile.ts index d8e129ccdf..6afe3a8a1a 100644 --- a/apps/remix-ide-e2e/src/commands/removeFile.ts +++ b/apps/remix-ide-e2e/src/commands/removeFile.ts @@ -40,7 +40,7 @@ function removeFile (browser: NightwatchBrowser, path: string, done: VoidFunctio .perform(() => { console.log(path, 'to remove') browser.waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') - .click('.modal-ok') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .waitForElementNotPresent('[data-path="' + path + '"]') done() }) From ba744311ba7cf5c76fdf24aca8fb5f3f6f151418 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 15:36:53 +0100 Subject: [PATCH 22/26] fix e2e --- apps/remix-ide-e2e/src/commands/removeFile.ts | 10 +++++----- apps/remix-ide-e2e/src/tests/fileExplorer.test.ts | 3 ++- apps/remix-ide-e2e/src/tests/remixd.test.ts | 2 +- apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts | 2 +- apps/remix-ide-e2e/src/types/index.d.ts | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/remix-ide-e2e/src/commands/removeFile.ts b/apps/remix-ide-e2e/src/commands/removeFile.ts index 6afe3a8a1a..8f0e2c9ee2 100644 --- a/apps/remix-ide-e2e/src/commands/removeFile.ts +++ b/apps/remix-ide-e2e/src/commands/removeFile.ts @@ -3,9 +3,9 @@ import { NightwatchBrowser } from 'nightwatch' const EventEmitter = require('events') class RemoveFile extends EventEmitter { - command (this: NightwatchBrowser, path: string): NightwatchBrowser { + command (this: NightwatchBrowser, path: string, workspace: string): NightwatchBrowser { this.api.perform((done) => { - removeFile(this.api, path, () => { + removeFile(this.api, path, workspace, () => { done() this.emit('complete') }) @@ -14,7 +14,7 @@ class RemoveFile extends EventEmitter { } } -function removeFile (browser: NightwatchBrowser, path: string, done: VoidFunction) { +function removeFile (browser: NightwatchBrowser, path: string, workspace: string, done: VoidFunction) { browser.execute(function (path) { function contextMenuClick (element) { const evt = element.ownerDocument.createEvent('MouseEvents') @@ -39,8 +39,8 @@ function removeFile (browser: NightwatchBrowser, path: string, done: VoidFunctio .pause(2000) .perform(() => { console.log(path, 'to remove') - browser.waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') - .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') + browser.waitForElementVisible('*[data-id="' + workspace + 'ModalDialogContainer-react"] .modal-ok') + .click('*[data-id="' + workspace + 'ModalDialogContainer-react"] .modal-ok') .waitForElementNotPresent('[data-path="' + path + '"]') done() }) diff --git a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts index 91bda71ee9..562a7cba6e 100644 --- a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts +++ b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts @@ -43,7 +43,8 @@ module.exports = { .rightClick('[data-path="5_Renamed_Contract.sol"]') .click('*[id="menuitemdelete"]') .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') - .click('*[data-id="default_workspace-modal-footer-ok-react"]') + .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') + .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"') }, diff --git a/apps/remix-ide-e2e/src/tests/remixd.test.ts b/apps/remix-ide-e2e/src/tests/remixd.test.ts index bf72bcb4af..818834b837 100644 --- a/apps/remix-ide-e2e/src/tests/remixd.test.ts +++ b/apps/remix-ide-e2e/src/tests/remixd.test.ts @@ -125,7 +125,7 @@ function runTests (browser: NightwatchBrowser) { .pause(1000) .renamePath('folder1/contract_' + browserName + '.sol', 'renamed_contract_' + browserName + '.sol', 'folder1/renamed_contract_' + browserName + '.sol') .pause(1000) - .removeFile('folder1/contract_' + browserName + '_toremove.sol') + .removeFile('folder1/contract_' + browserName + '_toremove.sol', 'localhost') .perform(function (done) { testImportFromRemixd(browser, () => { done() }) }) diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts index 9ec8c7585d..8ed2635e1d 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts @@ -37,7 +37,7 @@ module.exports = { .clickLaunchIcon('fileExplorers') .pause(10000) .openFile('tests/simple_storage_test.sol') - .removeFile('tests/simple_storage_test.sol') + .removeFile('tests/simple_storage_test.sol', 'default_workspace') }, 'Should run simple unit test `simple_storage_test.sol` ': function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/types/index.d.ts b/apps/remix-ide-e2e/src/types/index.d.ts index aac4cccdfb..a2423ab159 100644 --- a/apps/remix-ide-e2e/src/types/index.d.ts +++ b/apps/remix-ide-e2e/src/types/index.d.ts @@ -41,7 +41,7 @@ declare module "nightwatch" { getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser, verifyCallReturnValue(address: string, checks: string[]): NightwatchBrowser, testEditorValue(testvalue: string): NightwatchBrowser, - removeFile(path: string): NightwatchBrowser, + removeFile(path: string, workspace: string): NightwatchBrowser, switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult) => void): NightwatchBrowser, setupMetamask(passphrase: string, password: string): NightwatchBrowser, signMessage(msg: string, callback: (hash: { value: string }, signature: { value: string }) => void): NightwatchBrowser, From 2378304cd0978a14f4a613deebd19778751a43d8 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 15:51:48 +0100 Subject: [PATCH 23/26] fix e2e test --- apps/remix-ide-e2e/src/tests/workspace.test.ts | 2 ++ libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index cc6eb3d99b..a186c5ced8 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -18,6 +18,8 @@ module.exports = { 'Editor should be focused on the 3_Ballot.sol': function (browser: NightwatchBrowser) { browser .pause(5000) + .refresh() + .pause(2000) .getEditorValue((content) => { browser.assert.ok(content.indexOf('contract Ballot {') !== -1, 'content doesn\'t include Ballot contract') }) diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index fe8a2befbd..e6086e7cf5 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -259,7 +259,7 @@ export const Workspace = (props: WorkspaceProps) => { return ( <> { state.modal.message } - + ) } @@ -268,7 +268,7 @@ export const Workspace = (props: WorkspaceProps) => { return ( <> { state.modal.message } - + ) } From 7827a965a4a831f0cccc4032479d66f09e3d0d68 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 16:14:56 +0100 Subject: [PATCH 24/26] fix e2e --- apps/remix-ide-e2e/src/tests/fileExplorer.test.ts | 6 +----- apps/remix-ide-e2e/src/tests/workspace.test.ts | 14 ++++++-------- .../workspace/src/lib/remix-ui-workspace.tsx | 4 ++-- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts index 562a7cba6e..aa54c740d9 100644 --- a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts +++ b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts @@ -40,11 +40,7 @@ module.exports = { 'Should delete file `5_Renamed_Contract.sol` from file explorer': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"]') - .rightClick('[data-path="5_Renamed_Contract.sol"]') - .click('*[id="menuitemdelete"]') - .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') - .waitForElementVisible('*[data-id="default_workspaceModalDialogContainer-react"]') - .click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok') + .removeFile('5_Renamed_Contract.sol', 'default_workspace') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"') }, diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index a186c5ced8..4ce27b356f 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -34,18 +34,16 @@ module.exports = { browser .clickLaunchIcon('fileExplorers') .click('*[data-id="workspaceCreate"]') // create workspace_name - .waitForElementVisible('*[data-id="modalDialogCustomPromptText"]') - .clearValue('*[data-id="modalDialogCustomPromptText"]') - .setValue('*[data-id="modalDialogCustomPromptText"]', 'workspace_name') - .modalFooterOKClick() + .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') + .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name' }) + .click('*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .addFile('test.sol', { content: 'test' }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') .click('*[data-id="workspaceCreate"]') // create workspace_name_1 - .waitForElementVisible('*[data-id="modalDialogCustomPromptText"]') - .clearValue('*[data-id="modalDialogCustomPromptText"]') - .setValue('*[data-id="modalDialogCustomPromptText"]', 'workspace_name_1') - .modalFooterOKClick() + .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') + .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name_1' }) + .click('*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') .click('*[data-id="workspacesSelect"] option[value="workspace_name"]') diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index e6086e7cf5..a23e143a0a 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -259,7 +259,7 @@ export const Workspace = (props: WorkspaceProps) => { return ( <> { state.modal.message } - + ) } @@ -268,7 +268,7 @@ export const Workspace = (props: WorkspaceProps) => { return ( <> { state.modal.message } - + ) } From eaf7ddb0168db14ceba977590261b96709265d45 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 18:44:01 +0100 Subject: [PATCH 25/26] linting --- apps/remix-ide-e2e/src/tests/workspace.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 4ce27b356f..bbfe3919a5 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -35,14 +35,16 @@ module.exports = { .clickLaunchIcon('fileExplorers') .click('*[data-id="workspaceCreate"]') // create workspace_name .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name' }) + // eslint-disable-next-line dot-notation + .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name' }) .click('*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .addFile('test.sol', { content: 'test' }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') .click('*[data-id="workspaceCreate"]') // create workspace_name_1 .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name_1' }) + // eslint-disable-next-line dot-notation + .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name_1' }) .click('*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') From 15135a9a599dc385a11dc14d911cfd211f99f379 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Mar 2021 20:19:09 +0100 Subject: [PATCH 26/26] linting --- apps/remix-ide-e2e/src/tests/workspace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index bbfe3919a5..6ce4c81c60 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -44,7 +44,7 @@ module.exports = { .click('*[data-id="workspaceCreate"]') // create workspace_name_1 .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') // eslint-disable-next-line dot-notation - .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name_1' }) + .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name_1' }) .click('*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]')