diff --git a/backend/controllers/file.ts b/backend/controllers/file.ts index bc9c4b4..e03de07 100644 --- a/backend/controllers/file.ts +++ b/backend/controllers/file.ts @@ -727,11 +727,11 @@ class FileController { } try { - const fileID = req.body.id; - const userID = req.user._id; - const folderID = req.body.parentID; + const fileID = req.body.id as string; + const userID = req.user._id as string; + const parentID = (req.body.parentID as string) || "/"; - await fileService.moveFile(userID, fileID, folderID); + await fileService.moveFile(userID, fileID, parentID); res.send(); } catch (e: unknown) { diff --git a/backend/controllers/folder.ts b/backend/controllers/folder.ts index ba7851e..2b44c17 100644 --- a/backend/controllers/folder.ts +++ b/backend/controllers/folder.ts @@ -177,9 +177,9 @@ class FolderController { try { const userID = req.user._id; const folderID = req.body.id; - const parent = req.body.parent; + const parentID = req.body.parentID; - await folderService.moveFolder(userID, folderID, parent); + await folderService.moveFolder(userID, folderID, parentID); res.send(); } catch (e: unknown) { diff --git a/backend/db/utils/folderUtils/index.ts b/backend/db/utils/folderUtils/index.ts index 4b80c7e..eb0df20 100644 --- a/backend/db/utils/folderUtils/index.ts +++ b/backend/db/utils/folderUtils/index.ts @@ -129,7 +129,7 @@ class DbUtil { ) => { const folder = (await Folder.findOneAndUpdate( { _id: new ObjectId(folderID), owner: userID }, - { $set: { parent: parent, parentList: parentList } } + { $set: { parent: parent, parentList: parentList }, new: true } )) as FolderInterface; return folder; @@ -194,7 +194,11 @@ class DbUtil { owner: userID, }; - const idQuery = [currentParent]; + const idQuery = []; + + if (currentParent && currentParent !== "/") { + idQuery.push(currentParent); + } if (folderID) { query.parentList = { $ne: folderID }; @@ -217,6 +221,17 @@ class DbUtil { return result; }; + + getFolderListByIncludedParent = async (userID: string, parent: string) => { + const folderList = await Folder.find({ + owner: userID, + parentList: { + $in: parent, + }, + }); + + return folderList; + }; } export default DbUtil; diff --git a/backend/express-routers/folder.ts b/backend/express-routers/folder.ts index 3405da4..8f7d03b 100644 --- a/backend/express-routers/folder.ts +++ b/backend/express-routers/folder.ts @@ -4,7 +4,7 @@ import env from "../enviroment/env"; import FileSystemService from "../services/ChunkService/FileSystemService"; import S3Service from "../services/ChunkService/S3Service"; import FolderController from "../controllers/folder"; -import { moveFolderValidationRules } from "../middleware/Folders/FolderMiddleware"; +import { moveFolderListValidationRules } from "../middleware/Folders/FolderMiddleware"; let folderController: FolderController; @@ -51,7 +51,7 @@ router.get( router.get( "/folder-service/move-folder-list", auth, - moveFolderValidationRules, + moveFolderListValidationRules, folderController.getMoveFolderList ); diff --git a/backend/middleware/Files/FilesMiddleware.ts b/backend/middleware/Files/FilesMiddleware.ts new file mode 100644 index 0000000..12eba0e --- /dev/null +++ b/backend/middleware/Files/FilesMiddleware.ts @@ -0,0 +1,9 @@ +import { query, validationResult } from "express-validator"; +import { Request, Response, NextFunction } from "express"; +import { middlewareValidationFunction } from "../Utils/MiddlewareUtils"; + +export const moveFileValidationRules = [ + query("fileID").isString().withMessage("Parent must be a string"), + query("parentID").isString().withMessage("Parent must be a string"), + middlewareValidationFunction, +]; diff --git a/backend/middleware/Folders/FolderMiddleware.ts b/backend/middleware/Folders/FolderMiddleware.ts index fbfa690..abeb241 100644 --- a/backend/middleware/Folders/FolderMiddleware.ts +++ b/backend/middleware/Folders/FolderMiddleware.ts @@ -1,18 +1,9 @@ import { query, validationResult } from "express-validator"; import { Request, Response, NextFunction } from "express"; +import { middlewareValidationFunction } from "../Utils/MiddlewareUtils"; -export const moveFolderValidationRules = [ +export const moveFolderListValidationRules = [ query("parent").optional().isString().withMessage("Parent must be a string"), query("search").optional().isString().withMessage("Search must be a string"), - query("folderID") - .optional() - .isString() - .withMessage("FolderID must be a string"), - (req: Request, res: Response, next: NextFunction) => { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - next(); - }, + middlewareValidationFunction, ]; diff --git a/backend/middleware/Utils/MiddlewareUtils.ts b/backend/middleware/Utils/MiddlewareUtils.ts new file mode 100644 index 0000000..df0368d --- /dev/null +++ b/backend/middleware/Utils/MiddlewareUtils.ts @@ -0,0 +1,15 @@ +import { NextFunction } from "express"; +import { validationResult } from "express-validator"; +import { Request, Response } from "express"; + +export const middlewareValidationFunction = ( + req: Request, + res: Response, + next: NextFunction +) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + next(); +}; diff --git a/backend/services/FileService/index.ts b/backend/services/FileService/index.ts index 4a3db7a..86243a2 100644 --- a/backend/services/FileService/index.ts +++ b/backend/services/FileService/index.ts @@ -315,21 +315,29 @@ class MongoFileService { return file; }; - moveFile = async (userID: string, fileID: string, folderID: string) => { + moveFile = async (userID: string, fileID: string, parentID: string) => { const file = await dbUtilsFile.getFileInfo(fileID, userID); if (!file) throw new NotFoundError("Move File Not Found Error"); - const folder = await dbUtilsFolder.getFolderInfo(folderID, userID); + const newParentList = []; - if (!folder) throw new NotFoundError("Move Folder Not Found Error"); + if (parentID !== "/") { + const folder = await dbUtilsFolder.getFolderInfo(parentID, userID); - const newParentList = [...folder.parentList, folder._id]; + if (!folder) throw new NotFoundError("Move Folder Not Found Error"); + + newParentList.push(...folder.parentList, folder._id); + } else { + newParentList.push("/"); + } + + console.log("new parent list", newParentList, parentID); const updatedFile = await dbUtilsFile.moveFile( fileID, userID, - folderID, + parentID, newParentList.toString() ); diff --git a/backend/services/FolderService/index.ts b/backend/services/FolderService/index.ts index ff165bb..25255f4 100644 --- a/backend/services/FolderService/index.ts +++ b/backend/services/FolderService/index.ts @@ -248,16 +248,56 @@ class FolderService { }; moveFolder = async (userID: string, folderID: string, parentID: string) => { + console.log("parentID", parentID); + const foldersByIncludedParent = + await utilsFolder.getFolderListByIncludedParent(userID, folderID); + + //const newParentList = []; + + if (parentID !== "/") { + const folderToMoveTo = await utilsFolder.getFolderInfo(parentID, userID); + + if (!folderToMoveTo) { + throw new NotFoundError("Move Folder Not Found Error"); + } + + for (let i = 0; i < foldersByIncludedParent.length; i++) { + const currentFolder = foldersByIncludedParent[i]; + + const currentParentIndex = currentFolder.parentList.indexOf(folderID); + + const newParentList = [ + ...folderToMoveTo.parentList, + folderToMoveTo._id.toString(), + ...currentFolder.parentList.slice(currentParentIndex + 1), + ]; + + console.log("new parent list", newParentList); + + await utilsFolder.moveFolder( + folderID, + userID, + folderToMoveTo._id.toString(), + newParentList + ); + } + } else { + // newParentList.push("/"); + } + + console.log("foldersByIncludedParent", foldersByIncludedParent); + + return; let parentList = ["/"]; - if (parentID.length !== 1) { - const parentFile = await utilsFolder.getFolderInfo(parentID, userID); + // if (parentID.length !== 1) { + // const parentFile = await utilsFolder.getFolderInfo(parentID, userID); - if (!parentFile) throw new NotFoundError("Parent Folder Info Not Found"); + // if (!parentFile) throw new NotFoundError("Parent Folder Info Not Found"); - parentList = parentFile.parentList; - parentList.push(parentID); - } + // parentList = parentFile.parentList; + // parentList.push(parentID); + // } const folder = await utilsFolder.moveFolder( folderID, diff --git a/src/api/filesAPI.ts b/src/api/filesAPI.ts index 5d46bd1..47e63d5 100644 --- a/src/api/filesAPI.ts +++ b/src/api/filesAPI.ts @@ -206,6 +206,14 @@ export const removeLinkAPI = async (fileID: string) => { return response.data; }; +export const moveFileAPI = async (fileID: string, parentID: string) => { + const response = await axios.patch(`/file-service/move`, { + id: fileID, + parentID, + }); + return response.data; +}; + // DELETE export const deleteFileAPI = async (fileID: string) => { diff --git a/src/api/foldersAPI.ts b/src/api/foldersAPI.ts index d422e8c..562a9b0 100644 --- a/src/api/foldersAPI.ts +++ b/src/api/foldersAPI.ts @@ -90,6 +90,14 @@ export const restoreFolderAPI = async (folderID: string) => { return response.data; }; +export const moveFolderAPI = async (folderID: string, parentID: string) => { + const response = await axios.patch(`/folder-service/move`, { + id: folderID, + parentID, + }); + return response.data; +}; + // DELETE export const deleteFolderAPI = async (folderID: string) => { diff --git a/src/components/MoverPopup/MoverPopup.tsx b/src/components/MoverPopup/MoverPopup.tsx index 5933e45..4b4cca2 100644 --- a/src/components/MoverPopup/MoverPopup.tsx +++ b/src/components/MoverPopup/MoverPopup.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import { useAppDispatch, useAppSelector } from "../../hooks/store"; -import { useMoveFolders } from "../../hooks/folders"; +import { useFoldersClient, useMoveFolders } from "../../hooks/folders"; import { FolderInterface } from "../../types/folders"; import CloseIcon from "../../icons/CloseIcon"; import { resetMoveModal } from "../../reducers/selected"; @@ -10,6 +10,10 @@ import HomeIconOutline from "../../icons/HomeIconOutline"; import ArrowBackIcon from "../../icons/ArrowBackIcon"; import classNames from "classnames"; import FolderIcon from "../../icons/FolderIcon"; +import { toast } from "react-toastify"; +import { moveFileAPI } from "../../api/filesAPI"; +import { useFilesClient } from "../../hooks/files"; +import { moveFolderAPI } from "../../api/foldersAPI"; const MoverPopup = () => { const [search, setSearch] = useState(""); @@ -22,9 +26,12 @@ const MoverPopup = () => { const multiSelectMode = useAppSelector( (state) => state.selected.multiSelectMode ); + const [isLoadingMove, setIsLoadingMove] = useState(false); const file = useAppSelector((state) => state.selected.moveModal.file); const folder = useAppSelector((state) => state.selected.moveModal.folder); const dispatch = useAppDispatch(); + const { invalidateFilesCache } = useFilesClient(); + const { invalidateFoldersCache } = useFoldersClient(); const lastSelected = useRef({ timestamp: 0, folderID: "", @@ -59,7 +66,7 @@ const MoverPopup = () => { setDebouncedSearch(""); setParentList([...parentList, folder]); setParent(folder); - setSelectedFolder(folder); + setSelectedFolder(null); } else { setSelectedFolder(folder); } @@ -83,14 +90,21 @@ const MoverPopup = () => { }; const moveText = useMemo(() => { - if (selectedFolder?._id) { - return "Move to selected folder"; + if (selectedFolder?._id && selectedFolder?.name) { + let reducedLengthFileName = selectedFolder.name; + if (reducedLengthFileName.length > 10) + reducedLengthFileName = reducedLengthFileName.substring(0, 10) + "..."; + return `Move to ${reducedLengthFileName}`; } else if (!parent) { - return "Move home"; + return "Move to home"; } else { - return "Move here"; + const lastParent = parentList[parentList.length - 1]; + let reducedLengthFileName = lastParent.name; + if (reducedLengthFileName.length > 10) + reducedLengthFileName = reducedLengthFileName.substring(0, 10) + "..."; + return `Move to ${reducedLengthFileName}`; } - }, [selectedFolder?._id, parent?._id]); + }, [selectedFolder?._id, selectedFolder?.name, parent?._id]); const headerText = useMemo(() => { if (parent) { @@ -108,14 +122,40 @@ const MoverPopup = () => { setSelectedFolder(null); }; - const onMoveClick = () => { + const onMoveClick = async () => { + setIsLoadingMove(true); const moveTo = selectedFolder?._id ? selectedFolder?._id : parent?._id || "/"; - if (file) { - } else if (folder) { + try { + if (multiSelectMode) { + } else if (file) { + await toast.promise(moveFileAPI(file._id, moveTo), { + pending: "Moving File...", + success: "File Moved", + error: "Error Moving File", + }); + invalidateFilesCache(); + dispatch(resetMoveModal()); + } else if (folder) { + await toast.promise(moveFolderAPI(folder._id, moveTo), { + pending: "Moving Folder...", + success: "Folder Moved", + error: "Error Moving Folder", + }); + invalidateFoldersCache(); + dispatch(resetMoveModal()); + } + console.log("move to", moveTo); + } catch (e) { + console.log("move error", e); + } finally { + setIsLoadingMove(false); } - console.log("move to", moveTo); + }; + + const onTitleClick = () => { + setSelectedFolder(parentList[parentList.length - 1]); }; const closeMoverModal = (e: any) => { @@ -163,7 +203,10 @@ const MoverPopup = () => { />
-

+

{headerText}

@@ -206,9 +249,13 @@ const MoverPopup = () => {
diff --git a/src/components/PhotoViewerPopup/index.tsx b/src/components/PhotoViewerPopup/index.tsx index fe115e5..0ae71ca 100644 --- a/src/components/PhotoViewerPopup/index.tsx +++ b/src/components/PhotoViewerPopup/index.tsx @@ -264,14 +264,14 @@ const PhotoViewerPopup: React.FC = memo((props) => {
@@ -280,11 +280,11 @@ const PhotoViewerPopup: React.FC = memo((props) => {