From 96ed68460117808be6f970b9529af952f358f36b Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Mon, 9 Sep 2024 22:19:57 +0500 Subject: [PATCH 1/2] feat: add files previewer component --- backend/server.js | 8 +- frontend/src/File Manager/Files/FileItem.jsx | 6 +- .../src/File Manager/Files/FilePreviewer.jsx | 133 +++++++++--------- frontend/src/File Manager/Files/Files.scss | 29 ++++ 4 files changed, 107 insertions(+), 69 deletions(-) diff --git a/backend/server.js b/backend/server.js index eb8e999..6e88f0e 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,6 +3,9 @@ const connectDB = require("./app/config/db.config"); const cors = require("cors"); const fileSystemRoutes = require("./app/routes/fileSystem.routes"); const errorHandler = require("./app/middlewares/errorHandler.middleware"); +const dotenv = require("dotenv"); + +dotenv.config(); const app = express(); @@ -10,7 +13,10 @@ const app = express(); connectDB(); // CORS setup -app.use(cors()); +app.use(cors({ origin: process.env.CLIENT_URI })); + +// Static files serving +app.use(express.static("public/uploads")); // Middlewares to parse URL-encoded body & JSON app.use(express.urlencoded({ extended: true })); diff --git a/frontend/src/File Manager/Files/FileItem.jsx b/frontend/src/File Manager/Files/FileItem.jsx index 6937829..d9157c6 100644 --- a/frontend/src/File Manager/Files/FileItem.jsx +++ b/frontend/src/File Manager/Files/FileItem.jsx @@ -15,6 +15,7 @@ import { useFileNavigation } from "../../contexts/FileNavigationContext"; import { useSelection } from "../../contexts/SelectionContext"; import { useClipBoard } from "../../contexts/ClipboardContext"; import { useLayout } from "../../contexts/LayoutContext"; +import FilePreviewer from "./FilePreviewer"; const FileItem = ({ index, @@ -226,12 +227,11 @@ const FileItem = ({ - {/* */} + /> ); }; diff --git a/frontend/src/File Manager/Files/FilePreviewer.jsx b/frontend/src/File Manager/Files/FilePreviewer.jsx index b5472ac..95abe16 100644 --- a/frontend/src/File Manager/Files/FilePreviewer.jsx +++ b/frontend/src/File Manager/Files/FilePreviewer.jsx @@ -1,73 +1,76 @@ -import { useSelector } from "react-redux"; -import FileExplorerAction from "./FileExplorerAction" import { useState } from "react"; -import Skeleton from "react-loading-skeleton"; +import Modal from "../../components/Modal/Modal"; +import { getFileExtension } from "../../utils/getFileExtension"; -const imageExtensions = ['jpg', 'jpeg', 'png']; -const iFrameExtensions = ['txt', 'pdf']; +const imageExtensions = ["jpg", "jpeg", "png"]; +const videoExtensions = ["mp4", "mov", "avi"]; +const audioExtensions = ["mp3", "wav", "m4a"]; +const iFrameExtensions = ["txt", "pdf"]; -const FilePreviewer = ({ file, showFilePreview, setShowFilePreview, currentPath }) => { - const [isLoading, setIsLoading] = useState(true); - const [hasError, setHasError] = useState(false); - const extension = file.FileName?.split('.')?.pop()?.toLowerCase(); - const practiceId = useSelector((e) => e.show.practiceID); - const patientId = useSelector((e) => e.show.selectedPatientId); - const filePath = `${process.env.REACT_APP_APPLICATION_URL}/Patient Documents/${practiceId}/${patientId}/${currentPath === "" ? currentPath + file.FileName : currentPath + '/' + file.FileName}`; +const FilePreviewer = ({ file, showFilePreview, setShowFilePreview }) => { + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + const extension = getFileExtension(file.name)?.toLowerCase(); + const filePath = `${import.meta.env.VITE_API_FILES_BASE_URL}${file.path}`; - const handleImageLoad = () => { - setIsLoading(false); // Loading is complete - setHasError(false); // No error - }; + const handleImageLoad = () => { + setIsLoading(false); // Loading is complete + setHasError(false); // No error + }; - const handleImageError = () => { - setIsLoading(false); // Loading is complete - setHasError(true); // Error occurred - }; + const handleImageError = () => { + setIsLoading(false); // Loading is complete + setHasError(true); // Error occurred + }; + if (showFilePreview) { return ( - -
- {isLoading && } - { - hasError && -
-
Preview Unavaiablle
-
- } - { - imageExtensions.includes(extension) && - <> - Preview Unavailable - - } - { - iFrameExtensions.includes(extension) && - <> - - - } - {/* Download File for extensions with no preview */} -
-
- ) -} + +
+ {/* {isLoading &&

Loading...

} */} + {hasError && ( +
+
Preview Unavaiablle
+
+ )} + {imageExtensions.includes(extension) && ( + <> + Preview Unavailable + + )} + {videoExtensions.includes(extension) && ( +
+
+ ); + } +}; -export default FilePreviewer \ No newline at end of file +export default FilePreviewer; diff --git a/frontend/src/File Manager/Files/Files.scss b/frontend/src/File Manager/Files/Files.scss index 22ad3ca..6886f80 100644 --- a/frontend/src/File Manager/Files/Files.scss +++ b/frontend/src/File Manager/Files/Files.scss @@ -20,6 +20,35 @@ background-color: rgb(0, 0, 0, 0.04); } + .file-previewer { + padding: .8em; + height: 40dvh; + display: flex; + justify-content: center; + + .photo-popup-image { + object-fit: contain; + width: -webkit-fill-available; + } + + .audio-preview { + align-self: center; + width: 60%; + } + + .photo-popup-iframe { + width: -webkit-fill-available; + } + } + + .file-previewer.pdf-previewer { + height: 85dvh; + } + + .video-preview { + width: -webkit-fill-available; + } + .file-item { position: relative; height: 81px; From 7cd41a64a8d72019ce8bde8049629f7dbae2a36d Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Tue, 10 Sep 2024 10:59:38 +0500 Subject: [PATCH 2/2] feat(file preview): add previewer component for previewing files --- README.md | 25 ++--- frontend/README.md | 25 ++--- frontend/src/App.jsx | 1 + frontend/src/File Manager/Actions/Actions.jsx | 11 ++- .../Preview File/PreviewFile.action.jsx | 98 +++++++++++++++++++ .../Preview File/PreviewFile.action.scss | 84 ++++++++++++++++ .../File Manager/Actions/Preview.action.jsx | 0 frontend/src/File Manager/FileManager.jsx | 2 + frontend/src/File Manager/FileManager.scss | 18 ---- frontend/src/File Manager/Files/FileItem.jsx | 22 ++--- .../src/File Manager/Files/FilePreviewer.jsx | 76 -------------- frontend/src/File Manager/Files/Files.scss | 29 ------ frontend/src/components/Loader/Loader.scss | 4 +- frontend/src/hooks/useFileIcons.jsx | 4 +- frontend/src/utils/getDataSize.js | 2 + 15 files changed, 240 insertions(+), 161 deletions(-) create mode 100644 frontend/src/File Manager/Actions/Preview File/PreviewFile.action.jsx create mode 100644 frontend/src/File Manager/Actions/Preview File/PreviewFile.action.scss delete mode 100644 frontend/src/File Manager/Actions/Preview.action.jsx delete mode 100644 frontend/src/File Manager/Files/FilePreviewer.jsx diff --git a/README.md b/README.md index 98edcca..5eeb8dd 100644 --- a/README.md +++ b/README.md @@ -73,17 +73,20 @@ type File = { ## ⚙️ Props -| Name | Type | Description | -| ------------------ | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `isLoading` | boolean | A boolean state indicating whether the application is currently performing an operation, such as creating, renaming, or deleting a file/folder. Displays a loading state if set `true`. | -| `fileUploadConfig` | { url: string; headers?: { [key: string]: string } } | Configuration object for file uploads. It includes the upload URL (`url`) and an optional `headers` object for setting custom HTTP headers in the upload request. The `headers` object can accept any standard or custom headers required by the server. Example: `{ url: "https://example.com/fileupload", headers: { Authorization: "Bearer" + TOKEN, "X-Custom-Header": "value" } }` | -| `onCreateFolder` | (name: string, parentFolder: [File](#-file-structure)) => void | A callback function triggered when a new folder is created. Use this function to update the files state to include the new folder under the specified parent folder using create folder API call to your server. | -| `onFileUploading` | (file: [File](#-file-structure), parentFolder: [File](#-file-structure)) => { [key: string]: any } | A callback function triggered during the file upload process. You can also return an object with key-value pairs that will be appended to the `FormData` along with the file being uploaded. The object can contain any number of valid properties. | -| `onFileUploaded` | (response: { [key: string]: any }) => void | A callback function triggered after a file is successfully uploaded. Provides JSON `response` holding uploaded file details, use it to extract the uploaded file details and add it to the `files` state e.g. `setFiles((prev) => [...prev, JSON.parse(response)]);` | -| `onRename` | (file: [File](#-file-structure), newName: string) => void | A callback function triggered when a file or folder is renamed. | -| `onDelete` | (file: [File](#-file-structure)) => void | A callback function triggered when a file or folder is deleted. | -| `onPaste` | (file: [File](#-file-structure), destinationFolder: [File](#-file-structure), operationType: "copy" \| "move") => void | A callback function triggered when a file or folder is pasted into a new location. Depending on `operationType`, use this to either copy or move the `sourceItem` to the `destinationFolder`, updating the files state accordingly. | -| `onRefresh` | () => void | A callback function triggered when the file manager is refreshed. Use this to refresh the `files` state to reflect any changes or updates. | +| Name | Type | Description | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `files` | Array<[File](#-file-structure)> | An array of file and folder objects representing the current directory structure. Each object includes `name`, `isDirectory`, and `path` properties. | +| `isLoading` | boolean | A boolean state indicating whether the application is currently performing an operation, such as creating, renaming, or deleting a file/folder. Displays a loading state if set `true`. | +| `fileUploadConfig` | { url: string; headers?: { [key: string]: string } } | Configuration object for file uploads. It includes the upload URL (`url`) and an optional `headers` object for setting custom HTTP headers in the upload request. The `headers` object can accept any standard or custom headers required by the server. Example: `{ url: "https://example.com/fileupload", headers: { Authorization: "Bearer" + TOKEN, "X-Custom-Header": "value" } }` | +| `filePreviewPath` | String | The base URL for file previews e.g. `https://example.com`, file path will be appended automatically to it i.e. `https://example.com/yourFilePath`. | +| `allowedFileExtensions` | String | A comma-separated list of allowed file extensions (e.g., `.txt, .png, .pdf`). | +| `onCreateFolder` | (name: string, parentFolder: [File](#-file-structure)) => void | A callback function triggered when a new folder is created. Use this function to update the files state to include the new folder under the specified parent folder using create folder API call to your server. | +| `onFileUploading` | (file: [File](#-file-structure), parentFolder: [File](#-file-structure)) => { [key: string]: any } | A callback function triggered during the file upload process. You can also return an object with key-value pairs that will be appended to the `FormData` along with the file being uploaded. The object can contain any number of valid properties. | +| `onFileUploaded` | (response: { [key: string]: any }) => void | A callback function triggered after a file is successfully uploaded. Provides JSON `response` holding uploaded file details, use it to extract the uploaded file details and add it to the `files` state e.g. `setFiles((prev) => [...prev, JSON.parse(response)]);` | +| `onRename` | (file: [File](#-file-structure), newName: string) => void | A callback function triggered when a file or folder is renamed. | +| `onDelete` | (file: [File](#-file-structure)) => void | A callback function triggered when a file or folder is deleted. | +| `onPaste` | (file: [File](#-file-structure), destinationFolder: [File](#-file-structure), operationType: "copy" \| "move") => void | A callback function triggered when a file or folder is pasted into a new location. Depending on `operationType`, use this to either copy or move the `sourceItem` to the `destinationFolder`, updating the files state accordingly. | +| `onRefresh` | () => void | A callback function triggered when the file manager is refreshed. Use this to refresh the `files` state to reflect any changes or updates. | ## 🤝 Contributing diff --git a/frontend/README.md b/frontend/README.md index 98edcca..5eeb8dd 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -73,17 +73,20 @@ type File = { ## ⚙️ Props -| Name | Type | Description | -| ------------------ | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `isLoading` | boolean | A boolean state indicating whether the application is currently performing an operation, such as creating, renaming, or deleting a file/folder. Displays a loading state if set `true`. | -| `fileUploadConfig` | { url: string; headers?: { [key: string]: string } } | Configuration object for file uploads. It includes the upload URL (`url`) and an optional `headers` object for setting custom HTTP headers in the upload request. The `headers` object can accept any standard or custom headers required by the server. Example: `{ url: "https://example.com/fileupload", headers: { Authorization: "Bearer" + TOKEN, "X-Custom-Header": "value" } }` | -| `onCreateFolder` | (name: string, parentFolder: [File](#-file-structure)) => void | A callback function triggered when a new folder is created. Use this function to update the files state to include the new folder under the specified parent folder using create folder API call to your server. | -| `onFileUploading` | (file: [File](#-file-structure), parentFolder: [File](#-file-structure)) => { [key: string]: any } | A callback function triggered during the file upload process. You can also return an object with key-value pairs that will be appended to the `FormData` along with the file being uploaded. The object can contain any number of valid properties. | -| `onFileUploaded` | (response: { [key: string]: any }) => void | A callback function triggered after a file is successfully uploaded. Provides JSON `response` holding uploaded file details, use it to extract the uploaded file details and add it to the `files` state e.g. `setFiles((prev) => [...prev, JSON.parse(response)]);` | -| `onRename` | (file: [File](#-file-structure), newName: string) => void | A callback function triggered when a file or folder is renamed. | -| `onDelete` | (file: [File](#-file-structure)) => void | A callback function triggered when a file or folder is deleted. | -| `onPaste` | (file: [File](#-file-structure), destinationFolder: [File](#-file-structure), operationType: "copy" \| "move") => void | A callback function triggered when a file or folder is pasted into a new location. Depending on `operationType`, use this to either copy or move the `sourceItem` to the `destinationFolder`, updating the files state accordingly. | -| `onRefresh` | () => void | A callback function triggered when the file manager is refreshed. Use this to refresh the `files` state to reflect any changes or updates. | +| Name | Type | Description | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `files` | Array<[File](#-file-structure)> | An array of file and folder objects representing the current directory structure. Each object includes `name`, `isDirectory`, and `path` properties. | +| `isLoading` | boolean | A boolean state indicating whether the application is currently performing an operation, such as creating, renaming, or deleting a file/folder. Displays a loading state if set `true`. | +| `fileUploadConfig` | { url: string; headers?: { [key: string]: string } } | Configuration object for file uploads. It includes the upload URL (`url`) and an optional `headers` object for setting custom HTTP headers in the upload request. The `headers` object can accept any standard or custom headers required by the server. Example: `{ url: "https://example.com/fileupload", headers: { Authorization: "Bearer" + TOKEN, "X-Custom-Header": "value" } }` | +| `filePreviewPath` | String | The base URL for file previews e.g. `https://example.com`, file path will be appended automatically to it i.e. `https://example.com/yourFilePath`. | +| `allowedFileExtensions` | String | A comma-separated list of allowed file extensions (e.g., `.txt, .png, .pdf`). | +| `onCreateFolder` | (name: string, parentFolder: [File](#-file-structure)) => void | A callback function triggered when a new folder is created. Use this function to update the files state to include the new folder under the specified parent folder using create folder API call to your server. | +| `onFileUploading` | (file: [File](#-file-structure), parentFolder: [File](#-file-structure)) => { [key: string]: any } | A callback function triggered during the file upload process. You can also return an object with key-value pairs that will be appended to the `FormData` along with the file being uploaded. The object can contain any number of valid properties. | +| `onFileUploaded` | (response: { [key: string]: any }) => void | A callback function triggered after a file is successfully uploaded. Provides JSON `response` holding uploaded file details, use it to extract the uploaded file details and add it to the `files` state e.g. `setFiles((prev) => [...prev, JSON.parse(response)]);` | +| `onRename` | (file: [File](#-file-structure), newName: string) => void | A callback function triggered when a file or folder is renamed. | +| `onDelete` | (file: [File](#-file-structure)) => void | A callback function triggered when a file or folder is deleted. | +| `onPaste` | (file: [File](#-file-structure), destinationFolder: [File](#-file-structure), operationType: "copy" \| "move") => void | A callback function triggered when a file or folder is pasted into a new location. Depending on `operationType`, use this to either copy or move the `sourceItem` to the `destinationFolder`, updating the files state accordingly. | +| `onRefresh` | () => void | A callback function triggered when the file manager is refreshed. Use this to refresh the `files` state to reflect any changes or updates. | ## 🤝 Contributing diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5ec373c..1ac2447 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -117,6 +117,7 @@ function App() { onPaste={handlePaste} onLayoutChange={handleLayoutChange} onRefresh={handleRefresh} + filePreviewPath={import.meta.env.VITE_API_FILES_BASE_URL} allowedFileExtensions=".txt, .png, .jpg, .jpeg, .pdf, .doc, .docx" /> diff --git a/frontend/src/File Manager/Actions/Actions.jsx b/frontend/src/File Manager/Actions/Actions.jsx index 12820a7..2c21e62 100644 --- a/frontend/src/File Manager/Actions/Actions.jsx +++ b/frontend/src/File Manager/Actions/Actions.jsx @@ -2,16 +2,20 @@ import { useEffect, useState } from "react"; import Modal from "../../components/Modal/Modal"; import DeleteAction from "./Delete/Delete.action"; import UploadFileAction from "./Upload File/UploadFile.action"; +import PreviewFileAction from "./Preview File/PreviewFile.action"; +import { useSelection } from "../../contexts/SelectionContext"; const Actions = ({ fileUploadConfig, onFileUploading, onFileUploaded, onDelete, + filePreviewPath, allowedFileExtensions, triggerAction, }) => { const [activeAction, setActiveAction] = useState(null); + const { selectedFile } = useSelection(); const actionTypes = { uploadFile: { @@ -31,14 +35,19 @@ const Actions = ({ component: , width: "25%", }, - preview: { + previewFile: { title: "Preview", + component: , + width: "50%", }, }; useEffect(() => { if (triggerAction.isActive) { const actionType = triggerAction.actionType; + if (actionType === "previewFile") { + actionTypes[actionType].title = selectedFile?.name ?? "Preview"; + } setActiveAction(actionTypes[actionType]); } else { setActiveAction(null); diff --git a/frontend/src/File Manager/Actions/Preview File/PreviewFile.action.jsx b/frontend/src/File Manager/Actions/Preview File/PreviewFile.action.jsx new file mode 100644 index 0000000..26792b4 --- /dev/null +++ b/frontend/src/File Manager/Actions/Preview File/PreviewFile.action.jsx @@ -0,0 +1,98 @@ +import { useState } from "react"; +import { getFileExtension } from "../../../utils/getFileExtension"; +import Loader from "../../../components/Loader/Loader"; +import { useSelection } from "../../../contexts/SelectionContext"; +import "./PreviewFile.action.scss"; +import Button from "../../../components/Button/Button"; +import { getDataSize } from "../../../utils/getDataSize"; +import { MdOutlineFileDownload } from "react-icons/md"; +import { useFileIcons } from "../../../hooks/useFileIcons"; +import { FaRegFileAlt } from "react-icons/fa"; + +const imageExtensions = ["jpg", "jpeg", "png"]; +const videoExtensions = ["mp4", "mov", "avi"]; +const audioExtensions = ["mp3", "wav", "m4a"]; +const iFrameExtensions = ["txt", "pdf"]; + +const PreviewFileAction = ({ filePreviewPath }) => { + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + const { selectedFile } = useSelection(); + const fileIcons = useFileIcons(73); + const extension = getFileExtension(selectedFile.name)?.toLowerCase(); + const filePath = `${filePreviewPath}${selectedFile.path}`; + + const handleImageLoad = () => { + setIsLoading(false); // Loading is complete + setHasError(false); // No error + }; + + const handleImageError = () => { + setIsLoading(false); // Loading is complete + setHasError(true); // Error occurred + }; + + const handleDownload = () => { + window.location.href = filePath; + }; + + return ( +
+ {hasError || + (!imageExtensions.includes(extension) && + !videoExtensions.includes(extension) && + !audioExtensions.includes(extension) && + !iFrameExtensions.includes(extension) && ( +
+ + {fileIcons[extension] ?? } + + Sorry! Preview is not available for this file. +
+ {selectedFile.name} + {selectedFile.size && -} + {getDataSize(selectedFile.size)} +
+ +
+ ))} + {imageExtensions.includes(extension) && ( + <> + + Preview Unavailable + + )} + {videoExtensions.includes(extension) && ( +
+ ); +}; + +export default PreviewFileAction; diff --git a/frontend/src/File Manager/Actions/Preview File/PreviewFile.action.scss b/frontend/src/File Manager/Actions/Preview File/PreviewFile.action.scss new file mode 100644 index 0000000..affc58d --- /dev/null +++ b/frontend/src/File Manager/Actions/Preview File/PreviewFile.action.scss @@ -0,0 +1,84 @@ +.file-previewer { + padding: .8em; + height: 40dvh; + display: flex; + justify-content: center; + + .photo-popup-image { + object-fit: contain; + width: -webkit-fill-available; + opacity: 1; + transition: opacity 0.5s ease-in-out; + } + + .img-loading { + opacity: 0; + height: 0%; + width: 0%; + } + + .img-loading { + height: 0; + } + + .audio-preview { + align-self: center; + width: 60%; + } + + .photo-popup-iframe { + width: -webkit-fill-available; + } + + .preview-error { + max-width: 40%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 10px; + + .error-icon { + color: rgb(73, 73, 73); + } + + .error-msg { + font-weight: 500; + font-size: 1.1em; + margin-bottom: 4px; + } + + .file-info { + display: flex; + gap: 6px; + align-items: center; + margin: 1px 0 5px 0; + + .file-name { + padding: 4px 15px; + background-color: rgb(233, 233, 233); + border: 1px solid rgb(163, 173, 173); + border-radius: 3px; + } + + .file-size { + font-size: .8em; + } + } + + .download-btn { + display: flex; + gap: 3px; + align-items: center; + } + + } +} + +.file-previewer.pdf-previewer { + height: 85dvh; +} + +.video-preview { + width: -webkit-fill-available; +} \ No newline at end of file diff --git a/frontend/src/File Manager/Actions/Preview.action.jsx b/frontend/src/File Manager/Actions/Preview.action.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/File Manager/FileManager.jsx b/frontend/src/File Manager/FileManager.jsx index 363ced2..39d09da 100644 --- a/frontend/src/File Manager/FileManager.jsx +++ b/frontend/src/File Manager/FileManager.jsx @@ -26,6 +26,7 @@ const FileManager = ({ onPaste, onLayoutChange, onRefresh, + filePreviewPath, allowedFileExtensions, }) => { const triggerAction = useTriggerAction(); @@ -78,6 +79,7 @@ const FileManager = ({ onFileUploading={onFileUploading} onFileUploaded={onFileUploaded} onDelete={onDelete} + filePreviewPath={filePreviewPath} allowedFileExtensions={allowedFileExtensions} triggerAction={triggerAction} /> diff --git a/frontend/src/File Manager/FileManager.scss b/frontend/src/File Manager/FileManager.scss index ca2ceb5..556d50e 100644 --- a/frontend/src/File Manager/FileManager.scss +++ b/frontend/src/File Manager/FileManager.scss @@ -161,24 +161,6 @@ svg { } } -.file-previewer { - height: 75vh; - - .photo-popup-image { - opacity: 1; - object-fit: contain; - height: 100%; - width: 100%; - transition: opacity 0.5s ease-in-out; - } - - .img-loading { - opacity: 0; - height: 0%; - width: 0%; - } -} - .fm-rename-folder-container { padding: 8px 0; diff --git a/frontend/src/File Manager/Files/FileItem.jsx b/frontend/src/File Manager/Files/FileItem.jsx index d9157c6..c38f1d3 100644 --- a/frontend/src/File Manager/Files/FileItem.jsx +++ b/frontend/src/File Manager/Files/FileItem.jsx @@ -15,7 +15,6 @@ import { useFileNavigation } from "../../contexts/FileNavigationContext"; import { useSelection } from "../../contexts/SelectionContext"; import { useClipBoard } from "../../contexts/ClipboardContext"; import { useLayout } from "../../contexts/LayoutContext"; -import FilePreviewer from "./FilePreviewer"; const FileItem = ({ index, @@ -38,7 +37,6 @@ const FileItem = ({ const [visible, setVisible] = useState(false); const [fileSelected, setFileSelected] = useState(false); const [lastClickTime, setLastClickTime] = useState(0); - const [showFilePreview, setShowFilePreview] = useState(false); const isFileMoving = clipBoard?.isMoving && clipBoard.files.find((f) => f.name === file.name && f.path === file.path); @@ -87,9 +85,10 @@ const FileItem = ({ if (file.isDirectory) { setCurrentPath((prev) => prev + "/" + file.name); setSelectedFileIndex(null); + setSelectedFile(null); } else { // Display File Image/PDF/Txt etc - setShowFilePreview(true); + triggerAction.show("previewFile"); } }; @@ -101,15 +100,22 @@ const FileItem = ({ setSelectedFileIndex(index); const currentTime = new Date().getTime(); if (currentTime - lastClickTime < 300) { - setSelectedFile(null); handleFileAccess(); + return; } setLastClickTime(currentTime); }; + const handleFileOpen = (e) => { + e.stopPropagation(); + handleFileAccess(); + }; + const handleOnKeyDown = (e) => { e.stopPropagation(); if (e.key === "Enter") { + setSelectedFile(file); + setSelectedFileIndex(index); handleFileAccess(); } }; @@ -125,7 +131,7 @@ const FileItem = ({ const menuItems = (
    -
  • +
  • {file.isDirectory ? : } Open
  • @@ -226,12 +232,6 @@ const FileItem = ({ )}
- - ); }; diff --git a/frontend/src/File Manager/Files/FilePreviewer.jsx b/frontend/src/File Manager/Files/FilePreviewer.jsx deleted file mode 100644 index 95abe16..0000000 --- a/frontend/src/File Manager/Files/FilePreviewer.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import { useState } from "react"; -import Modal from "../../components/Modal/Modal"; -import { getFileExtension } from "../../utils/getFileExtension"; - -const imageExtensions = ["jpg", "jpeg", "png"]; -const videoExtensions = ["mp4", "mov", "avi"]; -const audioExtensions = ["mp3", "wav", "m4a"]; -const iFrameExtensions = ["txt", "pdf"]; - -const FilePreviewer = ({ file, showFilePreview, setShowFilePreview }) => { - const [isLoading, setIsLoading] = useState(true); - const [hasError, setHasError] = useState(false); - const extension = getFileExtension(file.name)?.toLowerCase(); - const filePath = `${import.meta.env.VITE_API_FILES_BASE_URL}${file.path}`; - - const handleImageLoad = () => { - setIsLoading(false); // Loading is complete - setHasError(false); // No error - }; - - const handleImageError = () => { - setIsLoading(false); // Loading is complete - setHasError(true); // Error occurred - }; - - if (showFilePreview) { - return ( - -
- {/* {isLoading &&

Loading...

} */} - {hasError && ( -
-
Preview Unavaiablle
-
- )} - {imageExtensions.includes(extension) && ( - <> - Preview Unavailable - - )} - {videoExtensions.includes(extension) && ( -
-
- ); - } -}; - -export default FilePreviewer; diff --git a/frontend/src/File Manager/Files/Files.scss b/frontend/src/File Manager/Files/Files.scss index 6886f80..22ad3ca 100644 --- a/frontend/src/File Manager/Files/Files.scss +++ b/frontend/src/File Manager/Files/Files.scss @@ -20,35 +20,6 @@ background-color: rgb(0, 0, 0, 0.04); } - .file-previewer { - padding: .8em; - height: 40dvh; - display: flex; - justify-content: center; - - .photo-popup-image { - object-fit: contain; - width: -webkit-fill-available; - } - - .audio-preview { - align-self: center; - width: 60%; - } - - .photo-popup-iframe { - width: -webkit-fill-available; - } - } - - .file-previewer.pdf-previewer { - height: 85dvh; - } - - .video-preview { - width: -webkit-fill-available; - } - .file-item { position: relative; height: 81px; diff --git a/frontend/src/components/Loader/Loader.scss b/frontend/src/components/Loader/Loader.scss index bbf24ca..38b4fed 100644 --- a/frontend/src/components/Loader/Loader.scss +++ b/frontend/src/components/Loader/Loader.scss @@ -4,7 +4,7 @@ display: flex; justify-content: center; align-items: center; - height: 100%; + height: -webkit-fill-available; width: -webkit-fill-available; background-color: rgba(255, 255, 255, 0.542); -} +} \ No newline at end of file diff --git a/frontend/src/hooks/useFileIcons.jsx b/frontend/src/hooks/useFileIcons.jsx index 14561eb..6cba295 100644 --- a/frontend/src/hooks/useFileIcons.jsx +++ b/frontend/src/hooks/useFileIcons.jsx @@ -1,4 +1,3 @@ -import { FaRegFileArchive } from "react-icons/fa"; import { FaRegFileAudio, FaRegFileImage, @@ -10,6 +9,7 @@ import { FaRegFileExcel, FaRegFileCode, FaLaptopFile, + FaRegFileZipper, } from "react-icons/fa6"; export const useFileIcons = (size) => { @@ -25,7 +25,7 @@ export const useFileIcons = (size) => { webm: , mp3: , m4a: , - zip: , + zip: , ppt: , pptx: , xls: , diff --git a/frontend/src/utils/getDataSize.js b/frontend/src/utils/getDataSize.js index bcd220a..9e14090 100644 --- a/frontend/src/utils/getDataSize.js +++ b/frontend/src/utils/getDataSize.js @@ -1,4 +1,6 @@ export const getDataSize = (size) => { + if (!size) return ""; + const KiloBytes = size / 1024; const MegaBytes = KiloBytes / 1024; const GigaBytes = MegaBytes / 1024;