lots of UI improvements

This commit is contained in:
subnub
2025-02-20 16:47:53 -05:00
parent 8999e384bb
commit 1b44ce8dbf
16 changed files with 575 additions and 357 deletions
@@ -8,6 +8,7 @@ export const moveFolderListValidationRules = [
query("parent").optional().isString().withMessage("Parent must be a string"),
query("search").optional().isString().withMessage("Search must be a string"),
query("folderIDs")
.optional()
.isArray()
.isLength({ min: 1 })
.withMessage("FolderIDs must be an array of strings"),
@@ -98,7 +98,10 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
}, []);
return (
<div ref={wrapperRef} className="absolute bottom-0 top-full w-full">
<div
ref={wrapperRef}
className="absolute bottom-0 top-full w-full text-gray-500"
>
<input
className="hidden"
ref={uploadRef}
@@ -121,7 +124,7 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
className="flex items-center justify-start px-5 py-3 no-underline overflow-hidden text-sm bg-white hover:bg-white-hover"
onClick={triggerFileUpload}
>
<UploadFileIcon className="w-5 h-5 mr-2.5 text-primary" />
<UploadFileIcon className="w-4 h-4 mr-2.5 text-primary" />
<p className="text-sm">Upload Files</p>
</a>
</div>
@@ -131,7 +134,7 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
className="flex items-center justify-start px-5 py-3 no-underline overflow-hidden text-sm bg-white hover:bg-white-hover"
onClick={createFolder}
>
<CreateFolderIcon className="w-5 h-5 mr-2.5 text-primary" />
<CreateFolderIcon className="w-4 h-4 mr-2.5 text-primary" />
<p className="text-sm">Create Folder</p>
</a>
</li>
@@ -141,7 +144,7 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
className="flex items-center justify-start px-5 py-3 no-underline overflow-hidden text-sm bg-white hover:bg-white-hover"
onClick={triggerFolderUpload}
>
<FolderUploadIcon className="w-5 h-5 mr-2.5 text-primary" />
<FolderUploadIcon className="w-4 h-4 mr-2.5 text-primary" />
<p className="text-sm">Upload Folder</p>
</a>
</li>
+64 -247
View File
@@ -1,27 +1,5 @@
import React, { memo, useEffect, useState } from "react";
import {
deleteFileAPI,
renameFileAPI,
downloadFileAPI,
trashFileAPI,
restoreFileAPI,
} from "../../api/filesAPI";
import { useFiles, useQuickFiles } from "../../hooks/files";
import {
deleteFolderAPI,
downloadZIPAPI,
renameFolder,
restoreFolderAPI,
trashFolderAPI,
} from "../../api/foldersAPI";
import { useClickOutOfBounds, useUtils } from "../../hooks/utils";
import { useAppDispatch } from "../../hooks/store";
import {
resetSelected,
setMoveModal,
setMultiSelectMode,
setShareModal,
} from "../../reducers/selected";
import TrashIcon from "../../icons/TrashIcon";
import MultiSelectIcon from "../../icons/MultiSelectIcon";
import RenameIcon from "../../icons/RenameIcon";
@@ -29,18 +7,10 @@ import ShareIcon from "../../icons/ShareIcon";
import DownloadIcon from "../../icons/DownloadIcon";
import MoveIcon from "../../icons/MoveIcon";
import RestoreIcon from "../../icons/RestoreIcon";
import {
deleteFilePopup,
renameFilePopup,
restoreItemPopup,
trashItemsPopup,
} from "../../popups/file";
import { FileInterface } from "../../types/file";
import { FolderInterface } from "../../types/folders";
import { toast } from "react-toastify";
import { deleteFolderPopup, renameFolderPopup } from "../../popups/folder";
import { useFolder, useFolders } from "../../hooks/folders";
import { useNavigate } from "react-router-dom";
import { useActions } from "../../hooks/actions";
import { FolderInterface } from "../../types/folders";
export interface ContextMenuProps {
closeContext: () => void;
@@ -63,10 +33,6 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
Y: 0,
set: false,
});
const { refetch: refetchFiles } = useFiles(false);
const { refetch: refetchFolders } = useFolders(false);
const { refetch: refetchFolder } = useFolder(false);
const { refetch: refetchQuickFiles } = useQuickFiles(false);
const {
closeContext,
contextSelected,
@@ -79,8 +45,17 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
} = props;
const { wrapperRef } = useClickOutOfBounds(closeContext);
const { isTrash, isMedia } = useUtils();
const dispatch = useAppDispatch();
const navigate = useNavigate();
const {
renameItem,
trashItem,
deleteItem,
restoreItem,
openMoveItemModal,
openShareItemModal,
downloadItem,
selectItemMultiSelect,
} = useActions({ quickItemMode });
useEffect(() => {
if (!wrapperRef.current) return;
@@ -108,212 +83,54 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
});
}, [wrapperRef, contextSelected.X, contextSelected.Y]);
const renameItem = async () => {
const onAction = async (
action:
| "rename"
| "trash"
| "delete"
| "restore"
| "move"
| "share"
| "download"
| "multi-select"
) => {
closeContext();
if (!folderMode && file) {
try {
const filename = await renameFilePopup(file.filename);
if (!filename || filename === file.filename) return;
await toast.promise(renameFileAPI(file._id, filename), {
pending: "Renaming...",
success: "Renamed",
error: "Error Renaming",
});
reloadItems();
} catch (e) {
console.log("Error renaming file", e);
}
} else if (folderMode && folder) {
try {
const folderName = await renameFolderPopup(folder.name);
if (!folderName || folderName === folder.name) return;
await toast.promise(renameFolder(folder._id, folderName), {
pending: "Renaming...",
success: "Renamed",
error: "Error Renaming",
});
reloadItems();
} catch (e) {
console.log("Error renaming folder", e);
}
switch (action) {
case "rename":
await renameItem(file, folder);
break;
case "trash":
await trashItem(file, folder);
break;
case "delete":
await deleteItem(file, folder);
break;
case "restore":
await restoreItem(file, folder);
break;
case "move":
await openMoveItemModal(file, folder);
break;
case "share":
openShareItemModal(file);
break;
case "download":
downloadItem(file, folder);
break;
case "multi-select":
selectItemMultiSelect(file, folder);
}
};
const trashItem = async () => {
closeContext();
if (!folderMode && file) {
try {
const result = await trashItemsPopup();
if (!result) return;
await toast.promise(trashFileAPI(file._id), {
pending: "Trashing...",
success: "Trashed",
error: "Error Trashing",
});
reloadItems();
} catch (e) {
console.log("Error trashing file", e);
if (
folder &&
parentBarMode &&
["trash", "delete", "restore"].includes(action)
) {
if (folder.parent === "/") {
navigate("/trash");
} else {
navigate(`/folder-trash/${folder.parent}`);
}
} else if (folderMode && folder) {
try {
const result = await trashItemsPopup();
if (!result) return;
await toast.promise(trashFolderAPI(folder._id), {
pending: "Trashing...",
success: "Trashed",
error: "Error Trashing",
});
reloadItems();
if (parentBarMode) {
if (folder.parent === "/") {
navigate("/home");
} else {
navigate(`/folder/${folder.parent}`);
}
}
} catch (e) {
console.log("Error trashing folder", e);
}
}
};
const deleteItem = async () => {
closeContext();
if (!folderMode && file) {
try {
const result = await deleteFilePopup();
if (!result) return;
await toast.promise(deleteFileAPI(file._id), {
pending: "Deleting...",
success: "Deleted",
error: "Error Deleting",
});
reloadItems();
} catch (e) {
console.log("Error deleting file", e);
}
} else if (folderMode && folder) {
try {
const result = await deleteFolderPopup();
if (!result) return;
await toast.promise(deleteFolderAPI(folder._id), {
pending: "Deleting...",
success: "Deleted",
error: "Error Deleting",
});
reloadItems();
if (parentBarMode) {
if (folder.parent === "/") {
navigate("/trash");
} else {
navigate(`/folder-trash/${folder.parent}`);
}
}
} catch (e) {
console.log("Error deleting folder", e);
}
}
};
const restoreItem = async () => {
closeContext();
const result = await restoreItemPopup();
if (!result) return;
if (!folderMode && file) {
try {
await toast.promise(restoreFileAPI(file._id), {
pending: "Restoring...",
success: "Restored",
error: "Error Restoring",
});
reloadItems();
} catch (e) {
console.log("Error restoring file", e);
}
} else if (folderMode && folder) {
try {
await toast.promise(restoreFolderAPI(folder._id), {
pending: "Restoring...",
success: "Restored",
error: "Error Restoring",
});
reloadItems();
if (parentBarMode) {
if (folder.parent === "/") {
navigate("/trash");
} else {
navigate(`/folder-trash/${folder.parent}`);
}
}
} catch (e) {
console.log("Error restoring folder", e);
}
}
};
const reloadItems = () => {
refetchFiles();
refetchQuickFiles();
refetchFolders();
refetchFolder();
dispatch(resetSelected());
};
const openMoveItemModal = async () => {
closeContext();
if (!folderMode && file) {
// dispatch(setMoverID(file._id, file.metadata.parent, true));
dispatch(setMoveModal({ type: "file", file, folder: null }));
} else if (folderMode && folder) {
// dispatch(setMoverID(folder._id, folder.parent, false));
// }
dispatch(setMoveModal({ type: "folder", file: null, folder }));
}
};
const openShareItemModal = () => {
closeContext();
dispatch(setShareModal(file!));
};
const downloadItem = () => {
closeContext();
if (file) downloadFileAPI(file._id);
if (folder) downloadZIPAPI([folder._id], []);
};
const selectItemMultiSelect = () => {
closeContext();
if (folderMode && folder) {
dispatch(
setMultiSelectMode([
{
type: "folder",
id: folder._id,
file: null,
folder: folder,
},
])
);
} else if (!folderMode && file) {
dispatch(
setMultiSelectMode([
{
type: quickItemMode ? "quick-item" : "file",
id: file._id,
file: file,
folder: null,
},
])
);
}
};
@@ -348,7 +165,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
<div>
{!parentBarMode && (
<div
onClick={selectItemMultiSelect}
onClick={() => onAction("multi-select")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary rounded-t-md"
>
<MultiSelectIcon className="w-5 h-5" />
@@ -357,7 +174,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{!isTrash && !isMedia && (
<div
onClick={renameItem}
onClick={() => onAction("rename")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<RenameIcon className="w-5 h-5" />
@@ -366,7 +183,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{!folderMode && !isTrash && (
<div
onClick={openShareItemModal}
onClick={() => onAction("share")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<ShareIcon className="w-5 h-5" />
@@ -375,7 +192,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{!isTrash && (
<div
onClick={downloadItem}
onClick={() => onAction("download")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<DownloadIcon className="w-5 h-5" />
@@ -384,7 +201,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{!isTrash && !isMedia && (
<div
onClick={openMoveItemModal}
onClick={() => onAction("move")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<MoveIcon className="w-5 h-5" />
@@ -393,7 +210,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{!isTrash && (
<div
onClick={trashItem}
onClick={() => onAction("trash")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary rounded-b-md"
>
<TrashIcon className="w-5 h-5" />
@@ -402,7 +219,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{isTrash && (
<div
onClick={restoreItem}
onClick={() => onAction("restore")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<RestoreIcon className="w-5 h-5" />
@@ -411,7 +228,7 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
)}
{isTrash && (
<div
onClick={deleteItem}
onClick={() => onAction("delete")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-red-500 rounded-b-md"
>
<TrashIcon className="w-5 h-5" />
+2 -2
View File
@@ -118,8 +118,8 @@ const DataForm = memo(
{isTrash && (
<div
className="bg-primary p-4 rounded-md text-sm
text-white mt-4 flex items-center select-none"
className="bg-light-primary p-4 rounded-md text-sm
text-black mt-4 flex items-center select-none"
>
<AlertIcon className="w-6 h-6 min-h-6 min-w-6 mr-2" />
<span>
+17 -12
View File
@@ -13,6 +13,8 @@ import ActionsIcon from "../../icons/ActionsIcon";
import { FileInterface } from "../../types/file";
import getBackendURL from "../../utils/getBackendURL";
import dayjs from "dayjs";
import CalendarIcon from "../../icons/CalendarIcon";
import ClockIcon from "../../icons/ClockIcon";
interface FileItemProps {
file: FileInterface;
@@ -168,7 +170,7 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
return (
<div
className={classNames(
"border rounded-md o transition-all duration-400 ease-in-out cursor-pointer flex items-center justify-center flex-col h-[130px] sm:h-[150px] animiate hover:border-primary overflow-hidden bg-white ",
"border rounded-md o transition-all duration-400 ease-in-out cursor-pointer flex items-center justify-center flex-col h-[150px] animiate hover:border-primary overflow-hidden bg-white ",
elementSelected || elementMultiSelected
? "border-primary"
: "border-gray-third"
@@ -250,7 +252,7 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
>
<p
className={classNames(
"text-[14px] leading-[16px] font-normal max-w-full overflow-hidden text-ellipsis whitespace-nowrap animate mb-1 sm:mb-0",
"text-[14px] leading-[16px] font-normal max-w-full overflow-hidden text-ellipsis whitespace-nowrap animate mb-0",
elementSelected || elementMultiSelected
? "text-white"
: "text-[#212b36]"
@@ -258,16 +260,19 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
>
{formattedFilename}
</p>
<span
className={classNames(
"m-0 font-normal max-w-full whitespace-nowrap text-xs animate hidden sm:block mt-1",
elementSelected || elementMultiSelected
? "text-white"
: "text-gray-primary]"
)}
>
Created {formattedCreatedDate}
</span>
<div className="flex flex-row items-center mt-2">
<ClockIcon className="h-4 w-4 mr-1" />
<p
className={classNames(
"m-0 font-normal max-w-full whitespace-nowrap text-xs animate block",
elementSelected || elementMultiSelected
? "text-white"
: "text-gray-primary]"
)}
>
{formattedCreatedDate}
</p>
</div>
</div>
</div>
);
+14 -10
View File
@@ -12,6 +12,7 @@ import {
import { useUtils } from "../../hooks/utils";
import { FolderInterface } from "../../types/folders";
import dayjs from "dayjs";
import ClockIcon from "../../icons/ClockIcon";
interface FolderItemProps {
folder: FolderInterface;
@@ -163,16 +164,19 @@ const FolderItem: React.FC<FolderItemProps> = memo((props) => {
>
{folder.name}
</p>
<span
className={classNames(
"m-0 font-normal max-w-full whitespace-nowrap text-xs animate hidden sm:block mt-1",
elementSelected || elementMultiSelected
? "text-white"
: "text-gray-primary"
)}
>
Created {dayjs(folder.createdAt).format("MM/DD/YY hh:mma")}
</span>
<div className="flex flex-row items-center mt-1">
<ClockIcon className="h-4 w-4 mr-1" />
<p
className={classNames(
"m-0 font-normal max-w-full whitespace-nowrap text-xs animate",
elementSelected || elementMultiSelected
? "text-white"
: "text-gray-primary"
)}
>
{dayjs(folder.createdAt).format("MM/DD/YY hh:mma")}
</p>
</div>
</div>
</div>
);
+4 -4
View File
@@ -111,7 +111,7 @@ const LeftSection = ({
}
)}
>
<div className="flex flex-col h-full select-none">
<div className="flex flex-col h-full select-none text-sm">
<div>
<div className="relative mb-7">
<a
@@ -148,7 +148,7 @@ const LeftSection = ({
onClick={goMedia}
>
<PhotoIcon className="w-6 h-6" />
<p className="ml-3">Media</p>
<p className="ml-2.5">Media</p>
</div>
<div
@@ -159,7 +159,7 @@ const LeftSection = ({
onClick={goSettings}
>
<SettingsIcon className="w-6 h-6" />
<p className="ml-3">Settings</p>
<p className="ml-2.5">Settings</p>
</div>
<div
@@ -170,7 +170,7 @@ const LeftSection = ({
onClick={goTrash}
>
<TrashIcon className="w-6 h-6" />
<p className="ml-3">Trash</p>
<p className="ml-2.5">Trash</p>
</div>
</div>
</div>
+1 -1
View File
@@ -86,7 +86,7 @@ const MediaItem: React.FC<MediaItemType> = memo(({ file }) => {
return (
<div
className={classNames(
"h-[100px] bg-black overflow-hidden rounded-md cursor-pointer relative",
"h-[100px] bg-black overflow-hidden cursor-pointer relative",
{
"border-4 border-[#3c85ee]": elementSelected || elementMultiSelected,
}
+1 -1
View File
@@ -139,7 +139,7 @@ const Medias = memo(
{!isLoadingFiles && (
<div
className={classNames(
"grid grid-cols-[repeat(auto-fill,minmax(100px,1fr))] gap-[10px]"
"grid grid-cols-[repeat(auto-fill,minmax(100px,1fr))] gap-[2px]"
)}
>
<div className="fixed bottom-0 flex justify-center items-center right-0 left-0 z-10">
+2 -2
View File
@@ -35,8 +35,8 @@ const QuickAccess = memo(() => {
? "justify-normal"
: "justify-center xs:justify-normal",
{
"max-h-36 sm:max-h-40": !quickAccessExpanded,
"max-h-[720px] sm:max-h-[665px] quickAccessOne:max-h-[1000px] quickAccessTwo:max-h-[660px] quickAccessThree:max-h-[490px]":
"max-h-40": !quickAccessExpanded,
"max-h-[655px] quickAccessOne:max-h-[990px] quickAccessTwo:max-h-[660px] quickAccessThree:max-h-[490px]":
quickAccessExpanded,
}
)}
@@ -15,6 +15,7 @@ import {
import PlayButtonIcon from "../../icons/PlayIcon";
import dayjs from "dayjs";
import getBackendURL from "../../utils/getBackendURL";
import ClockIcon from "../../icons/ClockIcon";
interface QuickAccessItemProps {
file: FileInterface;
@@ -98,7 +99,7 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
return (
<div
className={classNames(
"border rounded-md o transition-all duration-400 ease-in-out cursor-pointer flex items-center justify-center flex-col h-[130px] sm:h-[150px] animiate hover:border-[#3c85ee] overflow-hidden bg-white",
"border rounded-md o transition-all duration-400 ease-in-out cursor-pointer flex items-center justify-center flex-col h-[150px] animiate hover:border-[#3c85ee] overflow-hidden bg-white",
elementSelected || elementMultiSelected
? "border-[#3c85ee]"
: "border-gray-third"
@@ -180,7 +181,7 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
>
<p
className={classNames(
"text-[14px] leading-[16px] font-normal max-w-full overflow-hidden text-ellipsis whitespace-nowrap animate mb-1 sm:mb-0",
"text-[14px] leading-[16px] font-normal max-w-full overflow-hidden text-ellipsis whitespace-nowrap animate mb-0",
elementSelected || elementMultiSelected
? "text-white"
: "text-[#212b36]"
@@ -188,16 +189,19 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
>
{formattedFilename}
</p>
<span
className={classNames(
"text-[#637381] font-normal max-w-full whitespace-nowrap text-xs animate hidden sm:block mt-1",
elementSelected || elementMultiSelected
? "text-white"
: "text-[#637381]"
)}
>
Created {formattedDate}
</span>
<div className="flex flex-row items-center mt-2">
<ClockIcon className="h-4 w-4 mr-1" />
<p
className={classNames(
"m-0 font-normal max-w-full whitespace-nowrap text-xs animate",
elementSelected || elementMultiSelected
? "text-white"
: "text-gray-primary]"
)}
>
{formattedDate}
</p>
</div>
</div>
</div>
);
+160 -62
View File
@@ -8,10 +8,56 @@ import { resetSelected } from "../../reducers/selected";
import CloseIcon from "../../icons/CloseIcon";
import FileDetailsIcon from "../../icons/FileDetailsIcon";
import dayjs from "dayjs";
import DownloadIcon from "../../icons/DownloadIcon";
import ShareIcon from "../../icons/ShareIcon";
import TrashIcon from "../../icons/TrashIcon";
import RenameIcon from "../../icons/RenameIcon";
import { useActions } from "../../hooks/actions";
import RestoreIcon from "../../icons/RestoreIcon";
const RightSection = memo(() => {
const selectedItem = useAppSelector((state) => state.selected.mainSection);
const dispatch = useDispatch();
const {
renameItem,
trashItem,
deleteItem,
restoreItem,
openShareItemModal,
downloadItem,
} = useActions({
quickItemMode: false,
});
const onAction = async (
action: "rename" | "trash" | "delete" | "restore" | "download" | "share"
) => {
const file = selectedItem.file;
const folder = selectedItem.folder;
switch (action) {
case "rename":
await renameItem(file, folder);
break;
case "trash":
await trashItem(file, folder);
break;
case "delete":
await deleteItem(file, folder);
break;
case "restore":
await restoreItem(file, folder);
break;
case "download":
downloadItem(file, folder);
break;
case "share":
openShareItemModal(file);
}
};
const itemTrashed =
selectedItem?.file?.metadata.trashed || selectedItem?.folder?.trashed;
const formattedName = useMemo(() => {
if (!selectedItem.id) return "";
@@ -81,79 +127,131 @@ const RightSection = memo(() => {
>
{selectedItem.id === "" ? (
<div className="flex flex-col justify-center items-center text-center">
<FileDetailsIcon />
<p className="text-[#637381] text-[16px] leading-[24px] font-normal m-0 mt-[30px]">
<FileDetailsIcon className="w-10 h-10" />
<p className="text-gray-500 text-xs mt-4">
Select a file or folder to view its details
</p>
</div>
) : (
<div className="w-full">
<div className="flex flex-row">
<div></div>
<div className="w-full h-16 flex items-center relative">
<div
className="opacity-40 w-full h-full absolute"
style={{ background: bannerBackgroundColor }}
></div>
<p className="text-sm ml-6 z-10">{bannerText}</p>
<div className="w-full flex flex-col justify-between">
<div>
<div className="flex flex-row">
<div></div>
<div className="w-full h-16 flex items-center relative">
<div
className="opacity-40 w-full h-full absolute"
style={{ background: bannerBackgroundColor }}
></div>
<p className="text-sm ml-6 z-10">{bannerText}</p>
</div>
<CloseIcon
className="w-5 h-5 p-1 cursor-pointer absolute right-3 top-5 bg-white rounded-full shadow-lg z-10"
onClick={reset}
/>
</div>
<CloseIcon
className="w-5 h-5 p-1 cursor-pointer absolute right-3 top-5 bg-white rounded-full shadow-lg z-10"
onClick={reset}
/>
</div>
<div className="p-6">
<div className="mb-5">
<p className="m-0 text-[#212b36] text-[16px] font-bold max-h-[90px] overflow-hidden text-ellipsis block break-all">
<div className="p-6">
<p className="m-0 text-[#212b36] text-sm font-bold max-h-[90px] overflow-hidden text-ellipsis block break-all">
{formattedName}
</p>
</div>
<div>
<div className="flex mb-[7px] justify-start">
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Type
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{selectedItem.file ? fileExtension : "Folder"}
</span>
</div>
<div
className="flex mb-[7px] justify-start"
style={
!selectedItem.file ? { display: "none" } : { display: "flex" }
}
>
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Size
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{fileSize}
</span>
</div>
<div className="flex mb-[7px] justify-start">
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Created
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{formattedDate}
</span>
</div>
<div
className="flex mb-[7px] justify-start"
style={
!selectedItem.file ? { display: "none" } : { display: "flex" }
}
>
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Access
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{selectedItem.file?.metadata.link ? "Public" : "Private"}
</span>
<div className="p-6 pt-0">
<p className="text-sm mb-4">Information</p>
<div className="flex flex-col space-y-2">
<div className="flex justify-between border-b border-gray-200 pb-2">
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Type
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{selectedItem.file ? fileExtension : "Folder"}
</span>
</div>
<div
className="flex justify-between border-b border-gray-200 pb-2"
style={
!selectedItem.file
? { display: "none" }
: { display: "flex" }
}
>
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Size
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{fileSize}
</span>
</div>
<div className="flex justify-between border-b border-gray-200 pb-2">
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Created
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{formattedDate}
</span>
</div>
<div
className="flex justify-between border-b border-gray-200 pb-2"
style={
!selectedItem.file
? { display: "none" }
: { display: "flex" }
}
>
<span className="text-[#637381] text-[13px] font-normal mr-[35px] leading-[20px] min-w-[50px]">
Access
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{selectedItem.file?.metadata.link ? "Public" : "Private"}
</span>
</div>
</div>
</div>
</div>
{/* <div
className="w-full p-0.5 opacity-40 h-10 absolute bottom-0"
style={{ background: bannerBackgroundColor }}
></div>
<div className="flex flex-row justify-between w-full z-10">
{!itemTrashed && (
<div
className="text-gray-600 hover:text-black p-2.5 rounded-full flex items-center justify-center cursor-pointer"
onClick={() => onAction("download")}
>
<DownloadIcon className="w-5 h-5" />
</div>
)}
{selectedItem.file && !itemTrashed && (
<div
className="text-gray-600 hover:text-black p-2.5 rounded-full flex items-center justify-center cursor-pointer"
onClick={() => onAction("share")}
>
<ShareIcon className="w-5 h-5" />
</div>
)}
{itemTrashed && (
<div
className="text-gray-600 hover:text-black p-2.5 rounded-full flex items-center justify-center cursor-pointer"
onClick={() => onAction("restore")}
>
<RestoreIcon className="w-5 h-5" />
</div>
)}
<div
className="text-gray-600 hover:text-black p-2.5 rounded-full flex items-center justify-center cursor-pointer"
onClick={() => onAction(itemTrashed ? "delete" : "trash")}
>
<TrashIcon className="w-5 h-5" />
</div>
{!itemTrashed && (
<div
className="text-gray-600 hover:text-black p-2.5 rounded-full flex items-center justify-center cursor-pointer"
onClick={() => onAction("rename")}
>
<RenameIcon className="w-5 h-5" />
</div>
)}
</div> */}
</div>
)}
</div>
+267
View File
@@ -0,0 +1,267 @@
import { toast } from "react-toastify";
import {
deleteFileAPI,
downloadFileAPI,
renameFileAPI,
restoreFileAPI,
trashFileAPI,
} from "../api/filesAPI";
import {
deleteFilePopup,
renameFilePopup,
restoreItemPopup,
trashItemsPopup,
} from "../popups/file";
import { FileInterface } from "../types/file";
import { FolderInterface } from "../types/folders";
import { useFiles, useQuickFiles } from "./files";
import { useFolder, useFolders } from "./folders";
import { useAppDispatch } from "./store";
import {
resetSelected,
setMoveModal,
setMultiSelectMode,
setShareModal,
} from "../reducers/selected";
import { deleteFolderPopup, renameFolderPopup } from "../popups/folder";
import {
deleteFolderAPI,
downloadZIPAPI,
renameFolder,
restoreFolderAPI,
trashFolderAPI,
} from "../api/foldersAPI";
type UseActionsProps = {
quickItemMode?: boolean;
};
export const useActions = ({ quickItemMode }: UseActionsProps) => {
const { refetch: refetchFiles } = useFiles(false);
const { refetch: refetchFolders } = useFolders(false);
const { refetch: refetchFolder } = useFolder(false);
const { refetch: refetchQuickFiles } = useQuickFiles(false);
const dispatch = useAppDispatch();
const reloadItems = () => {
refetchFiles();
refetchQuickFiles();
refetchFolders();
refetchFolder();
dispatch(resetSelected());
};
const renameItem = async (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
if (file) {
try {
const filename = await renameFilePopup(file.filename);
if (!filename || filename === file.filename) return;
await toast.promise(renameFileAPI(file._id, filename), {
pending: "Renaming...",
success: "Renamed",
error: "Error Renaming",
});
reloadItems();
} catch (e) {
console.log("Error renaming file", e);
}
} else if (folder) {
try {
const folderName = await renameFolderPopup(folder.name);
if (!folderName || folderName === folder.name) return;
await toast.promise(renameFolder(folder._id, folderName), {
pending: "Renaming...",
success: "Renamed",
error: "Error Renaming",
});
reloadItems();
} catch (e) {
console.log("Error renaming folder", e);
}
}
};
const trashItem = async (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
if (file) {
try {
const result = await trashItemsPopup();
if (!result) return;
await toast.promise(trashFileAPI(file._id), {
pending: "Trashing...",
success: "Trashed",
error: "Error Trashing",
});
reloadItems();
} catch (e) {
console.log("Error trashing file", e);
}
} else if (folder) {
try {
const result = await trashItemsPopup();
if (!result) return;
await toast.promise(trashFolderAPI(folder._id), {
pending: "Trashing...",
success: "Trashed",
error: "Error Trashing",
});
reloadItems();
// if (parentBarMode) {
// if (folder.parent === "/") {
// navigate("/home");
// } else {
// navigate(`/folder/${folder.parent}`);
// }
// }
} catch (e) {
console.log("Error trashing folder", e);
}
}
};
const deleteItem = async (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
if (file) {
try {
const result = await deleteFilePopup();
if (!result) return;
await toast.promise(deleteFileAPI(file._id), {
pending: "Deleting...",
success: "Deleted",
error: "Error Deleting",
});
reloadItems();
} catch (e) {
console.log("Error deleting file", e);
}
} else if (folder) {
try {
const result = await deleteFolderPopup();
if (!result) return;
await toast.promise(deleteFolderAPI(folder._id), {
pending: "Deleting...",
success: "Deleted",
error: "Error Deleting",
});
reloadItems();
// if (parentBarMode) {
// if (folder.parent === "/") {
// navigate("/trash");
// } else {
// navigate(`/folder-trash/${folder.parent}`);
// }
// }
} catch (e) {
console.log("Error deleting folder", e);
}
}
};
const restoreItem = async (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
const result = await restoreItemPopup();
if (!result) return;
if (file) {
try {
await toast.promise(restoreFileAPI(file._id), {
pending: "Restoring...",
success: "Restored",
error: "Error Restoring",
});
reloadItems();
} catch (e) {
console.log("Error restoring file", e);
}
} else if (folder) {
try {
await toast.promise(restoreFolderAPI(folder._id), {
pending: "Restoring...",
success: "Restored",
error: "Error Restoring",
});
reloadItems();
} catch (e) {
console.log("Error restoring folder", e);
}
}
};
const openMoveItemModal = async (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
if (file) {
dispatch(setMoveModal({ type: "file", file, folder: null }));
} else if (folder) {
dispatch(setMoveModal({ type: "folder", file: null, folder }));
}
};
const openShareItemModal = (file?: FileInterface | null) => {
dispatch(setShareModal(file!));
};
const downloadItem = (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
if (file) downloadFileAPI(file._id);
if (folder) downloadZIPAPI([folder._id], []);
};
const selectItemMultiSelect = (
file?: FileInterface | null,
folder?: FolderInterface | null
) => {
if (folder) {
dispatch(
setMultiSelectMode([
{
type: "folder",
id: folder._id,
file: null,
folder: folder,
},
])
);
} else if (file) {
dispatch(
setMultiSelectMode([
{
type: quickItemMode ? "quick-item" : "file",
id: file._id,
file: file,
folder: null,
},
])
);
}
};
return {
renameItem,
trashItem,
deleteItem,
restoreItem,
openMoveItemModal,
openShareItemModal,
downloadItem,
selectItemMultiSelect,
};
};
+17
View File
@@ -0,0 +1,17 @@
type ClockIconType = React.SVGAttributes<SVGSVGElement>;
const ClockIcon: React.FC<ClockIconType> = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
{...props}
fill="currentColor"
>
<title>clock-time-eight-outline</title>
<path d="M12 20C16.4 20 20 16.4 20 12S16.4 4 12 4 4 7.6 4 12 7.6 20 12 20M12 2C17.5 2 22 6.5 22 12S17.5 22 12 22C6.5 22 2 17.5 2 12C2 6.5 6.5 2 12 2M12.5 12.8L7.7 15.6L7 14.2L11 11.9V7H12.5V12.8Z" />
</svg>
);
};
export default ClockIcon;
+1
View File
@@ -18,6 +18,7 @@ export interface FileInterface {
filePath?: string;
s3ID?: string;
personalFile?: boolean;
trashed?: boolean;
};
}
interface example {
+1
View File
@@ -7,4 +7,5 @@ export interface FolderInterface {
updatedAt: Date;
parentList: string[];
personalFolder?: boolean;
trashed?: boolean;
}