From 0881f25d574ddb4de8ed28c7aa08baa761d16dff Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Tue, 20 Jul 2021 03:57:48 +0100 Subject: [PATCH] Initialize workspace in reducer. --- apps/remix-ide/src/app/panels/file-panel.js | 14 -- .../workspace/src/lib/actions/workspace.ts | 112 ++++++++++++++ .../workspace/src/lib/reducers/workspace.ts | 30 ++++ .../workspace/src/lib/remix-ui-workspace.tsx | 137 +++++++++--------- 4 files changed, 211 insertions(+), 82 deletions(-) create mode 100644 libs/remix-ui/workspace/src/lib/actions/workspace.ts create mode 100644 libs/remix-ui/workspace/src/lib/reducers/workspace.ts diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 7b4b933d43..ea71d98c63 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -300,19 +300,5 @@ module.exports = class Filepanel extends ViewPlugin { workspaceCreated (workspace) { this.emit('createWorkspace', workspace) } - - resetFocus (reset) { - // setState(prevState => { - // return { ...prevState, reset } - // }) - } - - resetNewFile () { - // setState(prevState => { - // return { ...prevState, displayNewFile: !state.displayNewFile } - // }) - } - - resetUploadFile = () => {} /** end section */ } diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts new file mode 100644 index 0000000000..a09ec15ea8 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -0,0 +1,112 @@ +import { bufferToHex, keccakFromString } from 'ethereumjs-util' +import { checkSpecialChars, checkSlash } from '../../../../../../apps/remix-ide/src/lib/helper' + +const GistHandler = require('../../../../../../apps/remix-ide/src/lib/gist-handler') +const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params') +const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples') +let plugin + +export const setCurrentWorkspace = (workspace: string) => { + return { + type: 'SET_CURRENT_WORKSPACE', + payload: workspace + } +} + +export const initWorkspace = (filePanelPlugin) => async (dispatch: React.Dispatch) => { + plugin = filePanelPlugin + const queryParams = new QueryParams() + const gistHandler = new GistHandler() + const params = queryParams.get() + // get the file from gist + let loadedFromGist = false + + if (params.gist) { + await processCreateWorkspace('gist-sample') + plugin.initialWorkspace = 'gist-sample' + loadedFromGist = gistHandler.loadFromGist(params, plugin.fileManager) + } + if (loadedFromGist) return + + if (params.code) { + try { + await processCreateWorkspace('code-sample') + const hash = bufferToHex(keccakFromString(params.code)) + const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' + const path = fileName + + await plugin.fileProviders.workspace.set(path, atob(params.code)) + plugin.initialWorkspace = 'code-sample' + await plugin.fileManager.openFile(fileName) + } catch (e) { + console.error(e) + } + return + } + + return new Promise((resolve, reject) => { + plugin.fileProviders.browser.resolveDirectory('/', async (error, filesList) => { + if (error) return reject(error) + if (Object.keys(filesList).length === 0) { + await createWorkspace('default_workspace') + return resolve('default_workspace') + } else { + plugin.fileProviders.browser.resolveDirectory('.workspaces', async (error, filesList) => { + if (error) return reject(error) + if (Object.keys(filesList).length > 0) { + const workspacePath = Object.keys(filesList)[0].split('/').filter(val => val) + const workspaceName = workspacePath[workspacePath.length - 1] + + plugin.fileProviders.workspace.setWorkspace(workspaceName) + return resolve(workspaceName) + } + return reject(new Error('Can\'t find available workspace.')) + }) + } + }) + }) +} + +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) + plugin.fileProviders.workspace.setWorkspace(name) +} + +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 + + 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) +} diff --git a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts new file mode 100644 index 0000000000..bb376b64c5 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts @@ -0,0 +1,30 @@ +interface Action { + type: string + payload: Record | string +} + +export const browserInitialState = { + browser: { + currentWorkspace: '', + workspaces: [], + isRequesting: false, + isSuccessful: false, + error: null + } +} + +export const browserReducer = (state = browserInitialState, action: Action) => { + switch (action.type) { + case 'SET_CURRENT_WORKSPACE': { + return { + ...state, + browser: { + ...state.browser, + currentWorkspace: typeof action.payload === 'string' ? action.payload : '' + } + } + } + default: + throw new Error() + } +} 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 204ef3bdcc..a18d6426c5 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -1,58 +1,53 @@ -import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line +import React, { useState, useEffect, useRef, useReducer } from 'react' // eslint-disable-line 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 import { Toaster } from '@remix-ui/toaster'// eslint-disable-line import { MenuItems } from 'libs/remix-ui/file-explorer/src/lib/types' +import { initWorkspace } from './actions/workspace' +import { browserReducer, browserInitialState } from './reducers/workspace' /* eslint-disable-next-line */ export interface WorkspaceProps { - plugin: { - setWorkspace: ({ name: string, isLocalhost: boolean }, setEvent: boolean) => void, - createWorkspace: (name: string) => void, - renameWorkspace: (oldName: string, newName: 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 - fileManager : any - registry: any // registry - request: any // api request, - workspaces: any, - registeredMenuItems: MenuItems // menu items - removedMenuItems: MenuItems - initialWorkspace: string - } + setWorkspace: ({ name: string, isLocalhost: boolean }, setEvent: boolean) => void, + createWorkspace: (name: string) => void, + renameWorkspace: (oldName: string, newName: 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 + fileManager : any + registry: any // registry + plugin: any // plugin call and resetFocus + request: any // api request, + workspaces: any, + registeredMenuItems: MenuItems // menu items + removedMenuItems: MenuItems + initialWorkspace: string } var canUpload = window.File || window.FileReader || window.FileList || window.Blob export const Workspace = (props: WorkspaceProps) => { - const { - plugin, - plugin: { - setWorkspace, - createWorkspace, - renameWorkspace, - workspaceRenamed, - workspaceCreated, - workspaceDeleted, - workspace, - browser, - localhost, - fileManager, - registry, - request, - workspaces, - registeredMenuItems, - removedMenuItems, - initialWorkspace - } - } = props const LOCALHOST = ' - connect to localhost - ' const NO_WORKSPACE = ' - none - ' + /* extends the parent 'plugin' with some function needed by the file explorer */ + props.plugin.resetFocus = (reset) => { + setState(prevState => { + return { ...prevState, reset } + }) + } + + props.plugin.resetNewFile = () => { + setState(prevState => { + return { ...prevState, displayNewFile: !state.displayNewFile } + }) + } + + props.plugin.resetUploadFile = () => {} + /* implement an external API, consumed by the parent */ props.request.createWorkspace = () => { return createWorkspace() @@ -112,32 +107,36 @@ 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) - }) + // 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 } + // }) + // } + // }, []) - if (props.initialWorkspace) { - props.workspace.setWorkspace(props.initialWorkspace) - setState(prevState => { - return { ...prevState, currentWorkspace: props.initialWorkspace } - }) - } + useEffect(() => { + initWorkspace(props.plugin)(dispatch) }, []) const createNewWorkspace = async (workspaceName) => { @@ -174,6 +173,8 @@ export const Workspace = (props: WorkspaceProps) => { toasterMsg: '' }) + const [fs, dispatch] = useReducer(browserReducer, browserInitialState) + const toast = (message: string) => { setState(prevState => { return { ...prevState, toasterMsg: message } @@ -394,7 +395,7 @@ export const Workspace = (props: WorkspaceProps) => { title='Delete'> - setWorkspace(e.target.value)} className="form-control custom-select"> { state.workspaces .map((folder, index) => { @@ -416,7 +417,7 @@ export const Workspace = (props: WorkspaceProps) => { registry={props.registry} filesProvider={props.workspace} menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']} - plugin={plugin} + plugin={props.plugin} focusRoot={state.reset} contextMenuItems={props.registeredMenuItems} removedContextMenuItems={props.removedMenuItems} @@ -434,7 +435,7 @@ export const Workspace = (props: WorkspaceProps) => { registry={props.registry} filesProvider={props.localhost} menuItems={['createNewFile', 'createNewFolder']} - plugin={plugin} + plugin={props.plugin} focusRoot={state.reset} contextMenuItems={props.registeredMenuItems} removedContextMenuItems={props.removedMenuItems}