Commit 7ad686d1 authored by ioedeveloper's avatar ioedeveloper

Implement file-panel exposed apis

parent 59f23196
......@@ -4,13 +4,11 @@ import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { FileSystemProvider } from '@remix-ui/workspace' // eslint-disable-line
import { checkSpecialChars, checkSlash } from '../../lib/helper'
const { RemixdHandle } = require('../files/remixd-handle.js')
const { GitHandle } = require('../files/git-handle.js')
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 modalDialogCustom = require('../ui/modal-dialog-custom')
/*
Overview of APIs:
......@@ -61,6 +59,7 @@ module.exports = class Filepanel extends ViewPlugin {
this.workspaces = []
this.initialWorkspace = null
this.appManager = appManager
this.workspaceStatus = {}
}
onActivation () {
......@@ -104,33 +103,18 @@ module.exports = class Filepanel extends ViewPlugin {
this.renderComponent()
}
async getCurrentWorkspace () {
return await this.request.getCurrentWorkspace()
getCurrentWorkspace () {
return this.workspaceStatus
}
async getWorkspaces () {
const result = new Promise((resolve, reject) => {
const workspacesPath = this.fileProviders.workspace.workspacesPath
this.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => {
if (error) {
console.error(error)
return reject(error)
}
resolve(Object.keys(items)
.filter((item) => items[item].isDirectory)
.map((folder) => folder.replace(workspacesPath + '/', '')))
})
})
try {
this.workspaces = await result
} catch (e) {
modalDialogCustom.alert('Workspaces have not been created on your system. Please use "Migrate old filesystem to workspace" on the home page to transfer your files or start by creating a new workspace in the File Explorers.')
console.log(e)
}
this.renderComponent()
getWorkspaces () {
return this.workspaces
}
setWorkspaces (workspaces) {
this.worspaces = workspaces
}
async createNewFile () {
const provider = this.fileManager.currentFileProvider()
const dir = provider.workspace || '/'
......@@ -145,66 +129,18 @@ module.exports = class Filepanel extends ViewPlugin {
return this.emit('uploadFileEvent', dir, target)
}
async processCreateWorkspace (name) {
const workspaceProvider = this.fileProviders.workspace
const browserProvider = this.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)
async createWorkspace (workspaceName) {
this.emit('createWorkspace', workspaceName)
}
if (!workspaceRootPathExists) browserProvider.createDir(workspaceRootPath)
if (!workspacePathExists) browserProvider.createDir(workspacePath)
async renameWorkspace (oldName, workspaceName) {
this.emit('renameWorkspace', oldName, workspaceName)
}
async workspaceExists (name) {
setWorkspace (workspace) {
const workspaceProvider = this.fileProviders.workspace
const browserProvider = this.fileProviders.browser
const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name
return browserProvider.exists(workspacePath)
}
async createWorkspace (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 this.workspaceExists(workspaceName)) throw new Error('workspace already exists')
else {
const workspaceProvider = this.fileProviders.workspace
await this.processCreateWorkspace(workspaceName)
workspaceProvider.setWorkspace(workspaceName)
await this.request.setWorkspace(workspaceName) // tells the react component to switch to that workspace
if (setDefaults) {
for (const file in examples) {
try {
await workspaceProvider.set(examples[file].name, examples[file].content)
} catch (error) {
console.error(error)
}
}
}
}
}
async renameWorkspace (oldName, workspaceName) {
if (!workspaceName) throw new Error('name cannot be empty')
if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed')
if (await this.workspaceExists(workspaceName)) throw new Error('workspace already exists')
const browserProvider = this.fileProviders.browser
const workspacesPath = this.fileProviders.workspace.workspacesPath
browserProvider.rename('browser/' + workspacesPath + '/' + oldName, 'browser/' + workspacesPath + '/' + workspaceName, true)
}
/** these are called by the react component, action is already finished whent it's called */
async setWorkspace (workspace, setEvent = true) {
if (workspace.isLocalhost) {
this.call('manager', 'activatePlugin', 'remixd')
} else if (await this.call('manager', 'isActive', 'remixd')) {
this.call('manager', 'deactivatePlugin', 'remixd')
}
if (setEvent) {
this.fileManager.setMode(workspace.isLocalhost ? 'localhost' : 'browser')
this.emit('setWorkspace', workspace)
}
this.workspaceStatus = { name: workspace.name, isLocalhost: workspace.isLocalhost, absolutePath: `${workspaceProvider.workspacesPath}/${workspace.name}` }
}
workspaceDeleted (workspace) {
......
......@@ -110,7 +110,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
<input
className="form-control"
onChange={e => setName(e.target.value)}
value={ name}
value={ name || '' }
id="plugin-name"
data-id="localPluginName"
placeholder="Should be camelCase" />
......@@ -120,7 +120,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
<input
className="form-control"
onChange={e => setDisplayName(e.target.value)}
value={ displayName }
value={ displayName || '' }
id="plugin-displayname"
data-id="localPluginDisplayName"
placeholder="Name in the header" />
......@@ -130,7 +130,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
<input
className="form-control"
onChange={e => setMethods(e.target.value)}
value={ methods }
value={ methods || '' }
id="plugin-methods"
data-id="localPluginMethods"
placeholder="Methods" />
......@@ -140,7 +140,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
<input
className="form-control"
onChange={e => setCanactivate(e.target.value)}
value={ canactivate }
value={ canactivate || '' }
id="plugin-canactivate"
data-id="localPluginCanActivate"
placeholder="Plugin names" />
......@@ -151,7 +151,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
<input
className="form-control"
onChange={e => setUrl(e.target.value)}
value={ url }
value={ url || '' }
id="plugin-url"
data-id="localPluginUrl"
placeholder="ex: https://localhost:8000" />
......
import { extractParentFromKey } from '@remix-ui/helper'
import React from 'react'
import { displayNotification, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, rootFolderChangedSuccess } from './payload'
import { addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace'
const queuedEvents = []
const pendingEvents = {}
const LOCALHOST = ' - connect to localhost - '
let plugin, dispatch: React.Dispatch<any>
export const listenOnEvents = (filePanelPlugin, provider) => async (reducerDispatch: React.Dispatch<any>) => {
plugin = filePanelPlugin
dispatch = reducerDispatch
provider.event.on('fileAdded', async (filePath: string) => {
await executeEvent('fileAdded', filePath)
})
provider.event.on('folderAdded', async (folderPath: string) => {
if (folderPath.indexOf('/.workspaces') === 0) return
await executeEvent('folderAdded', folderPath)
})
provider.event.on('fileRemoved', async (removePath: string) => {
await executeEvent('fileRemoved', removePath)
})
provider.event.on('fileRenamed', async (oldPath: string, newPath: string) => {
await executeEvent('fileRenamed', oldPath, newPath)
})
plugin.on('remixd', 'rootFolderChanged', async (path: string) => {
await executeEvent('rootFolderChanged', path)
})
// provider.event.on('disconnected', () => {
// dispatch(setMode('browser'))
// })
provider.event.on('connected', async () => {
fetchWorkspaceDirectory('/')
// setState(prevState => {
// return { ...prevState, hideRemixdExplorer: false, loadingLocalhost: false }
// })
})
provider.event.on('disconnected', async () => {
const workspaceProvider = plugin.fileProviders.workspace
await switchToWorkspace(workspaceProvider.workspace)
})
provider.event.on('loadingLocalhost', async () => {
await switchToWorkspace(LOCALHOST)
// setState(prevState => {
// return { ...prevState, loadingLocalhost: true }
// })
})
provider.event.on('fileExternallyChanged', async (path: string, file: { content: string }) => {
const config = plugin.registry.get('config').api
const editor = plugin.registry.get('editor').api
if (config.get('currentFile') === path && editor.currentContent() !== file.content) {
if (provider.isReadOnly(path)) return editor.setText(file.content)
dispatch(displayNotification(
path + ' changed',
'This file has been changed outside of Remix IDE.',
'Replace by the new content', 'Keep the content displayed in Remix',
() => {
editor.setText(file.content)
}
))
}
})
provider.event.on('fileRenamedError', async () => {
dispatch(displayNotification('File Renamed Failed', '', 'Ok', 'Cancel'))
})
plugin.on('filePanel', 'displayNewFileInput', (path) => {
addInputField('file', path)
})
plugin.on('filePanel', 'uploadFileEvent', (dir: string, target) => {
uploadFile(target, dir)
})
provider.event.on('createWorkspace', (name: string) => {
createWorkspace(name)
})
plugin.on('filePanel', 'createWorkspace', (name: string) => {
createWorkspace(name)
})
plugin.on('filePanel', 'renameWorkspace', (oldName: string, workspaceName: string) => {
renameWorkspace(oldName, workspaceName)
})
}
const fileAdded = async (filePath: string) => {
await dispatch(fileAddedSuccess(filePath))
if (filePath.includes('_test.sol')) {
plugin.emit('newTestFileCreated', filePath)
}
}
const folderAdded = async (folderPath: string) => {
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(folderPath) || provider.workspace || provider.type || ''
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
promise.then((files) => {
dispatch(folderAddedSuccess(path, files))
}).catch((error) => {
console.error(error)
})
return promise
}
const fileRemoved = async (removePath: string) => {
await dispatch(fileRemovedSuccess(removePath))
}
const fileRenamed = async (oldPath: string) => {
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(oldPath) || provider.workspace || provider.type || ''
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
promise.then((files) => {
dispatch(fileRenamedSuccess(path, oldPath, files))
}).catch((error) => {
console.error(error)
})
}
const rootFolderChanged = async (path) => {
await dispatch(rootFolderChangedSuccess(path))
}
const executeEvent = async (eventName: 'fileAdded' | 'folderAdded' | 'fileRemoved' | 'fileRenamed' | 'rootFolderChanged', ...args) => {
if (Object.keys(pendingEvents).length) {
return queuedEvents.push({ eventName, path: args[0] })
}
pendingEvents[eventName + args[0]] = { eventName, path: args[0] }
switch (eventName) {
case 'fileAdded':
await fileAdded(args[0])
delete pendingEvents[eventName + args[0]]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'folderAdded':
await folderAdded(args[0])
delete pendingEvents[eventName + args[0]]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'fileRemoved':
await fileRemoved(args[0])
delete pendingEvents[eventName + args[0]]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'fileRenamed':
await fileRenamed(args[0])
delete pendingEvents[eventName + args[0]]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
case 'rootFolderChanged':
await rootFolderChanged(args[0])
delete pendingEvents[eventName + args[0]]
if (queuedEvents.length) {
const next = queuedEvents.pop()
await executeEvent(next.eventName, next.path)
}
break
}
}
export const setCurrentWorkspace = (workspace: string) => {
return {
type: 'SET_CURRENT_WORKSPACE',
payload: workspace
}
}
export const setWorkspaces = (workspaces: string[]) => {
return {
type: 'SET_WORKSPACES',
payload: workspaces
}
}
export const setMode = (mode: 'browser' | 'localhost') => {
return {
type: 'SET_MODE',
payload: mode
}
}
export const fetchDirectoryError = (error: any) => {
return {
type: 'FETCH_DIRECTORY_ERROR',
payload: error
}
}
export const fetchDirectoryRequest = (promise: Promise<any>) => {
return {
type: 'FETCH_DIRECTORY_REQUEST',
payload: promise
}
}
export const fetchDirectorySuccess = (path: string, fileTree) => {
return {
type: 'FETCH_DIRECTORY_SUCCESS',
payload: { path, fileTree }
}
}
export const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void) => {
return {
type: 'DISPLAY_NOTIFICATION',
payload: { title, message, labelOk, labelCancel, actionOk, actionCancel }
}
}
export const hideNotification = () => {
return {
type: 'HIDE_NOTIFICATION'
}
}
export const fileAddedSuccess = (filePath: string) => {
return {
type: 'FILE_ADDED_SUCCESS',
payload: filePath
}
}
export const folderAddedSuccess = (folderPath: string, fileTree) => {
return {
type: 'FOLDER_ADDED_SUCCESS',
payload: { path: folderPath, fileTree }
}
}
export const fileRemovedSuccess = (removePath: string) => {
return {
type: 'FILE_REMOVED_SUCCESS',
payload: removePath
}
}
export const fileRenamedSuccess = (path: string, oldPath: string, fileTree) => {
return {
type: 'FILE_RENAMED_SUCCESS',
payload: { path, oldPath, fileTree }
}
}
export const rootFolderChangedSuccess = (path: string) => {
return {
type: 'ROOT_FOLDER_CHANGED',
payload: path
}
}
export const addInputFieldSuccess = (path: string, fileTree, type: 'file' | 'folder' | 'gist') => {
return {
type: 'ADD_INPUT_FIELD',
payload: { path, fileTree, type }
}
}
export const removeInputFieldSuccess = (path: string, fileTree) => {
return {
type: 'REMOVE_INPUT_FIELD',
payload: { path, fileTree }
}
}
export const setReadOnlyMode = (mode: boolean) => {
return {
type: 'SET_READ_ONLY_MODE',
payload: mode
}
}
export const createWorkspaceError = (error: any) => {
return {
type: 'CREATE_WORKSPACE_ERROR',
payload: error
}
}
export const createWorkspaceRequest = (promise: Promise<any>) => {
return {
type: 'CREATE_WORKSPACE_REQUEST',
payload: promise
}
}
export const createWorkspaceSuccess = (workspaceName: string) => {
return {
type: 'CREATE_WORKSPACE_SUCCESS',
payload: workspaceName
}
}
export const fetchWorkspaceDirectoryError = (error: any) => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_ERROR',
payload: error
}
}
export const fetchWorkspaceDirectoryRequest = (promise: Promise<any>) => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_REQUEST',
payload: promise
}
}
export const fetchWorkspaceDirectorySuccess = (path: string, fileTree) => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_SUCCESS',
payload: { path, fileTree }
}
}
export const setRenameWorkspace = (oldName: string, workspaceName: string) => {
return {
type: 'RENAME_WORKSPACE',
payload: { oldName, workspaceName }
}
}
export const setDeleteWorkspace = (workspaceName: string) => {
return {
type: 'DELETE_WORKSPACE',
payload: workspaceName
}
}
export const displayPopUp = (message: string) => {
return {
type: 'DISPLAY_POPUP_MESSAGE',
payload: message
}
}
export const hidePopUp = () => {
return {
type: 'HIDE_POPUP_MESSAGE'
}
}
export const focusElement = (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => {
return {
type: 'SET_FOCUS_ELEMENT',
payload: elements
}
}
......@@ -32,83 +32,83 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
}
const dispatchFetchDirectory = async (path: string) => {
await fetchDirectory(path)(fsDispatch)
await fetchDirectory(path)
}
const dispatchAddInputField = async (path: string, type: 'file' | 'folder') => {
await addInputField(type, path)(fsDispatch)
await addInputField(type, path)
}
const dispatchRemoveInputField = async (path: string) => {
await removeInputField(path)(fsDispatch)
await removeInputField(path)
}
const dispatchCreateWorkspace = async (workspaceName: string) => {
await createWorkspace(workspaceName)(fsDispatch)
await createWorkspace(workspaceName)
}
const dispatchFetchWorkspaceDirectory = async (path: string) => {
await fetchWorkspaceDirectory(path)(fsDispatch)
await fetchWorkspaceDirectory(path)
}
const dispatchSwitchToWorkspace = async (name: string) => {
await switchToWorkspace(name)(fsDispatch)
await switchToWorkspace(name)
}
const dispatchRenameWorkspace = async (oldName: string, workspaceName: string) => {
await renameWorkspace(oldName, workspaceName)(fsDispatch)
await renameWorkspace(oldName, workspaceName)
}
const dispatchDeleteWorkspace = async (workspaceName: string) => {
await deleteWorkspace(workspaceName)(fsDispatch)
await deleteWorkspace(workspaceName)
}
const dispatchPublishToGist = async (path?: string, type?: string) => {
await publishToGist(path, type)(fsDispatch)
await publishToGist(path, type)
}
const dispatchUploadFile = async (target?: SyntheticEvent, targetFolder?: string) => {
await uploadFile(target, targetFolder)(fsDispatch)
await uploadFile(target, targetFolder)
}
const dispatchCreateNewFile = async (path: string, rootDir: string) => {
await createNewFile(path, rootDir)(fsDispatch)
await createNewFile(path, rootDir)
}
const dispatchSetFocusElement = async (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => {
await setFocusElement(elements)(fsDispatch)
await setFocusElement(elements)
}
const dispatchCreateNewFolder = async (path: string, rootDir: string) => {
await createNewFolder(path, rootDir)(fsDispatch)
await createNewFolder(path, rootDir)
}
const dispatchDeletePath = async (path: string[]) => {
await deletePath(path)(fsDispatch)
await deletePath(path)
}
const dispatchRenamePath = async (oldPath: string, newPath: string) => {
await renamePath(oldPath, newPath)(fsDispatch)
await renamePath(oldPath, newPath)
}
const dispatchCopyFile = async (src: string, dest: string) => {
await copyFile(src, dest)(fsDispatch)
await copyFile(src, dest)
}
const dispatchCopyFolder = async (src: string, dest: string) => {
await copyFolder(src, dest)(fsDispatch)
await copyFolder(src, dest)
}
const dispatchRunScript = async (path: string) => {
await runScript(path)(fsDispatch)
await runScript(path)
}
const dispatchEmitContextMenuEvent = async (cmd: customAction) => {
await emitContextMenuEvent(cmd)()
await emitContextMenuEvent(cmd)
}
const dispatchHandleClickFile = async (path: string, type: 'file' | 'folder' | 'gist') => {
await handleClickFile(path, type)(fsDispatch)
await handleClickFile(path, type)
}
useEffect(() => {
......@@ -171,7 +171,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
const handleToaster = () => {
setFocusToaster('')
clearPopUp()(fsDispatch)
clearPopUp()
}
const toast = (toasterMsg: string) => {
......
......@@ -42,17 +42,6 @@ export function Workspace (props: WorkspaceProps) {
}
}, [global.fs.browser.workspaces])
/* implement an external API, consumed by the parent */
props.plugin.request.createWorkspace = () => {
return createWorkspace()
}
props.plugin.request.getCurrentWorkspace = () => {
return { name: currentWorkspace, isLocalhost: currentWorkspace === LOCALHOST, absolutePath: `${props.plugin.workspace.workspacesPath}/${currentWorkspace}` }
}
/* workspace creation, renaming and deletion */
const renameCurrentWorkspace = () => {
global.modal('Rename Current Workspace', renameModalMessage(), 'OK', onFinishRenameWorkspace, '')
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment