Implement move directory

pull/2807/head
David Disu 2 years ago committed by Aniket
parent 887fd6e2a5
commit 589fbb1f1a
  1. 56
      apps/remix-ide/src/app/files/fileManager.ts
  2. 7
      libs/remix-ui/drag-n-drop/src/lib/context/moveContext.ts
  3. 40
      libs/remix-ui/drag-n-drop/src/lib/drag-n-drop.tsx
  4. 13
      libs/remix-ui/drag-n-drop/src/lib/types/index.ts
  5. 10
      libs/remix-ui/workspace/src/lib/actions/index.ts
  6. 15
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  7. 4
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  8. 11
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  9. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  10. 3
      libs/remix-ui/workspace/src/lib/types/index.ts

@ -236,7 +236,7 @@ class FileManager extends Plugin {
* @param {string} dest path of the destrination file * @param {string} dest path of the destrination file
* @returns {void} * @returns {void}
*/ */
async copyFile(src, dest, customName) { async copyFile(src: string, dest: string, customName?: string) {
try { try {
src = this.normalize(src) src = this.normalize(src)
dest = this.normalize(dest) dest = this.normalize(dest)
@ -262,7 +262,7 @@ class FileManager extends Plugin {
* @param {string} dest path of the destination dir * @param {string} dest path of the destination dir
* @returns {void} * @returns {void}
*/ */
async copyDir(src, dest) { async copyDir(src: string, dest: string, customName?: string) {
try { try {
src = this.normalize(src) src = this.normalize(src)
dest = this.normalize(dest) dest = this.normalize(dest)
@ -277,16 +277,16 @@ class FileManager extends Plugin {
if (provider.isSubDirectory(src, dest)) { if (provider.isSubDirectory(src, dest)) {
this.call('notification', 'toast', recursivePasteToastMsg()) this.call('notification', 'toast', recursivePasteToastMsg())
} else { } else {
await this.inDepthCopy(src, dest) await this.inDepthCopy(src, dest, customName)
} }
} catch (e) { } catch (e) {
throw new Error(e) throw new Error(e)
} }
} }
async inDepthCopy(src, dest, count = 0) { async inDepthCopy(src: string, dest: string, customName?: string) {
const content = await this.readdir(src) const content = await this.readdir(src)
let copiedFolderPath = count === 0 ? dest + '/' + `Copy_${helper.extractNameFromKey(src)}` : dest + '/' + helper.extractNameFromKey(src) let copiedFolderPath = !customName ? dest + '/' + `Copy_${helper.extractNameFromKey(src)}` : dest + '/' + helper.extractNameFromKey(src)
copiedFolderPath = await helper.createNonClashingDirNameAsync(copiedFolderPath, this) copiedFolderPath = await helper.createNonClashingDirNameAsync(copiedFolderPath, this)
await this.mkdir(copiedFolderPath) await this.mkdir(copiedFolderPath)
@ -295,7 +295,7 @@ class FileManager extends Plugin {
if (!value.isDirectory) { if (!value.isDirectory) {
await this.copyFile(key, copiedFolderPath, helper.extractNameFromKey(key)) await this.copyFile(key, copiedFolderPath, helper.extractNameFromKey(key))
} else { } else {
await this.inDepthCopy(key, copiedFolderPath, count + 1) await this.inDepthCopy(key, copiedFolderPath, helper.extractNameFromKey(key))
} }
} }
} }
@ -831,15 +831,45 @@ class FileManager extends Plugin {
dest = this.normalize(dest) dest = this.normalize(dest)
src = this.limitPluginScope(src) src = this.limitPluginScope(src)
dest = this.limitPluginScope(dest) dest = this.limitPluginScope(dest)
await this._handleExists(src, `Cannot copy from ${src}. Path does not exist.`) await this._handleExists(src, `Cannot move ${src}. Path does not exist.`)
await this._handleExists(dest, `Cannot paste content into ${dest}. Path does not exist.`) await this._handleExists(dest, `Cannot move content into ${dest}. Path does not exist.`)
await this._handleIsDir(dest, `Cannot paste content into ${dest}. Path is not directory.`) await this._handleIsFile(src, `Cannot move ${src}. Path is not a file.`)
await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`)
const fileName = helper.extractNameFromKey(src)
const content = await this.readFile(src) if (await this.exists(dest + '/' + fileName)) {
let movedFilePath = dest + ( '/' + `${helper.extractNameFromKey(src)}`) throw createError({ code: 'ENOENT', message: `Cannot move ${src}. File already exists at destination ${dest}`})
movedFilePath = await helper.createNonClashingNameAsync(movedFilePath, this) }
await this.copyFile(src, dest, fileName)
await this.remove(src)
} catch (e) {
throw new Error(e)
}
}
await this.writeFile(movedFilePath, content) /**
* Moves a folder to a new folder
* @param {string} src path of the source folder
* @param {string} dest path of the destination folder
* @returns {void}
*/
async moveDir(src: string, dest: string) {
try {
src = this.normalize(src)
dest = this.normalize(dest)
src = this.limitPluginScope(src)
dest = this.limitPluginScope(dest)
await this._handleExists(src, `Cannot move ${src}. Path does not exist.`)
await this._handleExists(dest, `Cannot move content into ${dest}. Path does not exist.`)
await this._handleIsDir(src, `Cannot move ${src}. Path is not directory.`)
await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`)
const dirName = helper.extractNameFromKey(src)
if (await this.exists(dest + '/' + dirName)) {
throw createError({ code: 'ENOENT', message: `Cannot move ${src}. Folder already exists at destination ${dest}`})
}
await this.copyDir(src, dest, dirName)
await this.remove(src) await this.remove(src)
} catch (e) { } catch (e) {

@ -2,7 +2,8 @@ import { createContext } from "react";
import { MoveContextType } from "../types"; import { MoveContextType } from "../types";
export const MoveContext = createContext<MoveContextType>({ export const MoveContext = createContext<MoveContextType>({
dragged: "", dragged: {} as { path: string, isDirectory: boolean },
moveFile: () => {}, moveFile: () => null,
currentlyMoved: () => {} moveFolder: () => null,
currentlyMoved: () => null
}) })

@ -4,13 +4,14 @@ import { MoveContext } from "./context/moveContext"
import { DraggableType, DragType } from "./types" import { DraggableType, DragType } from "./types"
export const Drag = (props: DragType) => { export const Drag = (props: DragType) => {
const [dragged, setDragged] = useState<string>("") const [dragged, setDragged] = useState<{ path: string, isDirectory: boolean }>({} as { path: string, isDirectory: boolean })
return ( return (
<MoveContext.Provider <MoveContext.Provider
value={{ value={{
dragged: dragged, dragged: dragged,
moveFile: props.onFileMoved, moveFile: props.onFileMoved,
moveFolder: props.onFolderMoved,
currentlyMoved: (path) => { currentlyMoved: (path) => {
setDragged(() => path) setDragged(() => path)
}, },
@ -23,37 +24,44 @@ export const Drag = (props: DragType) => {
export const Draggable = (props: DraggableType) => { export const Draggable = (props: DraggableType) => {
const dragRef = useRef<HTMLSpanElement>(null), const dragRef = useRef<HTMLSpanElement>(null),
file = props.file, destination = props.file,
context = useContext(MoveContext) context = useContext(MoveContext)
const handleDrop = (event: React.DragEvent<HTMLSpanElement>) => { const handleDrop = (event: React.DragEvent<HTMLSpanElement>) => {
event.preventDefault() event.preventDefault()
if (file.isDirectory) { if (destination.isDirectory) {
context.moveFile(file.path, context.dragged) if (context.dragged.isDirectory) {
context.moveFolder(destination.path, context.dragged.path)
} else { } else {
const path = extractParentFromKey(file.path) || '/' context.moveFile(destination.path, context.dragged.path)
}
} else {
const path = extractParentFromKey(destination.path) || '/'
context.moveFile(path, context.dragged) if (context.dragged.isDirectory) {
context.moveFolder(path, context.dragged.path)
} else {
context.moveFile(path, context.dragged.path)
}
} }
} }
const handleDragover = (event: React.DragEvent<HTMLSpanElement>) => { const handleDragover = (event: React.DragEvent<HTMLSpanElement>) => {
//Checks if the folder is opened //Checks if the folder is opened
event.preventDefault() event.preventDefault()
if (file.isDirectory && !props.expandedPath.includes(file.path)) { if (destination.isDirectory && !props.expandedPath.includes(destination.path)) {
props.handleClickFolder(file.path, file.type) props.handleClickFolder(destination.path, destination.type)
} }
} }
const handleDrag = () => { const handleDrag = () => {
if (context.dragged !== file.path) { if (context.dragged.path !== destination.path) {
context.currentlyMoved(file.path) context.currentlyMoved({
} path: destination.path,
isDirectory: destination.isDirectory
})
} }
if (props.isDraggable) {
return <>{props.children}</>
} }
return ( return (
@ -67,12 +75,12 @@ export const Draggable = (props: DraggableType) => {
handleDrop(event) handleDrop(event)
}} }}
onDragStart={() => { onDragStart={() => {
if (file) { if (destination) {
handleDrag() handleDrag()
} }
}} }}
onDragOver={(event) => { onDragOver={(event) => {
if (file) { if (destination) {
handleDragover(event) handleDragover(event)
} }
}} }}

@ -9,10 +9,14 @@ export interface FileType {
} }
export interface MoveContextType { export interface MoveContextType {
dragged: string dragged: {
path: string,
isDirectory: boolean
}
isDraggable?: boolean isDraggable?: boolean
moveFile: (dest: string, dragged: string) => void moveFile: (dest: string, src: string) => void
currentlyMoved: (path: string) => void moveFolder: (dest: string, src: string) => void
currentlyMoved: (file: { path: string, isDirectory: boolean }) => void
} }
export interface DraggableType { export interface DraggableType {
@ -25,5 +29,6 @@ export interface DraggableType {
export interface DragType { export interface DragType {
children: ReactNode children: ReactNode
onFileMoved: (dest: string, dragged: string) => void onFileMoved: (dest: string, src: string) => void
onFolderMoved: (dest: string, src: string) => void
} }

@ -472,3 +472,13 @@ export const moveFile = async (src: string, dest: string) => {
dispatch(displayPopUp('Oops! An error ocurred while performing moveFile operation.' + error)) dispatch(displayPopUp('Oops! An error ocurred while performing moveFile operation.' + error))
} }
} }
export const moveFolder = async (src: string, dest: string) => {
const fileManager = plugin.fileManager
try {
await fileManager.moveDir(src, dest)
} catch (error) {
dispatch(displayPopUp('Oops! An error ocurred while performing moveDir operation.' + error))
}
}

@ -410,16 +410,23 @@ export const FileExplorer = (props: FileExplorerProps) => {
props.dispatchHandleExpandPath(expandPath) props.dispatchHandleExpandPath(expandPath)
} }
const handleFileMove = (dest: string, dragged:string)=>{ const handleFileMove = (dest: string, src: string) => {
try { try {
props.dispatchMoveFile(dragged, dest) props.dispatchMoveFile(src, dest)
} catch (error) { } catch (error) {
props.modal('Moving File Failed', 'Unexpected error while moving file: ' + dragged, 'Close', async () => {}) props.modal('Moving File Failed', 'Unexpected error while moving file: ' + src, 'Close', async () => {})
} }
} }
const handleFolderMove = (dest: string, src: string) => {
try {
props.dispatchMoveFolder(src, dest)
} catch (error) {
props.modal('Moving Folder Failed', 'Unexpected error while moving folder: ' + src, 'Close', async () => {})
}
}
return ( return (
<Drag onFileMoved={handleFileMove}> <Drag onFileMoved={handleFileMove} onFolderMoved={handleFolderMove}>
<div ref={treeRef} tabIndex={0} style={{ outline: "none" }}> <div ref={treeRef} tabIndex={0} style={{ outline: "none" }}>
<TreeView id='treeView'> <TreeView id='treeView'>
<TreeViewItem id="treeViewItem" <TreeViewItem id="treeViewItem"

@ -30,8 +30,8 @@ export const FileSystemContext = createContext<{
dispatchHandleExpandPath: (paths: string[]) => Promise<void>, dispatchHandleExpandPath: (paths: string[]) => Promise<void>,
dispatchHandleDownloadFiles: () => Promise<void>, dispatchHandleDownloadFiles: () => Promise<void>,
dispatchHandleRestoreBackup: () => Promise<void> dispatchHandleRestoreBackup: () => Promise<void>
dispatchCloneRepository: (url: string) => Promise<void> dispatchCloneRepository: (url: string) => Promise<void>,
dispatchMoveFile: (src: string, dest: string) => Promise<void>, dispatchMoveFile: (src: string, dest: string) => Promise<void>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void>
}>(null) }>(null)

@ -7,7 +7,7 @@ import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace' import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile } from '../actions' fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder } from '../actions'
import { Modal, WorkspaceProps, WorkspaceTemplate } from '../types' import { Modal, WorkspaceProps, WorkspaceTemplate } 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'
@ -128,9 +128,15 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
const dispatchCloneRepository = async (url: string) => { const dispatchCloneRepository = async (url: string) => {
await cloneRepository(url) await cloneRepository(url)
} }
const dispatchMoveFile = async (src: string, dest: string) => { const dispatchMoveFile = async (src: string, dest: string) => {
await moveFile(src, dest) await moveFile(src, dest)
} }
const dispatchMoveFolder = async (src: string, dest: string) => {
await moveFolder(src, dest)
}
useEffect(() => { useEffect(() => {
dispatchInitWorkspace() dispatchInitWorkspace()
}, []) }, [])
@ -234,7 +240,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchHandleDownloadFiles, dispatchHandleDownloadFiles,
dispatchHandleRestoreBackup, dispatchHandleRestoreBackup,
dispatchCloneRepository, dispatchCloneRepository,
dispatchMoveFile dispatchMoveFile,
dispatchMoveFolder
} }
return ( return (
<FileSystemContext.Provider value={value}> <FileSystemContext.Provider value={value}>

@ -331,6 +331,7 @@ export function Workspace () {
dispatchAddInputField={global.dispatchAddInputField} dispatchAddInputField={global.dispatchAddInputField}
dispatchHandleExpandPath={global.dispatchHandleExpandPath} dispatchHandleExpandPath={global.dispatchHandleExpandPath}
dispatchMoveFile={global.dispatchMoveFile} dispatchMoveFile={global.dispatchMoveFile}
dispatchMoveFolder={global.dispatchMoveFolder}
/> />
} }
</div> </div>
@ -369,6 +370,7 @@ export function Workspace () {
dispatchAddInputField={global.dispatchAddInputField} dispatchAddInputField={global.dispatchAddInputField}
dispatchHandleExpandPath={global.dispatchHandleExpandPath} dispatchHandleExpandPath={global.dispatchHandleExpandPath}
dispatchMoveFile={global.dispatchMoveFile} dispatchMoveFile={global.dispatchMoveFile}
dispatchMoveFolder={global.dispatchMoveFolder}
/> />
} }
</div> </div>

@ -97,8 +97,9 @@ export interface FileExplorerProps {
dispatchFetchDirectory:(path: string) => Promise<void>, dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchRemoveInputField:(path: string) => Promise<void>, dispatchRemoveInputField:(path: string) => Promise<void>,
dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>, dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>,
dispatchHandleExpandPath: (paths: string[]) => Promise<void> dispatchHandleExpandPath: (paths: string[]) => Promise<void>,
dispatchMoveFile: (src: string, dest: string) => Promise<void>, dispatchMoveFile: (src: string, dest: string) => Promise<void>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void>
} }
export interface FileExplorerMenuProps { export interface FileExplorerMenuProps {

Loading…
Cancel
Save