From dc57090e52924c628234ab7bccdbaf69924d8189 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 bbcec763c859a586ed0fcd6c56a97a16058c97bb 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 c0c99f9f1245f2f70e2de9427d505ade70ad4caf 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 3bd07e9a558c51ebcd55a243f19e321561a0f8fd 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 99196493b462d6ffed31d2ab95c8285e312f3a1e 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 d467f4b331819d91284b7796ddbdeac29bc1bb75 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 c70a5a74a7e245a5172235499bc495388d03a42b 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 0dddd4531264ad99404b1fafa218a7b72a9008bc 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 6ef49df0215639be3426bb3fcba70d75989b191c 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 b3f363144a33fc9932807d90c7e46bc363fdc3d1 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 22ee1cf6a320d313066c3bcc8cd02cfe3aef9210 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 67146f1f9794e5ac8aae11983395c93b0c2f2e23 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 75987af1735e014da9479aa9703a5de505577f8d 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 19668a4456ff4b8d16c22c022a6203b181d640c9 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 dd29373322035778de57efc851df510376ea8549 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 913753f78d7c000e36996311e215a51f14f17b2c 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 2882391bba0b78f208b8e4a10ad408c7e244df96 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 8bb265fd7bde1dc226d3a247db1f145ba3aab68d 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 447745fe349ecbe41fa659912ab98779fd82b308 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 4fe31d7ebd76170b4e6a65fc456f79dcedbea1fc 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 b83bd26c131f68c940e26f7c6dee904bec201ca2 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 0d1954d7d79a2375a134629f5c0b55e6dd13e5d3 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 3d9c0a8655858663bd23dc7eac39ba25dc6d2d58 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 b920e2500bc3af3dd0ec8b52e84f414d6dcbaa8f 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 ce43be0fb1aaf7552bd02d41877fdd952dc38240 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"]')