Commit 0b6579f4 authored by ioedeveloper's avatar ioedeveloper

Export remaining file-explorer functions to actions

parent 39d47a4a
import React, { useEffect, useState, useRef, useReducer, useContext } from 'react' // eslint-disable-line import React, { useEffect, useState, useRef, useContext } from 'react' // eslint-disable-line
// import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' // eslint-disable-line // import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' // eslint-disable-line
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line
import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint-disable-line import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint-disable-line
import { FileExplorerProps, File, MenuItems, FileExplorerState } from './types' import { FileExplorerProps, File, MenuItems, FileExplorerState } from './types'
import * as helper from '../../../../../apps/remix-ide/src/lib/helper'
import { FileSystemContext } from '@remix-ui/workspace' import { FileSystemContext } from '@remix-ui/workspace'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel'
import { contextMenuActions } from './utils' import { contextMenuActions } from './utils'
import './css/file-explorer.css' import './css/file-explorer.css'
import { extractParentFromKey } from '@remix-ui/helper' import { checkSpecialChars, extractParentFromKey, getPathIcon, joinPath } from '@remix-ui/helper'
export const FileExplorer = (props: FileExplorerProps) => { export const FileExplorer = (props: FileExplorerProps) => {
const { name, focusRoot, contextMenuItems, externalUploads, removedContextMenuItems, resetFocus, files } = props const { name, focusRoot, contextMenuItems, externalUploads, removedContextMenuItems, resetFocus, files } = props
const [state, setState] = useState<FileExplorerState>({ const [state, setState] = useState<FileExplorerState>({
focusElement: [{
key: '',
type: 'folder'
}],
ctrlKey: false, ctrlKey: false,
newFileName: '', newFileName: '',
actions: contextMenuActions, actions: contextMenuActions,
...@@ -69,9 +63,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -69,9 +63,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
useEffect(() => { useEffect(() => {
if (focusRoot) { if (focusRoot) {
setState(prevState => { global.dispatchSetFocusElement([{ key: '', type: 'folder' }])
return { ...prevState, focusElement: [{ key: '', type: 'folder' }] }
})
resetFocus(false) resetFocus(false)
} }
}, [focusRoot]) }, [focusRoot])
...@@ -181,10 +173,10 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -181,10 +173,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const getFocusedFolder = () => { const getFocusedFolder = () => {
if (state.focusElement[0]) { if (global.fs.focusElement[0]) {
if (state.focusElement[0].type === 'folder' && state.focusElement[0].key) return state.focusElement[0].key if (global.fs.focusElement[0].type === 'folder' && global.fs.focusElement[0].key) return global.fs.focusElement[0].key
else if (state.focusElement[0].type === 'gist' && state.focusElement[0].key) return state.focusElement[0].key else if (global.fs.focusElement[0].type === 'gist' && global.fs.focusElement[0].key) return global.fs.focusElement[0].key
else if (state.focusElement[0].type === 'file' && state.focusElement[0].key) return extractParentFromKey(state.focusElement[0].key) ? extractParentFromKey(state.focusElement[0].key) : name else if (global.fs.focusElement[0].type === 'file' && global.fs.focusElement[0].key) return extractParentFromKey(global.fs.focusElement[0].key) ? extractParentFromKey(global.fs.focusElement[0].key) : name
else return name else return name
} }
} }
...@@ -192,62 +184,29 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -192,62 +184,29 @@ export const FileExplorer = (props: FileExplorerProps) => {
const createNewFile = async (newFilePath: string) => { const createNewFile = async (newFilePath: string) => {
try { try {
global.dispatchCreateNewFile(newFilePath, props.name) global.dispatchCreateNewFile(newFilePath, props.name)
// setState(prevState => {
// return { ...prevState, focusElement: [{ key: newName, type: 'file' }] }
// })
} catch (error) { } catch (error) {
return global.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {}) return global.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {})
} }
} }
const createNewFolder = async (newFolderPath: string) => { const createNewFolder = async (newFolderPath: string) => {
const fileManager = state.fileManager
const dirName = newFolderPath + '/'
try { try {
const exists = await fileManager.exists(dirName) global.dispatchCreateNewFolder(newFolderPath, props.name)
if (exists) {
return global.modal('Rename File Failed', `A file or folder ${extractNameFromKey(newFolderPath)} already exists at this location. Please choose a different name.`, 'Close', () => {})
}
await fileManager.mkdir(dirName)
setState(prevState => {
return { ...prevState, focusElement: [{ key: newFolderPath, type: 'folder' }] }
})
} catch (e) { } catch (e) {
return global.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {}) return global.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {})
} }
} }
const deletePath = async (path: string | string[]) => { const deletePath = async (path: string[]) => {
if (global.fs.readonly) return global.toast('cannot delete file. ' + name + ' is a read only explorer') if (global.fs.readonly) return global.toast('cannot delete file. ' + name + ' is a read only explorer')
if (!Array.isArray(path)) path = [path] if (!Array.isArray(path)) path = [path]
global.modal(`Delete ${path.length > 1 ? 'items' : 'item'}`, deleteMessage(path), 'OK', async () => { global.modal(`Delete ${path.length > 1 ? 'items' : 'item'}`, deleteMessage(path), 'OK', () => { global.dispatchDeletePath(path) }, 'Cancel', () => {})
const fileManager = state.fileManager
for (const p of path) {
try {
await fileManager.remove(p)
} catch (e) {
const isDir = await state.fileManager.isDirectory(p)
global.toast(`Failed to remove ${isDir ? 'folder' : 'file'} ${p}.`)
}
}
}, 'Cancel', () => {})
} }
const renamePath = async (oldPath: string, newPath: string) => { const renamePath = async (oldPath: string, newPath: string) => {
try { try {
const fileManager = state.fileManager global.dispatchRenamePath(oldPath, newPath)
const exists = await fileManager.exists(newPath)
if (exists) {
global.modal('Rename File Failed', `A file or folder ${extractNameFromKey(newPath)} already exists at this location. Please choose a different name.`, 'Close', () => {})
} else {
await fileManager.rename(oldPath, newPath)
}
} catch (error) { } catch (error) {
global.modal('Rename File Failed', 'Unexpected error while renaming: ' + typeof error === 'string' ? error : error.message, 'Close', async () => {}) global.modal('Rename File Failed', 'Unexpected error while renaming: ' + typeof error === 'string' ? error : error.message, 'Close', async () => {})
} }
...@@ -264,22 +223,18 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -264,22 +223,18 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const copyFile = (src: string, dest: string) => { const copyFile = (src: string, dest: string) => {
const fileManager = state.fileManager
try { try {
fileManager.copyFile(src, dest) global.dispatchCopyFile(src, dest)
} catch (error) { } catch (error) {
console.log('Oops! An error ocurred while performing copyFile operation.' + error) global.modal('Copy File Failed', 'Unexpected error while copying file: ' + src, 'Close', async () => {})
} }
} }
const copyFolder = (src: string, dest: string) => { const copyFolder = (src: string, dest: string) => {
const fileManager = state.fileManager
try { try {
fileManager.copyDir(src, dest) global.dispatchCopyFolder(src, dest)
} catch (error) { } catch (error) {
console.log('Oops! An error ocurred while performing copyDir operation.' + error) global.modal('Copy Folder Failed', 'Unexpected error while copying folder: ' + src, 'Close', async () => {})
} }
} }
...@@ -304,54 +259,50 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -304,54 +259,50 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
const runScript = async (path: string) => { const runScript = async (path: string) => {
const filesProvider = fileSystem.provider.provider try {
global.dispatchRunScript(path)
filesProvider.get(path, (error, content: string) => { } catch (error) {
if (error) return console.log(error) global.toast('Run script failed')
plugin.call('scriptRunner', 'execute', content) }
})
} }
const emitContextMenuEvent = (cmd: customAction) => { const emitContextMenuEvent = (cmd: customAction) => {
plugin.call(cmd.id, cmd.name, cmd) try {
global.dispatchEmitContextMenuEvent(cmd)
} catch (error) {
global.toast(error)
}
} }
const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => { const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => {
path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path path = path.indexOf(props.name + '/') === 0 ? path.replace(props.name + '/', '') : path
if (!state.ctrlKey) { if (!state.ctrlKey) {
state.fileManager.open(path) global.dispatchHandleClickFile(path, type)
setState(prevState => {
return { ...prevState, focusElement: [{ key: path, type }] }
})
} else { } else {
if (state.focusElement.findIndex(item => item.key === path) !== -1) { if (global.fs.focusElement.findIndex(item => item.key === path) !== -1) {
setState(prevState => { const focusElement = global.fs.focusElement.filter(item => item.key !== path)
return { ...prevState, focusElement: prevState.focusElement.filter(item => item.key !== path) }
}) global.dispatchSetFocusElement(focusElement)
} else { } else {
setState(prevState => { const nonRootFocus = global.fs.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
const nonRootFocus = prevState.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
nonRootFocus.push({ key: path, type }) nonRootFocus.push({ key: path, type })
return { ...prevState, focusElement: nonRootFocus } global.dispatchSetFocusElement(nonRootFocus)
})
} }
} }
} }
const handleClickFolder = async (path: string, type: 'folder' | 'file' | 'gist') => { const handleClickFolder = async (path: string, type: 'folder' | 'file' | 'gist') => {
if (state.ctrlKey) { if (state.ctrlKey) {
if (state.focusElement.findIndex(item => item.key === path) !== -1) { if (global.fs.focusElement.findIndex(item => item.key === path) !== -1) {
setState(prevState => { const focusElement = global.fs.focusElement.filter(item => item.key !== path)
return { ...prevState, focusElement: [...prevState.focusElement.filter(item => item.key !== path)] }
}) global.dispatchSetFocusElement(focusElement)
} else { } else {
setState(prevState => { const nonRootFocus = global.fs.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
const nonRootFocus = prevState.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
nonRootFocus.push({ key: path, type }) nonRootFocus.push({ key: path, type })
return { ...prevState, focusElement: nonRootFocus } global.dispatchSetFocusElement(nonRootFocus)
})
} }
} else { } else {
let expandPath = [] let expandPath = []
...@@ -363,8 +314,9 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -363,8 +314,9 @@ export const FileExplorer = (props: FileExplorerProps) => {
expandPath = [...new Set(state.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(path)))] expandPath = [...new Set(state.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(path)))]
} }
global.dispatchSetFocusElement([{ key: path, type }])
setState(prevState => { setState(prevState => {
return { ...prevState, focusElement: [{ key: path, type }], expandPath } return { ...prevState, expandPath }
}) })
} }
} }
...@@ -419,7 +371,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -419,7 +371,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } } return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
}) })
} }
if (helper.checkSpecialChars(content)) { if (checkSpecialChars(content)) {
global.modal('Validation Error', 'Special characters are not allowed', 'OK', () => {}) global.modal('Validation Error', 'Special characters are not allowed', 'OK', () => {})
} else { } else {
if (state.focusEdit.isNew) { if (state.focusEdit.isNew) {
...@@ -547,11 +499,11 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -547,11 +499,11 @@ export const FileExplorer = (props: FileExplorerProps) => {
const renderFiles = (file: File, index: number) => { const renderFiles = (file: File, index: number) => {
if (!file || !file.path || typeof file === 'string' || typeof file === 'number' || typeof file === 'boolean') return if (!file || !file.path || typeof file === 'string' || typeof file === 'number' || typeof file === 'boolean') return
const labelClass = state.focusEdit.element === file.path const labelClass = state.focusEdit.element === file.path
? 'bg-light' : state.focusElement.findIndex(item => item.key === file.path) !== -1 ? 'bg-light' : global.fs.focusElement.findIndex(item => item.key === file.path) !== -1
? 'bg-secondary' : state.mouseOverElement === file.path ? 'bg-secondary' : state.mouseOverElement === file.path
? 'bg-light border' : (state.focusContext.element === file.path) && (state.focusEdit.element !== file.path) ? 'bg-light border' : (state.focusContext.element === file.path) && (state.focusEdit.element !== file.path)
? 'bg-light border' : '' ? 'bg-light border' : ''
const icon = helper.getPathIcon(file.path) const icon = getPathIcon(file.path)
const spreadProps = { const spreadProps = {
onClick: (e) => e.stopPropagation() onClick: (e) => e.stopPropagation()
} }
...@@ -654,7 +606,6 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -654,7 +606,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
createNewFolder={handleNewFolderInput} createNewFolder={handleNewFolderInput}
publishToGist={publishToGist} publishToGist={publishToGist}
uploadFile={uploadFile} uploadFile={uploadFile}
fileManager={state.fileManager}
/> />
</div> </div>
} }
...@@ -672,7 +623,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -672,7 +623,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
</TreeView> </TreeView>
{ state.showContextMenu && { state.showContextMenu &&
<FileExplorerContextMenu <FileExplorerContextMenu
actions={state.focusElement.length > 1 ? state.actions.filter(item => item.multiselect) : state.actions.filter(item => !item.multiselect)} actions={global.fs.focusElement.length > 1 ? state.actions.filter(item => item.multiselect) : state.actions.filter(item => !item.multiselect)}
hideContextMenu={hideContextMenu} hideContextMenu={hideContextMenu}
createNewFile={handleNewFileInput} createNewFile={handleNewFileInput}
createNewFolder={handleNewFolderInput} createNewFolder={handleNewFolderInput}
...@@ -686,7 +637,7 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -686,7 +637,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
pageY={state.focusContext.y} pageY={state.focusContext.y}
path={state.focusContext.element} path={state.focusContext.element}
type={state.focusContext.type} type={state.focusContext.type}
focus={state.focusElement} focus={global.fs.focusElement}
onMouseOver={(e) => { onMouseOver={(e) => {
e.stopPropagation() e.stopPropagation()
handleMouseOver(state.focusContext.element) handleMouseOver(state.focusContext.element)
...@@ -701,9 +652,3 @@ export const FileExplorer = (props: FileExplorerProps) => { ...@@ -701,9 +652,3 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
export default FileExplorer export default FileExplorer
function joinPath (...paths) {
paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash)
if (paths.length === 1) return paths[0]
return paths.join('/')
}
...@@ -25,7 +25,6 @@ export interface File { ...@@ -25,7 +25,6 @@ export interface File {
export interface FileExplorerMenuProps { export interface FileExplorerMenuProps {
title: string, title: string,
menuItems: string[], menuItems: string[],
fileManager: any,
createNewFile: (folder?: string) => void, createNewFile: (folder?: string) => void,
createNewFolder: (parentFolder?: string) => void, createNewFolder: (parentFolder?: string) => void,
publishToGist: (path?: string) => void, publishToGist: (path?: string) => void,
...@@ -58,10 +57,6 @@ export interface FileExplorerContextMenuProps { ...@@ -58,10 +57,6 @@ export interface FileExplorerContextMenuProps {
} }
export interface FileExplorerState { export interface FileExplorerState {
focusElement: {
key: string
type: 'folder' | 'file' | 'gist'
}[]
ctrlKey: boolean ctrlKey: boolean
newFileName: string newFileName: string
actions: { actions: {
......
...@@ -20,9 +20,9 @@ export const checkSlash = (name: string) => { ...@@ -20,9 +20,9 @@ export const checkSlash = (name: string) => {
return name.match(/\//) != null return name.match(/\//) != null
} }
export const createNonClashingNameAsync = async (name, fileManager, prefix = '') => { export const createNonClashingNameAsync = async (name: string, fileManager, prefix = '') => {
if (!name) name = 'Undefined' if (!name) name = 'Undefined'
let counter let _counter
let ext = 'sol' let ext = 'sol'
const reg = /(.*)\.([^.]+)/g const reg = /(.*)\.([^.]+)/g
const split = reg.exec(name) const split = reg.exec(name)
...@@ -33,11 +33,30 @@ export const createNonClashingNameAsync = async (name, fileManager, prefix = '') ...@@ -33,11 +33,30 @@ export const createNonClashingNameAsync = async (name, fileManager, prefix = '')
let exist = true let exist = true
do { do {
const isDuplicate = await fileManager.exists(name + counter + prefix + '.' + ext) const isDuplicate = await fileManager.exists(name + _counter + prefix + '.' + ext)
if (isDuplicate) counter = (counter | 0) + 1 if (isDuplicate) _counter = (_counter | 0) + 1
else exist = false else exist = false
} while (exist) } while (exist)
const counter = _counter || ''
return name + counter + prefix + '.' + ext return name + counter + prefix + '.' + ext
} }
export const joinPath = (...paths) => {
paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash)
if (paths.length === 1) return paths[0]
return paths.join('/')
}
export const getPathIcon = (path: string) => {
return path.endsWith('.txt')
? 'far fa-file-alt' : path.endsWith('.md')
? 'far fa-file-alt' : path.endsWith('.sol')
? 'fak fa-solidity-mono' : path.endsWith('.js')
? 'fab fa-js' : path.endsWith('.json')
? 'fas fa-brackets-curly' : path.endsWith('.vy')
? 'fak fa-vyper-mono' : path.endsWith('.lex')
? 'fak fa-lexon' : path.endsWith('.contract')
? 'fab fa-ethereum' : 'far fa-file'
}
...@@ -3,6 +3,7 @@ import { bufferToHex, keccakFromString } from 'ethereumjs-util' ...@@ -3,6 +3,7 @@ import { bufferToHex, keccakFromString } from 'ethereumjs-util'
import axios, { AxiosResponse } from 'axios' import axios, { AxiosResponse } from 'axios'
import { checkSpecialChars, checkSlash, extractParentFromKey, extractNameFromKey, createNonClashingNameAsync } from '@remix-ui/helper' import { checkSpecialChars, checkSlash, extractParentFromKey, extractNameFromKey, createNonClashingNameAsync } from '@remix-ui/helper'
import Gists from 'gists' import Gists from 'gists'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel/type'
const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params') const QueryParams = require('../../../../../../apps/remix-ide/src/lib/query-params')
const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples') const examples = require('../../../../../../apps/remix-ide/src/app/editor/examples')
...@@ -194,6 +195,13 @@ const hidePopUp = () => { ...@@ -194,6 +195,13 @@ const hidePopUp = () => {
} }
} }
const focusElement = (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => {
return {
type: 'SET_FOCUS_ELEMENT',
payload: elements
}
}
const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true, template: 'gist-template' | 'code-template' | 'default-template' = 'default-template') => { const createWorkspaceTemplate = async (workspaceName: string, setDefaults = true, template: 'gist-template' | 'code-template' | 'default-template' = 'default-template') => {
if (!workspaceName) throw new Error('workspace name cannot be empty') if (!workspaceName) throw new Error('workspace name cannot be empty')
if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed') if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed')
...@@ -765,9 +773,95 @@ export const createNewFile = (path: string, rootDir: string) => async (dispatch: ...@@ -765,9 +773,95 @@ export const createNewFile = (path: string, rootDir: string) => async (dispatch:
const path = newName.indexOf(rootDir + '/') === 0 ? newName.replace(rootDir + '/', '') : newName const path = newName.indexOf(rootDir + '/') === 0 ? newName.replace(rootDir + '/', '') : newName
await fileManager.open(path) await fileManager.open(path)
setFocusElement([{ key: path, type: 'file' }])(dispatch)
}
}
export const setFocusElement = (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => async (dispatch: React.Dispatch<any>) => {
dispatch(focusElement(elements))
}
export const createNewFolder = (path: string, rootDir: string) => async (dispatch: React.Dispatch<any>) => {
const fileManager = plugin.fileManager
const dirName = path + '/'
const exists = await fileManager.exists(dirName)
if (exists) {
return dispatch(displayNotification('Rename File Failed', `A file or folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => {}))
}
await fileManager.mkdir(dirName)
path = path.indexOf(rootDir + '/') === 0 ? path.replace(rootDir + '/', '') : path
dispatch(focusElement([{ key: path, type: 'folder' }]))
}
export const deletePath = (path: string[]) => async (dispatch: React.Dispatch<any>) => {
const fileManager = plugin.fileManager
for (const p of path) {
try {
await fileManager.remove(p)
} catch (e) {
const isDir = await fileManager.isDirectory(p)
dispatch(displayPopUp(`Failed to remove ${isDir ? 'folder' : 'file'} ${p}.`))
}
}
}
export const renamePath = (oldPath: string, newPath: string) => async (dispatch: React.Dispatch<any>) => {
const fileManager = plugin.fileManager
const exists = await fileManager.exists(newPath)
if (exists) {
dispatch(displayNotification('Rename File Failed', `A file or folder ${extractNameFromKey(newPath)} already exists at this location. Please choose a different name.`, 'Close', null, () => {}))
} else {
await fileManager.rename(oldPath, newPath)
} }
} }
export const copyFile = (src: string, dest: string) => async (dispatch: React.Dispatch<any>) => {
const fileManager = plugin.fileManager
try {
fileManager.copyFile(src, dest)
} catch (error) {
console.log('Oops! An error ocurred while performing copyFile operation.' + error)
dispatch(displayPopUp('Oops! An error ocurred while performing copyFile operation.' + error))
}
}
export const copyFolder = (src: string, dest: string) => async (dispatch: React.Dispatch<any>) => {
const fileManager = plugin.fileManager
try {
fileManager.copyDir(src, dest)
} catch (error) {
console.log('Oops! An error ocurred while performing copyDir operation.' + error)
dispatch(displayPopUp('Oops! An error ocurred while performing copyDir operation.' + error))
}
}
export const runScript = (path: string) => async (dispatch: React.Dispatch<any>) => {
const provider = plugin.fileManager.currentFileProvider()
provider.get(path, (error, content: string) => {
if (error) {
dispatch(displayPopUp(error))
return console.log(error)
}
plugin.call('scriptRunner', 'execute', content)
})
}
export const emitContextMenuEvent = (cmd: customAction) => async () => {
plugin.call(cmd.id, cmd.name, cmd)
}
export const handleClickFile = (path: string, type: 'file' | 'folder' | 'gist') => async (dispatch: React.Dispatch<any>) => {
plugin.fileManager.open(path)
dispatch(focusElement([{ key: path, type }]))
}
const fileAdded = async (filePath: string) => { const fileAdded = async (filePath: string) => {
await dispatch(fileAddedSuccess(filePath)) await dispatch(fileAddedSuccess(filePath))
if (filePath.includes('_test.sol')) { if (filePath.includes('_test.sol')) {
......
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel/type'
import { createContext, SyntheticEvent } from 'react' import { createContext, SyntheticEvent } from 'react'
import { BrowserState } from '../reducers/workspace' import { BrowserState } from '../reducers/workspace'
...@@ -16,5 +17,14 @@ export const FileSystemContext = createContext<{ ...@@ -16,5 +17,14 @@ export const FileSystemContext = createContext<{
dispatchDeleteWorkspace: (workspaceName: string) => Promise<void>, dispatchDeleteWorkspace: (workspaceName: string) => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void>, dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise<void>, dispatchUploadFile: (target?: SyntheticEvent, targetFolder?: string) => Promise<void>,
dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void> dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>,
dispatchSetFocusElement: (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => Promise<void>,
dispatchCreateNewFolder: (path: string, rootDir: string) => Promise<void>,
dispatchDeletePath: (path: string[]) => Promise<void>,
dispatchRenamePath: (oldPath: string, newPath: string) => Promise<void>,
dispatchCopyFile: (src: string, dest: string) => Promise<void>,
dispatchCopyFolder: (src: string, dest: string) => Promise<void>,
dispatchRunScript: (path: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>
}>(null) }>(null)
...@@ -5,10 +5,11 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line ...@@ -5,10 +5,11 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts' import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace' import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, addInputField, removeInputField, createWorkspace, fetchWorkspaceDirectory, switchToWorkspace, renameWorkspace, deleteWorkspace, clearPopUp, publishToGist, uploadFile, createNewFile } from '../actions/workspace' import { initWorkspace, fetchDirectory, addInputField, removeInputField, createWorkspace, fetchWorkspaceDirectory, switchToWorkspace, renameWorkspace, deleteWorkspace, clearPopUp, publishToGist, uploadFile, createNewFile, setFocusElement, createNewFolder, deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile } from '../actions/workspace'
import { Modal, WorkspaceProps } from '../types' import { Modal, WorkspaceProps } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace' import { Workspace } from '../remix-ui-workspace'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel/type'
export const FileSystemProvider = (props: WorkspaceProps) => { export const FileSystemProvider = (props: WorkspaceProps) => {
const { plugin } = props const { plugin } = props
...@@ -74,6 +75,42 @@ export const FileSystemProvider = (props: WorkspaceProps) => { ...@@ -74,6 +75,42 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await createNewFile(path, rootDir)(fsDispatch) await createNewFile(path, rootDir)(fsDispatch)
} }
const dispatchSetFocusElement = async (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => {
await setFocusElement(elements)(fsDispatch)
}
const dispatchCreateNewFolder = async (path: string, rootDir: string) => {
await createNewFolder(path, rootDir)(fsDispatch)
}
const dispatchDeletePath = async (path: string[]) => {
await deletePath(path)(fsDispatch)
}
const dispatchRenamePath = async (oldPath: string, newPath: string) => {
await renamePath(oldPath, newPath)(fsDispatch)
}
const dispatchCopyFile = async (src: string, dest: string) => {
await copyFile(src, dest)(fsDispatch)
}
const dispatchCopyFolder = async (src: string, dest: string) => {
await copyFolder(src, dest)(fsDispatch)
}
const dispatchRunScript = async (path: string) => {
await runScript(path)(fsDispatch)
}
const dispatchEmitContextMenuEvent = async (cmd: customAction) => {
await emitContextMenuEvent(cmd)
}
const dispatchHandleClickFile = async (path: string, type: 'file' | 'folder' | 'gist') => {
await handleClickFile(path, type)
}
useEffect(() => { useEffect(() => {
if (modals.length > 0) { if (modals.length > 0) {
setFocusModal(() => { setFocusModal(() => {
...@@ -159,7 +196,16 @@ export const FileSystemProvider = (props: WorkspaceProps) => { ...@@ -159,7 +196,16 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchDeleteWorkspace, dispatchDeleteWorkspace,
dispatchPublishToGist, dispatchPublishToGist,
dispatchUploadFile, dispatchUploadFile,
dispatchCreateNewFile dispatchCreateNewFile,
dispatchSetFocusElement,
dispatchCreateNewFolder,
dispatchDeletePath,
dispatchRenamePath,
dispatchCopyFile,
dispatchCopyFolder,
dispatchRunScript,
dispatchEmitContextMenuEvent,
dispatchHandleClickFile
} }
return ( return (
<FileSystemContext.Provider value={value}> <FileSystemContext.Provider value={value}>
......
...@@ -33,7 +33,8 @@ export interface BrowserState { ...@@ -33,7 +33,8 @@ export interface BrowserState {
}, },
readonly: boolean, readonly: boolean,
popup: string, popup: string,
focusEdit: string focusEdit: string,
focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[]
} }
export const browserInitialState: BrowserState = { export const browserInitialState: BrowserState = {
...@@ -65,7 +66,8 @@ export const browserInitialState: BrowserState = { ...@@ -65,7 +66,8 @@ export const browserInitialState: BrowserState = {
}, },
readonly: false, readonly: false,
popup: '', popup: '',
focusEdit: '' focusEdit: '',
focusElement: []
} }
export const browserReducer = (state = browserInitialState, action: Action) => { export const browserReducer = (state = browserInitialState, action: Action) => {
...@@ -453,6 +455,15 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...@@ -453,6 +455,15 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
} }
} }
case 'SET_FOCUS_ELEMENT': {
const payload = action.payload as { key: string, type: 'file' | 'folder' | 'gist' }[]
return {
...state,
focusElement: payload
}
}
default: default:
throw new Error() throw new Error()
} }
......
...@@ -45,12 +45,6 @@ export function Workspace (props: WorkspaceProps) { ...@@ -45,12 +45,6 @@ export function Workspace (props: WorkspaceProps) {
} }
}, [global.fs.browser.workspaces]) }, [global.fs.browser.workspaces])
props.plugin.resetNewFile = () => {
setState(prevState => {
return { ...prevState, displayNewFile: !state.displayNewFile }
})
}
/* implement an external API, consumed by the parent */ /* implement an external API, consumed by the parent */
props.plugin.request.createWorkspace = () => { props.plugin.request.createWorkspace = () => {
return createWorkspace() return createWorkspace()
...@@ -73,18 +67,6 @@ export function Workspace (props: WorkspaceProps) { ...@@ -73,18 +67,6 @@ export function Workspace (props: WorkspaceProps) {
return { name: currentWorkspace, isLocalhost: currentWorkspace === LOCALHOST, absolutePath: `${props.plugin.workspace.workspacesPath}/${currentWorkspace}` } return { name: currentWorkspace, isLocalhost: currentWorkspace === LOCALHOST, absolutePath: `${props.plugin.workspace.workspacesPath}/${currentWorkspace}` }
} }
const createNewWorkspace = async (workspaceName) => {
try {
await props.plugin.fileManager.closeAllFiles()
await props.plugin.createWorkspace(workspaceName)
await setWorkspace(workspaceName)
global.toast('New default workspace has been created.')
} catch (e) {
global.modal('Create Default Workspace', e.message, 'OK', onFinishRenameWorkspace, '')
console.error(e)
}
}
/* workspace creation, renaming and deletion */ /* workspace creation, renaming and deletion */
const renameCurrentWorkspace = () => { const renameCurrentWorkspace = () => {
......
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