Manage workspaces from reducer

pull/5370/head
ioedeveloper 3 years ago
parent a796a74870
commit d04113c19d
  1. 71
      apps/remix-ide/src/app/panels/file-panel.js
  2. 101
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  3. 14
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  4. 115
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

@ -4,7 +4,6 @@ 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 { bufferToHex, keccakFromString } from 'ethereumjs-util'
import { checkSpecialChars, checkSlash } from '../../lib/helper'
const { RemixdHandle } = require('../files/remixd-handle.js')
const { GitHandle } = require('../files/git-handle.js')
@ -12,8 +11,6 @@ const { HardhatHandle } = require('../files/hardhat-handle.js')
const { SlitherHandle } = require('../files/slither-handle.js')
const globalRegistry = require('../../global/registry')
const examples = require('../editor/examples')
const GistHandler = require('../../lib/gist-handler')
const QueryParams = require('../../lib/query-params')
const modalDialogCustom = require('../ui/modal-dialog-custom')
/*
Overview of APIs:
@ -67,7 +64,6 @@ module.exports = class Filepanel extends ViewPlugin {
}
render () {
this.on('editor', 'editorMounted', () => this.initWorkspace().then(() => this.getWorkspaces()).catch(console.error))
return this.el
}
@ -149,73 +145,6 @@ module.exports = class Filepanel extends ViewPlugin {
return this.workspaces
}
async initWorkspace () {
this.renderComponent()
const queryParams = new QueryParams()
const gistHandler = new GistHandler()
const params = queryParams.get()
// get the file from gist
let loadedFromGist = false
if (params.gist) {
await this.processCreateWorkspace('gist-sample')
this.fileProviders.workspace.setWorkspace('gist-sample')
this.initialWorkspace = 'gist-sample'
loadedFromGist = gistHandler.loadFromGist(params, this._deps.fileManager)
}
if (loadedFromGist) return
if (params.code || params.url) {
try {
await this.processCreateWorkspace('code-sample')
this.fileProviders.workspace.setWorkspace('code-sample')
let path = ''
let content = ''
if (params.code) {
var hash = bufferToHex(keccakFromString(params.code))
path = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol'
content = atob(params.code)
await this.fileProviders.workspace.set(path, content)
}
if (params.url) {
const data = await this.call('contentImport', 'resolve', params.url)
path = data.cleanUrl
content = data.content
await this.fileProviders.workspace.set(path, content)
}
this.initialWorkspace = 'code-sample'
await this.fileManager.openFile(path)
} catch (e) {
console.error(e)
}
return
}
const self = this
this.appManager.on('manager', 'pluginDeactivated', self.removePluginActions.bind(this))
// insert example contracts if there are no files to show
return new Promise((resolve, reject) => {
this.fileProviders.browser.resolveDirectory('/', async (error, filesList) => {
if (error) return reject(error)
if (Object.keys(filesList).length === 0) {
await this.createWorkspace('default_workspace')
resolve('default_workspace')
} else {
this.fileProviders.browser.resolveDirectory('.workspaces', async (error, filesList) => {
if (error) return reject(error)
if (Object.keys(filesList).length > 0) {
const workspacePath = Object.keys(filesList)[0].split('/').filter(val => val)
const workspaceName = workspacePath[workspacePath.length - 1]
this.fileProviders.workspace.setWorkspace(workspaceName)
return resolve(workspaceName)
}
return reject(new Error('Can\'t find available workspace.'))
})
}
})
})
}
async createNewFile () {
return await this.request.createNewFile()
}

@ -6,13 +6,55 @@ const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-para
const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples')
let plugin
export const setCurrentWorkspace = (workspace: string) => {
const setCurrentWorkspace = (workspace: string) => {
return {
type: 'SET_CURRENT_WORKSPACE',
payload: workspace
}
}
const processCreateWorkspace = async (name: string) => {
const workspaceProvider = plugin.fileProviders.workspace
const browserProvider = plugin.fileProviders.browser
const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name
const workspaceRootPath = 'browser/' + workspaceProvider.workspacesPath
const workspaceRootPathExists = await browserProvider.exists(workspaceRootPath)
const workspacePathExists = await browserProvider.exists(workspacePath)
if (!workspaceRootPathExists) browserProvider.createDir(workspaceRootPath)
if (!workspacePathExists) browserProvider.createDir(workspacePath)
workspaceProvider.setWorkspace(name)
}
const createWorkspace = async (workspaceName: string, setDefaults = true) => {
if (!workspaceName) throw new Error('workspace name cannot be empty')
if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed')
if (await workspaceExists(workspaceName)) throw new Error('workspace already exists')
else {
const workspaceProvider = plugin.fileProviders.workspace
await processCreateWorkspace(workspaceName)
if (setDefaults) {
// insert example contracts if there are no files to show
for (const file in examples) {
try {
await workspaceProvider.set(examples[file].name, examples[file].content)
} catch (error) {
console.error(error)
}
}
}
}
}
const workspaceExists = async (name: string) => {
const workspaceProvider = plugin.fileProviders.workspace
const browserProvider = plugin.fileProviders.browser
const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name
return browserProvider.exists(workspacePath)
}
export const initWorkspace = (filePanelPlugin) => async (dispatch: React.Dispatch<any>) => {
plugin = filePanelPlugin
const queryParams = new QueryParams()
@ -66,49 +108,24 @@ export const initWorkspace = (filePanelPlugin) => async (dispatch: React.Dispatc
}
})
})
return dispatch(setCurrentWorkspace(initialWorkspace))
}
const processCreateWorkspace = async (name: string) => {
const workspaceProvider = plugin.fileProviders.workspace
const browserProvider = plugin.fileProviders.browser
const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name
const workspaceRootPath = 'browser/' + workspaceProvider.workspacesPath
const workspaceRootPathExists = await browserProvider.exists(workspaceRootPath)
const workspacePathExists = await browserProvider.exists(workspacePath)
// plugin.fileProviders.localhost.event.off('disconnected', localhostDisconnect)
// plugin.fileProviders.localhost.event.on('disconnected', localhostDisconnect)
// plugin.fileProviders.localhost.event.on('connected', () => {
// remixdExplorer.show()
// setWorkspace(LOCALHOST)
// })
if (!workspaceRootPathExists) browserProvider.createDir(workspaceRootPath)
if (!workspacePathExists) browserProvider.createDir(workspacePath)
plugin.fileProviders.workspace.setWorkspace(name)
}
// plugin.fileProviders.localhost.event.on('disconnected', () => {
// remixdExplorer.hide()
// })
const createWorkspace = async (workspaceName, setDefaults = true) => {
if (!workspaceName) throw new Error('name cannot be empty')
if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed')
if (await workspaceExists(workspaceName)) throw new Error('workspace already exists')
else {
const workspaceProvider = plugin.fileProviders.workspace
// plugin.fileProviders.localhost.event.on('loading', () => {
// remixdExplorer.loading()
// })
await processCreateWorkspace(workspaceName)
workspaceProvider.setWorkspace(workspaceName)
plugin.workspaceName = workspaceName
if (setDefaults) {
// insert example contracts if there are no files to show
for (const file in examples) {
try {
await workspaceProvider.set(examples[file].name, examples[file].content)
} catch (error) {
console.error(error)
}
}
}
}
}
const workspaceExists = async (name) => {
const workspaceProvider = plugin.fileProviders.workspace
const browserProvider = plugin.fileProviders.browser
const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name
return browserProvider.exists(workspacePath)
// plugin.fileProviders.workspace.event.on('createWorkspace', (name) => {
// createNewWorkspace(name)
// })
return dispatch(setCurrentWorkspace(initialWorkspace))
}

@ -2,8 +2,17 @@ interface Action {
type: string
payload: Record<string, any> | string
}
interface State {
browser: {
currentWorkspace: string,
workspaces: string[],
isRequesting: boolean,
isSuccessful: boolean,
error: string
}
}
export const browserInitialState = {
export const browserInitialState: State = {
browser: {
currentWorkspace: '',
workspaces: [],
@ -20,7 +29,8 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
...state,
browser: {
...state.browser,
currentWorkspace: typeof action.payload === 'string' ? action.payload : ''
currentWorkspace: typeof action.payload === 'string' ? action.payload : '',
workspaces: typeof action.payload === 'string' ? state.browser.workspaces.includes(action.payload) ? state.browser.workspaces : [...state.browser.workspaces, action.payload] : state.browser.workspaces
}
}
}

@ -32,6 +32,37 @@ var canUpload = window.File || window.FileReader || window.FileList || window.Bl
export const Workspace = (props: WorkspaceProps) => {
const LOCALHOST = ' - connect to localhost - '
const NO_WORKSPACE = ' - none - '
const [state, setState] = useState({
workspaces: [],
reset: false,
currentWorkspace: NO_WORKSPACE,
hideRemixdExplorer: true,
displayNewFile: false,
externalUploads: null,
uploadFileEvent: null,
modal: {
hide: true,
title: '',
message: null,
okLabel: '',
okFn: () => {},
cancelLabel: '',
cancelFn: () => {},
handleHide: null
},
loadingLocalhost: false,
toasterMsg: ''
})
const [currentWorkspace, setCurrentWorkspace] = useState<string>('')
const [fs, dispatch] = useReducer(browserReducer, browserInitialState)
useEffect(() => {
initWorkspace(props.plugin)(dispatch)
}, [])
useEffect(() => {
if (fs.browser.currentWorkspace) setCurrentWorkspace(fs.browser.currentWorkspace)
}, [fs.browser.currentWorkspace])
/* extends the parent 'plugin' with some function needed by the file explorer */
props.plugin.resetFocus = (reset) => {
@ -74,30 +105,6 @@ export const Workspace = (props: WorkspaceProps) => {
return { name: state.currentWorkspace, isLocalhost: state.currentWorkspace === LOCALHOST, absolutePath: `${props.workspace.workspacesPath}/${state.currentWorkspace}` }
}
useEffect(() => {
let getWorkspaces = async () => {
if (props.workspaces && Array.isArray(props.workspaces)) {
if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) {
const currentWorkspace = props.workspace.getWorkspace() ? props.workspace.getWorkspace() : props.workspaces[0]
await props.workspace.setWorkspace(currentWorkspace)
setState(prevState => {
return { ...prevState, workspaces: props.workspaces, currentWorkspace }
})
} else {
setState(prevState => {
return { ...prevState, workspaces: props.workspaces }
})
}
}
}
getWorkspaces()
return () => {
getWorkspaces = async () => {}
}
}, [props.workspaces])
const localhostDisconnect = () => {
if (state.currentWorkspace === LOCALHOST) setWorkspace(props.workspaces.length > 0 ? props.workspaces[0] : NO_WORKSPACE)
// This should be removed some time after refactoring: https://github.com/ethereum/remix-project/issues/1197
@ -107,38 +114,6 @@ export const Workspace = (props: WorkspaceProps) => {
}
}
// useEffect(() => {
// props.localhost.event.off('disconnected', localhostDisconnect)
// props.localhost.event.on('disconnected', localhostDisconnect)
// props.localhost.event.on('connected', () => {
// remixdExplorer.show()
// setWorkspace(LOCALHOST)
// })
// props.localhost.event.on('disconnected', () => {
// remixdExplorer.hide()
// })
// props.localhost.event.on('loading', () => {
// remixdExplorer.loading()
// })
// props.workspace.event.on('createWorkspace', (name) => {
// createNewWorkspace(name)
// })
// if (props.initialWorkspace) {
// props.workspace.setWorkspace(props.initialWorkspace)
// setState(prevState => {
// return { ...prevState, currentWorkspace: props.initialWorkspace }
// })
// }
// }, [])
useEffect(() => {
initWorkspace(props.plugin)(dispatch)
}, [])
const createNewWorkspace = async (workspaceName) => {
try {
await props.fileManager.closeAllFiles()
@ -151,30 +126,6 @@ export const Workspace = (props: WorkspaceProps) => {
}
}
const [state, setState] = useState({
workspaces: [],
reset: false,
currentWorkspace: NO_WORKSPACE,
hideRemixdExplorer: true,
displayNewFile: false,
externalUploads: null,
uploadFileEvent: null,
modal: {
hide: true,
title: '',
message: null,
okLabel: '',
okFn: () => {},
cancelLabel: '',
cancelFn: () => {},
handleHide: null
},
loadingLocalhost: false,
toasterMsg: ''
})
const [fs, dispatch] = useReducer(browserReducer, browserInitialState)
const toast = (message: string) => {
setState(prevState => {
return { ...prevState, toasterMsg: message }
@ -395,9 +346,9 @@ export const Workspace = (props: WorkspaceProps) => {
title='Delete'>
</span>
</span>
<select id="workspacesSelect" value={fs.browser.currentWorkspace} data-id="workspacesSelect" onChange={(e) => setWorkspace(e.target.value)} className="form-control custom-select">
<select id="workspacesSelect" value={currentWorkspace} data-id="workspacesSelect" onChange={(e) => setWorkspace(e.target.value)} className="form-control custom-select">
{
state.workspaces
fs.browser.workspaces
.map((folder, index) => {
return <option key={index} value={folder}>{folder}</option>
})

Loading…
Cancel
Save