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
* @returns {void}
*/
async copyFile(src, dest, customName) {
async copyFile(src: string, dest: string, customName?: string) {
try {
src = this.normalize(src)
dest = this.normalize(dest)
@ -262,7 +262,7 @@ class FileManager extends Plugin {
* @param {string} dest path of the destination dir
* @returns {void}
*/
async copyDir(src, dest) {
async copyDir(src: string, dest: string, customName?: string) {
try {
src = this.normalize(src)
dest = this.normalize(dest)
@ -277,16 +277,16 @@ class FileManager extends Plugin {
if (provider.isSubDirectory(src, dest)) {
this.call('notification', 'toast', recursivePasteToastMsg())
} else {
await this.inDepthCopy(src, dest)
await this.inDepthCopy(src, dest, customName)
}
} catch (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)
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)
await this.mkdir(copiedFolderPath)
@ -295,7 +295,7 @@ class FileManager extends Plugin {
if (!value.isDirectory) {
await this.copyFile(key, copiedFolderPath, helper.extractNameFromKey(key))
} 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)
src = this.limitPluginScope(src)
dest = this.limitPluginScope(dest)
await this._handleExists(src, `Cannot copy from ${src}. Path does not exist.`)
await this._handleExists(dest, `Cannot paste content into ${dest}. Path does not exist.`)
await this._handleIsDir(dest, `Cannot paste content into ${dest}. Path is not directory.`)
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._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)
let movedFilePath = dest + ( '/' + `${helper.extractNameFromKey(src)}`)
movedFilePath = await helper.createNonClashingNameAsync(movedFilePath, this)
if (await this.exists(dest + '/' + fileName)) {
throw createError({ code: 'ENOENT', message: `Cannot move ${src}. File already exists at destination ${dest}`})
}
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)
} catch (e) {

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

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

@ -9,10 +9,14 @@ export interface FileType {
}
export interface MoveContextType {
dragged: string
dragged: {
path: string,
isDirectory: boolean
}
isDraggable?: boolean
moveFile: (dest: string, dragged: string) => void
currentlyMoved: (path: string) => void
moveFile: (dest: string, src: string) => void
moveFolder: (dest: string, src: string) => void
currentlyMoved: (file: { path: string, isDirectory: boolean }) => void
}
export interface DraggableType {
@ -25,5 +29,6 @@ export interface DraggableType {
export interface DragType {
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))
}
}
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)
}
const handleFileMove = (dest: string, dragged:string)=>{
const handleFileMove = (dest: string, src: string) => {
try {
props.dispatchMoveFile(dragged, dest)
props.dispatchMoveFile(src, dest)
} 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 (
<Drag onFileMoved={handleFileMove}>
<Drag onFileMoved={handleFileMove} onFolderMoved={handleFolderMove}>
<div ref={treeRef} tabIndex={0} style={{ outline: "none" }}>
<TreeView id='treeView'>
<TreeViewItem id="treeViewItem"

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

@ -7,7 +7,7 @@ import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
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'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace'
@ -128,9 +128,15 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
const dispatchCloneRepository = async (url: string) => {
await cloneRepository(url)
}
const dispatchMoveFile = async (src: string, dest: string) => {
await moveFile(src, dest)
}
const dispatchMoveFolder = async (src: string, dest: string) => {
await moveFolder(src, dest)
}
useEffect(() => {
dispatchInitWorkspace()
}, [])
@ -234,7 +240,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchHandleDownloadFiles,
dispatchHandleRestoreBackup,
dispatchCloneRepository,
dispatchMoveFile
dispatchMoveFile,
dispatchMoveFolder
}
return (
<FileSystemContext.Provider value={value}>

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

@ -97,8 +97,9 @@ export interface FileExplorerProps {
dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchRemoveInputField:(path: string) => 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>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void>
}
export interface FileExplorerMenuProps {

Loading…
Cancel
Save