code cleanup and fixes
This commit is contained in:
+6
-1
@@ -92,10 +92,15 @@ export const getFileThumbnailAPI = async (thumbnailID: string) => {
|
||||
|
||||
export const getFileFullThumbnailAPI = async (fileID: string) => {
|
||||
const url = `http://localhost:5173/api/file-service/full-thumbnail/${fileID}`;
|
||||
|
||||
const response = await axios.get(url, {
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
return response.data;
|
||||
|
||||
const imgFile = new Blob([response.data]);
|
||||
const imgUrl = URL.createObjectURL(imgFile);
|
||||
|
||||
return imgUrl;
|
||||
};
|
||||
|
||||
export const getVideoTokenAPI = async () => {
|
||||
|
||||
@@ -56,24 +56,24 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
|
||||
multiple={true}
|
||||
onChange={handleUpload}
|
||||
/>
|
||||
<ul className="pl-0 list-none m-0 rounded-[5px] overflow-hidden shadow-md">
|
||||
<ul className="rounded-sm overflow-hidden shadow-lg">
|
||||
<li>
|
||||
<div>
|
||||
<a
|
||||
className="flex items-center justify-start p-[12px_20px] no-underline rounded-[5px] overflow-hidden text-[15px] leading-[18px] bg-white hover:bg-[#f6f5fd]"
|
||||
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-[20px] h-[20px] mr-[10px] text-[#3c85ee]" />
|
||||
<UploadFileIcon className="w-5 h-5 mr-2.5 text-primary" />
|
||||
<p className="text-sm">Upload Files</p>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="flex items-center justify-start p-[12px_20px] no-underline rounded-[5px] overflow-hidden text-[15px] leading-[18px] bg-white hover:bg-[#f6f5fd]"
|
||||
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-[20px] h-[20px] mr-[10px] text-[#3c85ee]" />
|
||||
<CreateFolderIcon className="w-5 h-5 mr-2.5 text-primary" />
|
||||
<p className="text-sm">Create Folder</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -61,20 +61,26 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
const { invalidateFilesCache } = useFilesClient();
|
||||
const { invalidateFoldersCache } = useFoldersClient();
|
||||
const { invalidateQuickFilesCache } = useQuickFilesClient();
|
||||
const { wrapperRef } = useClickOutOfBounds(props.closeContext);
|
||||
const {
|
||||
closeContext,
|
||||
contextSelected,
|
||||
folderMode,
|
||||
file,
|
||||
quickItemMode,
|
||||
stopPropagation,
|
||||
folder,
|
||||
} = props;
|
||||
const { wrapperRef } = useClickOutOfBounds(closeContext);
|
||||
const { isTrash, isMedia } = useUtils();
|
||||
const dispatch = useAppDispatch();
|
||||
const liClassname =
|
||||
"flex w-full px-[20px] py-[12px] items-center font-normal text-[#637381] justify-start no-underline animate hover:bg-[#f6f5fd] hover:text-[#3c85ee] hover:font-medium";
|
||||
const spanClassname = "flex items-center justify-center mr-[18px]";
|
||||
|
||||
const renameItem = async () => {
|
||||
props.closeContext();
|
||||
if (!props.folderMode && props.file) {
|
||||
closeContext();
|
||||
if (!folderMode && file) {
|
||||
try {
|
||||
const filename = await renameFilePopup(props.file.filename);
|
||||
if (!filename || filename === props.file.filename) return;
|
||||
await toast.promise(renameFileAPI(props.file._id, filename), {
|
||||
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",
|
||||
@@ -84,11 +90,11 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
} catch (e) {
|
||||
console.log("Error renaming file", e);
|
||||
}
|
||||
} else if (props.folderMode && props.folder) {
|
||||
} else if (folderMode && folder) {
|
||||
try {
|
||||
const folderName = await renameFolderPopup(props.folder.name);
|
||||
if (!folderName || folderName === props.folder.name) return;
|
||||
await toast.promise(renameFolder(props.folder._id, folderName), {
|
||||
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",
|
||||
@@ -101,13 +107,13 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
};
|
||||
|
||||
const trashItem = async () => {
|
||||
props.closeContext();
|
||||
if (!props.folderMode && props.file) {
|
||||
closeContext();
|
||||
if (!folderMode && file) {
|
||||
try {
|
||||
const result = await trashItemsPopup();
|
||||
if (!result) return;
|
||||
|
||||
await toast.promise(trashFileAPI(props.file._id), {
|
||||
await toast.promise(trashFileAPI(file._id), {
|
||||
pending: "Trashing...",
|
||||
success: "Trashed",
|
||||
error: "Error Trashing",
|
||||
@@ -118,12 +124,12 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
} catch (e) {
|
||||
console.log("Error trashing file", e);
|
||||
}
|
||||
} else if (props.folderMode && props.folder) {
|
||||
} else if (folderMode && folder) {
|
||||
try {
|
||||
const result = await trashItemsPopup();
|
||||
if (!result) return;
|
||||
|
||||
await toast.promise(trashFolderAPI(props.folder._id), {
|
||||
await toast.promise(trashFolderAPI(folder._id), {
|
||||
pending: "Trashing...",
|
||||
success: "Trashed",
|
||||
error: "Error Trashing",
|
||||
@@ -137,13 +143,13 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
};
|
||||
|
||||
const deleteItem = async () => {
|
||||
props.closeContext();
|
||||
if (!props.folderMode && props.file) {
|
||||
closeContext();
|
||||
if (!folderMode && file) {
|
||||
try {
|
||||
const result = await deleteFilePopup();
|
||||
if (!result) return;
|
||||
|
||||
await toast.promise(deleteFileAPI(props.file._id), {
|
||||
await toast.promise(deleteFileAPI(file._id), {
|
||||
pending: "Deleting...",
|
||||
success: "Deleted",
|
||||
error: "Error Deleting",
|
||||
@@ -154,12 +160,12 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
} catch (e) {
|
||||
console.log("Error deleting file", e);
|
||||
}
|
||||
} else if (props.folderMode && props.folder) {
|
||||
} else if (folderMode && folder) {
|
||||
try {
|
||||
const result = await deleteFolderPopup();
|
||||
if (!result) return;
|
||||
|
||||
await toast.promise(deleteFolderAPI(props.folder._id), {
|
||||
await toast.promise(deleteFolderAPI(folder._id), {
|
||||
pending: "Deleting...",
|
||||
success: "Deleted",
|
||||
error: "Error Deleting",
|
||||
@@ -173,12 +179,12 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
};
|
||||
|
||||
const restoreItem = async () => {
|
||||
props.closeContext();
|
||||
closeContext();
|
||||
const result = await restoreItemPopup();
|
||||
if (!result) return;
|
||||
if (!props.folderMode && props.file) {
|
||||
if (!folderMode && file) {
|
||||
try {
|
||||
await toast.promise(restoreFileAPI(props.file._id), {
|
||||
await toast.promise(restoreFileAPI(file._id), {
|
||||
pending: "Restoring...",
|
||||
success: "Restored",
|
||||
error: "Error Restoring",
|
||||
@@ -187,9 +193,9 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
} catch (e) {
|
||||
console.log("Error restoring file", e);
|
||||
}
|
||||
} else if (props.folderMode && props.folder) {
|
||||
} else if (folderMode && folder) {
|
||||
try {
|
||||
await toast.promise(restoreFolderAPI(props.folder._id), {
|
||||
await toast.promise(restoreFolderAPI(folder._id), {
|
||||
pending: "Restoring...",
|
||||
success: "Restored",
|
||||
error: "Error Restoring",
|
||||
@@ -202,41 +208,41 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
};
|
||||
|
||||
const openMoveItemModal = async () => {
|
||||
props.closeContext();
|
||||
if (!props.folderMode && props.file) {
|
||||
dispatch(setMoverID(props.file._id, props.file.metadata.parent, true));
|
||||
} else if (props.folderMode && props.folder) {
|
||||
dispatch(setMoverID(props.folder._id, props.folder.parent, false));
|
||||
closeContext();
|
||||
if (!folderMode && file) {
|
||||
dispatch(setMoverID(file._id, file.metadata.parent, true));
|
||||
} else if (folderMode && folder) {
|
||||
dispatch(setMoverID(folder._id, folder.parent, false));
|
||||
}
|
||||
};
|
||||
|
||||
const openShareItemModal = () => {
|
||||
props.closeContext();
|
||||
dispatch(setShareModal(props.file!));
|
||||
closeContext();
|
||||
dispatch(setShareModal(file!));
|
||||
};
|
||||
|
||||
const downloadItem = () => {
|
||||
props.closeContext();
|
||||
if (props.file) downloadFileAPI(props.file._id);
|
||||
closeContext();
|
||||
if (file) downloadFileAPI(file._id);
|
||||
};
|
||||
|
||||
const selectItemMultiSelect = () => {
|
||||
props.closeContext();
|
||||
if (props.folderMode && props.folder) {
|
||||
closeContext();
|
||||
if (folderMode && folder) {
|
||||
dispatch(
|
||||
setMultiSelectMode({
|
||||
type: "folder",
|
||||
id: props.folder._id,
|
||||
id: folder._id,
|
||||
file: null,
|
||||
folder: props.folder,
|
||||
folder: folder,
|
||||
})
|
||||
);
|
||||
} else if (!props.folderMode && props.file) {
|
||||
} else if (!folderMode && file) {
|
||||
dispatch(
|
||||
setMultiSelectMode({
|
||||
type: props.quickItemMode ? "quick-item" : "file",
|
||||
id: props.file._id,
|
||||
file: props.file,
|
||||
type: quickItemMode ? "quick-item" : "file",
|
||||
id: file._id,
|
||||
file: file,
|
||||
folder: null,
|
||||
})
|
||||
);
|
||||
@@ -245,105 +251,91 @@ const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={props.stopPropagation}
|
||||
onClick={stopPropagation}
|
||||
ref={wrapperRef}
|
||||
className={classNames(
|
||||
"fixed min-w-[215px] bg-white shadow-[0px_2px_4px_rgba(0,0,0,0.15),_inset_0px_1px_0px_#f5f7fa] rounded-[4px] mt-[-5px] z-50 ",
|
||||
props.contextSelected.selected ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
className="fixed min-w-[215px] bg-white shadow-lg rounded-md z-50"
|
||||
style={
|
||||
props.contextSelected.selected
|
||||
contextSelected.selected
|
||||
? {
|
||||
display: "block",
|
||||
left: `${props.contextSelected.X}px`,
|
||||
top: `${props.contextSelected.Y}px`,
|
||||
left: `${contextSelected.X}px`,
|
||||
top: `${contextSelected.Y}px`,
|
||||
}
|
||||
: { display: "none" }
|
||||
}
|
||||
>
|
||||
<ul className="p-0 list-none m-0 ">
|
||||
<li onClick={selectItemMultiSelect} className={liClassname}>
|
||||
<a className="flex">
|
||||
<span className={spanClassname}>
|
||||
<MultiSelectIcon className="w-[19px] h-[20px]" />
|
||||
</span>
|
||||
Multi-select
|
||||
</a>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
onClick={selectItemMultiSelect}
|
||||
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" />
|
||||
<p className="ml-2.5 text-sm">Multi-select</p>
|
||||
</div>
|
||||
{!isTrash && !isMedia && (
|
||||
<li onClick={renameItem} className={liClassname}>
|
||||
<a className="flex">
|
||||
<span className={spanClassname}>
|
||||
<RenameIcon />
|
||||
</span>
|
||||
Rename
|
||||
</a>
|
||||
</li>
|
||||
<div
|
||||
onClick={renameItem}
|
||||
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
|
||||
>
|
||||
<RenameIcon className="w-5 h-5" />
|
||||
<p className="ml-2.5 text-sm">Rename</p>
|
||||
</div>
|
||||
)}
|
||||
{!folderMode && !isTrash && (
|
||||
<div
|
||||
onClick={openShareItemModal}
|
||||
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
|
||||
>
|
||||
<ShareIcon className="w-5 h-5" />
|
||||
<p className="ml-2.5 text-sm">Share</p>
|
||||
</div>
|
||||
)}
|
||||
{!folderMode && !isTrash && (
|
||||
<div
|
||||
onClick={downloadItem}
|
||||
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
|
||||
>
|
||||
<DownloadIcon className="w-5 h-5" />
|
||||
<p className="ml-2.5 text-sm">Download</p>
|
||||
</div>
|
||||
)}
|
||||
{!props.folderMode && !isTrash ? (
|
||||
<li onClick={openShareItemModal} className={liClassname}>
|
||||
<a className="flex" data-modal="share__modal">
|
||||
<span className="inline-flex mr-[18px]">
|
||||
<ShareIcon />
|
||||
</span>
|
||||
Share
|
||||
</a>
|
||||
</li>
|
||||
) : undefined}
|
||||
{!props.folderMode && !isTrash ? (
|
||||
<li onClick={downloadItem} className={liClassname}>
|
||||
<a className="flex">
|
||||
<span className={spanClassname}>
|
||||
<DownloadIcon />
|
||||
</span>
|
||||
Download
|
||||
</a>
|
||||
</li>
|
||||
) : undefined}
|
||||
{!isTrash && !isMedia && (
|
||||
<li onClick={openMoveItemModal} className={liClassname}>
|
||||
<a className="flex" data-modal="destination__modal">
|
||||
<span className={spanClassname}>
|
||||
<MoveIcon />
|
||||
</span>{" "}
|
||||
Move
|
||||
</a>
|
||||
</li>
|
||||
<div
|
||||
onClick={openMoveItemModal}
|
||||
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
|
||||
>
|
||||
<MoveIcon className="w-5 h-5" />
|
||||
<p className="ml-2.5 text-sm">Move</p>
|
||||
</div>
|
||||
)}
|
||||
{!isTrash && (
|
||||
<li onClick={trashItem} className={liClassname}>
|
||||
<a className="flex">
|
||||
<span className={spanClassname}>
|
||||
<TrashIcon />
|
||||
</span>
|
||||
Trash
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{isTrash && (
|
||||
<li onClick={restoreItem} className={liClassname}>
|
||||
<a className="flex">
|
||||
<span className={spanClassname}>
|
||||
<RestoreIcon className="w-[19px] h-[20px]" />
|
||||
</span>
|
||||
Restore
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{isTrash && (
|
||||
<li
|
||||
onClick={deleteItem}
|
||||
className={classNames(liClassname, "hover:text-red-500")}
|
||||
<div
|
||||
onClick={trashItem}
|
||||
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary rounded-b-md"
|
||||
>
|
||||
<a className="flex">
|
||||
<span className={spanClassname}>
|
||||
<TrashIcon />
|
||||
</span>
|
||||
Delete
|
||||
</a>
|
||||
</li>
|
||||
<TrashIcon className="w-5 h-5" />
|
||||
<p className="ml-2.5 text-sm">Trash</p>
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
{isTrash && (
|
||||
<div
|
||||
onClick={restoreItem}
|
||||
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
|
||||
>
|
||||
<RestoreIcon className="w-5 h-5" />
|
||||
<p className="ml-2.5 text-sm">Restore</p>
|
||||
</div>
|
||||
)}
|
||||
{isTrash && (
|
||||
<div
|
||||
onClick={deleteItem}
|
||||
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" />
|
||||
<p className="ml-2.5 text-sm">Delete</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -34,10 +34,7 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
|
||||
(state) => state.selected.multiSelectMode
|
||||
);
|
||||
const listView = useAppSelector((state) => state.filter.listView);
|
||||
const { image, hasThumbnail, imageOnError } = useThumbnail(
|
||||
file.metadata.hasThumbnail,
|
||||
file.metadata.thumbnailID
|
||||
);
|
||||
const { data: thumbnail } = useThumbnail(file.metadata.thumbnailID);
|
||||
const dispatch = useAppDispatch();
|
||||
const lastSelected = useRef(0);
|
||||
const {
|
||||
@@ -201,17 +198,13 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
|
||||
className={classNames(
|
||||
"inline-flex items-center w-full bg-white relative",
|
||||
{
|
||||
"mt-2": !hasThumbnail,
|
||||
"mt-2": !thumbnail,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{hasThumbnail ? (
|
||||
{!!thumbnail ? (
|
||||
<div className="w-full min-h-[88px] max-h-[88px] h-full flex">
|
||||
<img
|
||||
className="object-cover"
|
||||
src={image}
|
||||
onError={imageOnError}
|
||||
/>
|
||||
<img className="object-cover" src={thumbnail} />
|
||||
{file.metadata.isVideo && (
|
||||
<div className="w-full h-full absolute flex justify-center items-center text-white">
|
||||
<PlayButtonIcon className="w-[50px] h-[50px]" />
|
||||
@@ -234,7 +227,7 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
{!hasThumbnail && (
|
||||
{!thumbnail && (
|
||||
<div className="w-full h-full absolute flex justify-center items-center text-white mt-3">
|
||||
<p className="text-sm">{fileExtension}</p>
|
||||
</div>
|
||||
|
||||
@@ -10,27 +10,26 @@ import FileInfoPopup from "../FileInfoPopup";
|
||||
import SharePopup from "../SharePopup";
|
||||
|
||||
const MainSection = memo(() => {
|
||||
const selectedItem = useAppSelector((state) => state.selected.popupModal);
|
||||
const popupModalItem = useAppSelector(
|
||||
(state) => state.selected.popupModal.file
|
||||
);
|
||||
const shareModalItem = useAppSelector(
|
||||
(state) => state.selected.shareModal.file
|
||||
);
|
||||
|
||||
const isMediaSelected =
|
||||
popupModalItem?.metadata.isVideo || popupModalItem?.metadata.hasThumbnail;
|
||||
const isFileInfoSelected = !isMediaSelected && popupModalItem;
|
||||
|
||||
const { isMedia } = useUtils();
|
||||
return (
|
||||
<div>
|
||||
<div className="flex h-full">
|
||||
{/* {showPopup ? <PopupWindow /> : undefined} */}
|
||||
{selectedItem?.file &&
|
||||
(selectedItem.file.metadata.hasThumbnail ||
|
||||
selectedItem.file.metadata.isVideo) ? (
|
||||
<PhotoViewerPopup />
|
||||
) : undefined}
|
||||
{isMediaSelected && (
|
||||
<PhotoViewerPopup file={popupModalItem} key={popupModalItem._id} />
|
||||
)}
|
||||
|
||||
{selectedItem?.file &&
|
||||
!selectedItem.file.metadata.isVideo &&
|
||||
!selectedItem.file.metadata.hasThumbnail ? (
|
||||
<FileInfoPopup />
|
||||
) : undefined}
|
||||
{isFileInfoSelected && <FileInfoPopup />}
|
||||
|
||||
{shareModalItem && <SharePopup />}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
setPopupSelect,
|
||||
} from "../../reducers/selected";
|
||||
import mobilecheck from "../../utils/mobileCheck";
|
||||
import { setPopupFile } from "../../actions/popupFile";
|
||||
import classNames from "classnames";
|
||||
import { useContextMenu } from "../../hooks/contextMenu";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
@@ -42,10 +41,7 @@ const MediaItem: React.FC<MediaItemType> = memo(({ file }) => {
|
||||
} = useContextMenu();
|
||||
const lastSelected = useRef(0);
|
||||
const dispatch = useAppDispatch();
|
||||
const { image, imageOnError } = useThumbnail(
|
||||
file.metadata.hasThumbnail,
|
||||
file.metadata.thumbnailID
|
||||
);
|
||||
const { data: thumbnail } = useThumbnail(file.metadata.thumbnailID);
|
||||
|
||||
// TODO: See if we can memoize this and remove any
|
||||
const mediaItemClick = (e: any) => {
|
||||
@@ -110,11 +106,7 @@ const MediaItem: React.FC<MediaItemType> = memo(({ file }) => {
|
||||
<PlayButtonIcon className="w-[50px] h-[50px]" />
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
className="object-cover h-full w-full"
|
||||
src={image}
|
||||
onError={imageOnError}
|
||||
/>
|
||||
<img className="object-cover h-full w-full" src={thumbnail} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import classNames from "classnames";
|
||||
import React, { memo, useEffect, useMemo, useState } from "react";
|
||||
import MediaItem from "../MediaItem";
|
||||
import { useFiles, useThumbnail } from "../../hooks/files";
|
||||
import { useFiles } from "../../hooks/files";
|
||||
import MultiSelectBar from "../MultiSelectBar";
|
||||
import { useInfiniteScroll } from "../../hooks/infiniteScroll";
|
||||
|
||||
@@ -44,6 +44,7 @@ const Medias = memo(() => {
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
{/* @ts-ignore */}
|
||||
<div ref={sentinelRef} className="h-1"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -12,20 +12,27 @@ import ContextMenu from "../ContextMenu";
|
||||
import { resetPopupSelect, setPopupSelect } from "../../reducers/selected";
|
||||
import CircleLeftIcon from "../../icons/CircleLeftIcon";
|
||||
import CircleRightIcon from "../../icons/CircleRightIcon";
|
||||
import { useFiles, useQuickFiles } from "../../hooks/files";
|
||||
import { useFiles, useFullThumbnail, useQuickFiles } from "../../hooks/files";
|
||||
import { FileInterface } from "../../types/file";
|
||||
import { InfiniteData } from "react-query";
|
||||
import { getFileColor, getFileExtension } from "../../utils/files";
|
||||
import Spinner from "../Spinner";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
const PhotoViewerPopup = memo(() => {
|
||||
const [image, setImage] = useState("");
|
||||
interface PhotoViewerPopupProps {
|
||||
file: FileInterface;
|
||||
}
|
||||
|
||||
const PhotoViewerPopup: React.FC<PhotoViewerPopupProps> = memo((props) => {
|
||||
const { file } = props;
|
||||
const [video, setVideo] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isVideoLoading, setIsVideoLoading] = useState(false);
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
const file = useAppSelector((state) => state.selected.popupModal.file)!;
|
||||
const type = useAppSelector((state) => state.selected.popupModal.type)!;
|
||||
const { data: thumbnail, isLoading: isThumbnailLoading } = useFullThumbnail(
|
||||
file._id,
|
||||
file.metadata.isVideo
|
||||
);
|
||||
const finalLastPageLoaded = useRef(false);
|
||||
const loadingNextPage = useRef(false);
|
||||
const { data: quickFiles } = useQuickFiles(false);
|
||||
@@ -51,29 +58,16 @@ const PhotoViewerPopup = memo(() => {
|
||||
[file.filename]
|
||||
);
|
||||
|
||||
const getImage = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const imageData = await getFileFullThumbnailAPI(file._id);
|
||||
const imgFile = new Blob([imageData]);
|
||||
const imgUrl = URL.createObjectURL(imgFile);
|
||||
setImage(imgUrl);
|
||||
setLoading(false);
|
||||
} catch (e) {
|
||||
console.log("Error getting image", e);
|
||||
toast.error("Error getting image");
|
||||
}
|
||||
}, [file._id, getFileFullThumbnailAPI]);
|
||||
|
||||
const getVideo = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setIsVideoLoading(true);
|
||||
setVideo("");
|
||||
// TODO: Change this
|
||||
await getVideoTokenAPI();
|
||||
const videoURL = `http://localhost:5173/api/file-service/stream-video/${file._id}`;
|
||||
console.log("video url", videoURL);
|
||||
setVideo(videoURL);
|
||||
setLoading(false);
|
||||
setIsVideoLoading(false);
|
||||
} catch (e) {
|
||||
console.log("Error getting video", e);
|
||||
toast.error("Error getting video");
|
||||
@@ -225,14 +219,12 @@ const PhotoViewerPopup = memo(() => {
|
||||
useEffect(() => {
|
||||
if (file.metadata.isVideo) {
|
||||
getVideo();
|
||||
} else {
|
||||
getImage();
|
||||
}
|
||||
|
||||
return () => {
|
||||
cleanUpVideo();
|
||||
};
|
||||
}, [file._id, getVideo, getImage, cleanUpVideo]);
|
||||
}, [file.metadata.isVideo, getVideo, cleanUpVideo]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -296,14 +288,14 @@ const PhotoViewerPopup = memo(() => {
|
||||
/>
|
||||
</div>
|
||||
<div className="max-w-[80vw] max-h-[80vh] flex justify-center items-center">
|
||||
{loading && <Spinner />}
|
||||
{!file.metadata.isVideo && !loading && (
|
||||
{(isVideoLoading || isThumbnailLoading) && <Spinner />}
|
||||
{!file.metadata.isVideo && !isThumbnailLoading && (
|
||||
<img
|
||||
src={image}
|
||||
src={thumbnail}
|
||||
className="max-w-full max-h-full object-contain select-none"
|
||||
/>
|
||||
)}
|
||||
{file.metadata.isVideo && !loading && (
|
||||
{file.metadata.isVideo && !isVideoLoading && (
|
||||
<video
|
||||
src={video}
|
||||
ref={videoRef}
|
||||
|
||||
@@ -6,10 +6,7 @@ import classNames from "classnames";
|
||||
import { getFileColor, getFileExtension } from "../../utils/files";
|
||||
import { useThumbnail } from "../../hooks/files";
|
||||
import { useContextMenu } from "../../hooks/contextMenu";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import mobilecheck from "../../utils/mobileCheck";
|
||||
import { startSetSelectedItem } from "../../actions/selectedItem";
|
||||
import { setPopupFile } from "../../actions/popupFile";
|
||||
import { FileInterface } from "../../types/file";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks/store";
|
||||
import {
|
||||
@@ -37,11 +34,7 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
|
||||
const multiSelectMode = useAppSelector(
|
||||
(state) => state.selected.multiSelectMode
|
||||
);
|
||||
const { image, hasThumbnail, imageOnError } = useThumbnail(
|
||||
file.metadata.hasThumbnail,
|
||||
file.metadata.thumbnailID,
|
||||
true
|
||||
);
|
||||
const { data: thumbnail } = useThumbnail(file.metadata.thumbnailID, true);
|
||||
const dispatch = useAppDispatch();
|
||||
const lastSelected = useRef(0);
|
||||
|
||||
@@ -127,13 +120,13 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
|
||||
className={classNames(
|
||||
"inline-flex items-center w-full bg-white relative",
|
||||
{
|
||||
"mt-2": !hasThumbnail,
|
||||
"mt-2": !thumbnail,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{hasThumbnail ? (
|
||||
{!!thumbnail ? (
|
||||
<div className="w-full min-h-[88px] max-h-[88px] h-full flex">
|
||||
<img className=" object-cover" src={image} onError={imageOnError} />
|
||||
<img className=" object-cover" src={thumbnail} />
|
||||
{file.metadata.isVideo && (
|
||||
<div className="w-full h-full absolute flex justify-center items-center text-white">
|
||||
<PlayButtonIcon className="w-[50px] h-[50px]" />
|
||||
@@ -156,7 +149,7 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
{!hasThumbnail && (
|
||||
{!thumbnail && (
|
||||
<div className="w-full h-full absolute flex justify-center items-center text-white mt-3">
|
||||
<p className="text-sm">{fileExtension}</p>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ export const useContextMenu = () => {
|
||||
}
|
||||
|
||||
if (Y + contextHeight > windowHeight) {
|
||||
Y = windowHeight - contextHeight;
|
||||
Y = windowHeight - contextHeight - 60;
|
||||
}
|
||||
|
||||
setContextData({
|
||||
|
||||
+39
-53
@@ -8,6 +8,7 @@ import {
|
||||
} from "react-query";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
getFileFullThumbnailAPI,
|
||||
getFileThumbnailAPI,
|
||||
getFilesListAPI,
|
||||
getQuickFilesListAPI,
|
||||
@@ -127,65 +128,50 @@ export const useQuickFilesClient = () => {
|
||||
return { ...quickFilesReactClientQuery, invalidateQuickFilesCache };
|
||||
};
|
||||
|
||||
interface thumbnailState {
|
||||
hasThumbnail: boolean;
|
||||
image: undefined | string;
|
||||
}
|
||||
|
||||
export const useThumbnail = (
|
||||
hasThumbnail: boolean,
|
||||
thumbnailID?: string,
|
||||
thumbnailID: string | undefined,
|
||||
isQuickFile?: boolean
|
||||
) => {
|
||||
const requestedThumbnail = useRef(false);
|
||||
const [state, setState] = useState<thumbnailState>({
|
||||
hasThumbnail: false,
|
||||
image: undefined,
|
||||
});
|
||||
const { isHome, isMedia } = useUtils();
|
||||
const listView = useAppSelector((state) => state.filter.listView);
|
||||
|
||||
const imageOnError = useCallback(() => {
|
||||
setState({
|
||||
hasThumbnail: false,
|
||||
image: undefined,
|
||||
});
|
||||
}, []);
|
||||
const getThumbnail = useCallback(async () => {
|
||||
try {
|
||||
if (!thumbnailID || requestedThumbnail.current) return;
|
||||
if (isQuickFile && !isHome) return;
|
||||
if (!isQuickFile && listView && !isMedia) return;
|
||||
requestedThumbnail.current = true;
|
||||
const thumbnailData = await getFileThumbnailAPI(thumbnailID);
|
||||
setState({
|
||||
hasThumbnail: true,
|
||||
image: thumbnailData,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("error getting thumbnail data", e);
|
||||
imageOnError();
|
||||
const { isMedia } = useUtils();
|
||||
const disabled = listView && !isMedia && !isQuickFile;
|
||||
const thumbnailQuery = useQuery(
|
||||
["thumbnail", { thumbnailID }],
|
||||
() => {
|
||||
if (thumbnailID) {
|
||||
return getFileThumbnailAPI(thumbnailID);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: !!thumbnailID && !disabled,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
keepPreviousData: true,
|
||||
}
|
||||
}, [
|
||||
thumbnailID,
|
||||
getFileThumbnailAPI,
|
||||
imageOnError,
|
||||
listView,
|
||||
isHome,
|
||||
isQuickFile,
|
||||
]);
|
||||
);
|
||||
return thumbnailQuery;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasThumbnail || !thumbnailID) return;
|
||||
|
||||
getThumbnail();
|
||||
|
||||
return () => {
|
||||
requestedThumbnail.current = false;
|
||||
};
|
||||
}, [hasThumbnail, getThumbnail, thumbnailID]);
|
||||
|
||||
return { ...state, imageOnError };
|
||||
export const useFullThumbnail = (fileID: string, isVideo: boolean) => {
|
||||
console.log("usefullthumbnail", fileID, isVideo);
|
||||
const thumbnailQuery = useQuery(
|
||||
["full-thumbnail", { fileID }],
|
||||
() => {
|
||||
if (!isVideo) {
|
||||
return getFileFullThumbnailAPI(fileID);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: !isVideo,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
}
|
||||
);
|
||||
return thumbnailQuery;
|
||||
};
|
||||
|
||||
export const useSearchSuggestions = (searchText: string) => {
|
||||
|
||||
+7
-1
@@ -6,7 +6,13 @@ module.exports = {
|
||||
"./public/index.html",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#3c85ee",
|
||||
"white-hover": "#f6f5fd",
|
||||
"gray-primary": "#637381",
|
||||
},
|
||||
},
|
||||
screens: {
|
||||
quickAccessOne: "1000px",
|
||||
quickAccessTwo: "1210px",
|
||||
|
||||
Reference in New Issue
Block a user