add & remove input field

pull/1575/head
ioedeveloper 3 years ago
parent 049705d121
commit 22f1dac72b
  1. 17
      libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
  2. 57
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  3. 6
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  4. 14
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  5. 147
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts

@ -6,8 +6,6 @@ import Gists from 'gists'
import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line
import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint-disable-line
import { FileExplorerProps, File, MenuItems, FileExplorerState } from './types'
import { fileSystemReducer, fileSystemInitialState } from './reducers/fileSystem'
import { addInputField, removeInputField } from './actions/fileSystem'
import * as helper from '../../../../../apps/remix-ide/src/lib/helper'
import QueryParams from '../../../../../apps/remix-ide/src/lib/query-params'
import { FileSystemContext } from '@remix-ui/workspace'
@ -49,7 +47,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
copyElement: []
})
const [canPaste, setCanPaste] = useState(false)
const [fileSystem, dispatch] = useReducer(fileSystemReducer, fileSystemInitialState)
const editRef = useRef(null)
const global = useContext(FileSystemContext)
@ -256,7 +253,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
const deletePath = async (path: string | string[]) => {
const filesProvider = fileSystem.provider.provider
// const filesProvider = fileSystem.provider.provider
if (!Array.isArray(path)) path = [path]
for (const p of path) {
if (filesProvider.isReadOnly(p)) {
@ -572,7 +569,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
const editModeOn = (path: string, type: string, isNew: boolean = false) => {
if (fileSystem.provider.provider.isReadOnly(path)) return
if (global.fs.readonly) return
setState(prevState => {
return { ...prevState, focusEdit: { ...prevState.focusEdit, element: path, isNew, type } }
})
@ -584,7 +581,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (!content || (content.trim() === '')) {
if (state.focusEdit.isNew) {
removeInputField(parentFolder)(dispatch)
global.dispatchRemoveInputField(parentFolder)
setState(prevState => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
})
@ -606,11 +603,11 @@ export const FileExplorer = (props: FileExplorerProps) => {
} else {
if (state.focusEdit.isNew) {
if (hasReservedKeyword(content)) {
removeInputField(parentFolder)(dispatch)
global.dispatchRemoveInputField(parentFolder)
global.modal('Reserved Keyword', `File name contains remix reserved keywords. '${content}'`, 'Close', () => {})
} else {
state.focusEdit.type === 'file' ? createNewFile(joinPath(parentFolder, content)) : createNewFolder(joinPath(parentFolder, content))
removeInputField(parentFolder)(dispatch)
global.dispatchRemoveInputField(parentFolder)
}
} else {
if (hasReservedKeyword(content)) {
@ -636,7 +633,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (!parentFolder) parentFolder = getFocusedFolder()
const expandPath = [...new Set([...state.expandPath, parentFolder])]
await addInputField(fileSystem.provider.provider, 'file', parentFolder)(dispatch)
await global.dispatchAddInputField(parentFolder, 'file')
setState(prevState => {
return { ...prevState, expandPath }
})
@ -648,7 +645,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
else if ((parentFolder.indexOf('.sol') !== -1) || (parentFolder.indexOf('.js') !== -1)) parentFolder = extractParentFromKey(parentFolder)
const expandPath = [...new Set([...state.expandPath, parentFolder])]
await addInputField(fileSystem.provider.provider, 'folder', parentFolder)(dispatch)
await global.dispatchAddInputField(parentFolder, 'folder')
setState(prevState => {
return { ...prevState, expandPath }
})

@ -93,6 +93,27 @@ const rootFolderChangedSuccess = (path: string) => {
}
}
const addInputFieldSuccess = (path: string, fileTree, type: 'file' | 'folder' | 'gist') => {
return {
type: 'ADD_INPUT_FIELD',
payload: { path, fileTree, type }
}
}
const removeInputFieldSuccess = (path: string, fileTree) => {
return {
type: 'REMOVE_INPUT_FIELD',
payload: { path, fileTree }
}
}
const setReadOnlyMode = (mode: boolean) => {
return {
type: 'SET_READ_ONLY_MODE',
payload: mode
}
}
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 (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed')
@ -341,6 +362,42 @@ export const fetchDirectory = (path: string) => (dispatch: React.Dispatch<any>)
return promise
}
export const addInputField = (type: 'file' | 'folder', path: string) => (dispatch: React.Dispatch<any>) => {
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
promise.then((files) => {
dispatch(addInputFieldSuccess(path, files, type))
}).catch((error) => {
console.error(error)
})
return promise
}
export const removeInputField = (path: string) => (dispatch: React.Dispatch<any>) => {
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
promise.then((files) => {
dispatch(removeInputFieldSuccess(path, files))
}).catch((error) => {
console.error(error)
})
return promise
}
const fileAdded = async (filePath: string) => {
await dispatch(fileAddedSuccess(filePath))
if (filePath.includes('_test.sol')) {

@ -4,6 +4,8 @@ import { BrowserState } from '../reducers/workspace'
export const FileSystemContext = createContext<{
fs: BrowserState,
modal:(title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
dispatchInitWorkspace:() => void,
dispatchFetchDirectory:(path: string) => void
dispatchInitWorkspace:() => Promise<void>,
dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>,
dispatchRemoveInputField:(path: string) => Promise<void>
}>(null)

@ -4,7 +4,7 @@ import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory } from '../actions/workspace'
import { initWorkspace, fetchDirectory, addInputField, removeInputField } from '../actions/workspace'
import { Modal, WorkspaceProps } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace'
@ -31,6 +31,14 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await fetchDirectory(path)(fsDispatch)
}
const dispatchAddInputField = async (path: string, type: 'file' | 'folder') => {
await addInputField(type, path)(fsDispatch)
}
const dispatchRemoveInputField = async (path: string) => {
await removeInputField(path)(fsDispatch)
}
useEffect(() => {
if (modals.length > 0) {
setFocusModal(() => {
@ -75,7 +83,9 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
fs,
modal,
dispatchInitWorkspace,
dispatchFetchDirectory
dispatchFetchDirectory,
dispatchAddInputField,
dispatchRemoveInputField
}
return (
<FileSystemContext.Provider value={value}>

@ -30,7 +30,8 @@ export interface BrowserState {
actionCancel: (() => void) | null,
labelOk: string,
labelCancel: string
}
},
readonly: boolean
}
export const browserInitialState: BrowserState = {
@ -59,7 +60,8 @@ export const browserInitialState: BrowserState = {
actionCancel: () => {},
labelOk: '',
labelCancel: ''
}
},
readonly: false
}
export const browserReducer = (state = browserInitialState, action: Action) => {
@ -245,44 +247,87 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
}
case 'ADD_INPUT_FIELD': {
const payload = action.payload as { path: string, fileTree, type: 'file' | 'folder' }
return {
...state,
browser: {
...state.browser,
files: state.mode === 'browser' ? fetchDirectoryContent(state, payload, true) : state.browser.files
},
localhost: {
...state.localhost,
files: state.mode === 'localhost' ? fetchDirectoryContent(state, payload, true) : state.localhost.files
}
}
}
case 'REMOVE_INPUT_FIELD': {
const payload = action.payload as { path: string, fileTree }
return {
...state,
browser: {
...state.browser,
files: state.mode === 'browser' ? removeInputField(state, payload) : state.browser.files
},
localhost: {
...state.localhost,
files: state.mode === 'localhost' ? removeInputField(state, payload) : state.localhost.files
}
}
}
case 'SET_READ_ONLY_MODE': {
const payload = action.payload as boolean
return {
...state,
readonly: payload
}
}
default:
throw new Error()
}
}
const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: string }) => {
const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: string, type?: 'file' | 'folder' }) => {
if (state.mode === 'browser') {
if (payload.path === state.browser.currentWorkspace) {
const files = normalize(payload.fileTree)
let files = normalize(payload.fileTree, payload.path, payload.type)
files = _.merge(files, state.browser.files[state.browser.currentWorkspace])
return { [state.browser.currentWorkspace]: files }
} else {
let files = state.browser.files
const _path = splitPath(state, payload.path)
const prevFiles = _.get(files, _path)
prevFiles.child = _.merge(normalize(payload.fileTree), prevFiles.child)
prevFiles.child = _.merge(normalize(payload.fileTree, payload.path, payload.type), prevFiles.child)
files = _.set(files, _path, prevFiles)
return files
}
} else {
if (payload.path === state.mode) {
const files = normalize(payload.fileTree)
let files = normalize(payload.fileTree, payload.path, payload.type)
files = _.merge(files, state[state.mode].files[state.mode])
return { [state.mode]: files }
} else {
let files = state.localhost.files
const _path = splitPath(state, payload.path)
const prevFiles = _.get(files, _path)
prevFiles.child = _.merge(normalize(payload.fileTree), prevFiles.child)
prevFiles.child = _.merge(normalize(payload.fileTree, payload.path, payload.type), prevFiles.child)
files = _.set(files, _path, prevFiles)
return files
}
}
}
const normalize = (filesList): Record<string, File> => {
const normalize = (filesList, directory?: string, newInputType?: 'folder' | 'file'): Record<string, File> => {
const folders = {}
const files = {}
@ -308,25 +353,25 @@ const normalize = (filesList): Record<string, File> => {
}
})
// if (newInputType === 'folder') {
// const path = parent + '/blank'
// folders[path] = {
// path: path,
// name: '',
// isDirectory: true,
// type: 'folder'
// }
// } else if (newInputType === 'file') {
// const path = parent + '/blank'
// files[path] = {
// path: path,
// name: '',
// isDirectory: false,
// type: 'file'
// }
// }
if (newInputType === 'folder') {
const path = directory + '/blank'
folders[path] = {
path: path,
name: '',
isDirectory: true,
type: 'folder'
}
} else if (newInputType === 'file') {
const path = directory + '/blank'
files[path] = {
path: path,
name: '',
isDirectory: false,
type: 'file'
}
}
return Object.assign({}, folders, files)
}
@ -335,7 +380,7 @@ const fileAdded = (state: BrowserState, path: string): { [x: string]: Record<str
let files = state.mode === 'browser' ? state.browser.files : state.localhost.files
const _path = splitPath(state, path)
files = _.set(files, _path)
files = _.set(files, _path, null)
return files
}
@ -343,7 +388,7 @@ const folderAdded = (state: BrowserState, path: string): { [x: string]: Record<s
let files = state.mode === 'browser' ? state.browser.files : state.localhost.files
const _path = splitPath(state, path)
files = _.set(files, _path)
files = _.set(files, _path, null)
return files
}
@ -351,7 +396,49 @@ const fileRemoved = (state: BrowserState, path: string): { [x: string]: Record<s
let files = state.mode === 'browser' ? state.browser.files : state.localhost.files
const _path = splitPath(state, path)
files = _.unset(files, _path)
// files = _.unset(files, _path)
return files
}
const removeInputField = (state: BrowserState, payload: { path: string, fileTree }) => {
if (state.mode === 'browser') {
if (payload.path === state.browser.currentWorkspace) {
const files = state.browser.files
delete files[state.browser.currentWorkspace][payload.path + '/' + 'blank']
return files
}
return fetchDirectoryContent(state, payload)
} else {
if (payload.path === state.mode) {
const files = state[state.mode].files
delete files[state.mode][payload.path + '/' + 'blank']
return files
}
return fetchDirectoryContent(state, payload)
}
}
// IDEA: pass new parameter to fetchDirectoryContent for delete Path option.
const removePath = (state: BrowserState, path: string, pathName, files) => {
const pathArr: string[] = path.split('/').filter(value => value)
if (pathArr[0] !== root) pathArr.unshift(root)
const _path = pathArr.map((key, index) => index > 1 ? ['child', key] : key).reduce((acc: string[], cur) => {
return Array.isArray(cur) ? [...acc, ...cur] : [...acc, cur]
}, [])
const prevFiles = _.get(files, _path)
if (prevFiles) {
prevFiles.child && prevFiles.child[pathName] && delete prevFiles.child[pathName]
files = _.set(files, _path, {
isDirectory: true,
path,
name: extractNameFromKey(path).indexOf('gist-') === 0 ? extractNameFromKey(path).split('-')[1] : extractNameFromKey(path),
type: extractNameFromKey(path).indexOf('gist-') === 0 ? 'gist' : 'folder',
child: prevFiles ? prevFiles.child : {}
})
}
return files
}

Loading…
Cancel
Save