From 998272719dbbb08211eebbc8e5dcd7db02423f92 Mon Sep 17 00:00:00 2001 From: Kevin Nielsen Date: Thu, 25 Jan 2024 15:35:44 +0100 Subject: [PATCH] feat: add UI option for viewing file metadata (#108) * refactor: move SetAtom type to separate file * chore: fix toasts * feat: add dialog trigger for metadata modal * chore: add missing dependency for useEffect * chore(ui): add filemetadata type * feat: add file-data-modal --- ui/src/actions/getFiles.ts | 10 ++-- ui/src/components/content-card.tsx | 33 ++++++----- ui/src/components/file-data-modal.tsx | 81 +++++++++++++++++++++++++++ ui/src/components/files.tsx | 2 +- ui/src/types/fileMetadata.ts | 7 +++ ui/src/types/setAtom.ts | 2 + 6 files changed, 114 insertions(+), 21 deletions(-) create mode 100644 ui/src/components/file-data-modal.tsx create mode 100644 ui/src/types/fileMetadata.ts create mode 100644 ui/src/types/setAtom.ts diff --git a/ui/src/actions/getFiles.ts b/ui/src/actions/getFiles.ts index 4605e5c..d3d68e3 100644 --- a/ui/src/actions/getFiles.ts +++ b/ui/src/actions/getFiles.ts @@ -2,9 +2,7 @@ import axios from "axios"; import { SetStateAction } from "jotai"; import toast from "react-hot-toast"; import File from "../types/file"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type SetAtom = (...args: Args) => Result; +import { SetAtom } from "@/types/setAtom"; export const getFiles = ( type: "images" | "documents", @@ -13,12 +11,14 @@ export const getFiles = ( toast.loading("Loading files..."); axios .get(`/api/cdn/${type === "images" ? "image" : "doc"}/all`) - .then((res) => res.data != null && setFiles(res.data)) + .then((res) => { + res.data != null && setFiles(res.data) + toast.dismiss(); + }) .catch((err: Error) => { toast.dismiss(); toast.error(err.message); console.log(err); }) - .finally(() => toast.dismiss()); } diff --git a/ui/src/components/content-card.tsx b/ui/src/components/content-card.tsx index ab7fbbd..457a016 100644 --- a/ui/src/components/content-card.tsx +++ b/ui/src/components/content-card.tsx @@ -7,6 +7,8 @@ import { filesAtom, sizeAtom } from "../store"; import { getSize } from "../actions/getSize"; import RenameModal from "./rename-modal"; import { Button } from "./ui/button"; +import { Dialog, DialogTrigger } from "./ui/dialog"; +import FileDataModal from "./file-data-modal"; type TContentCardProps = { file_name?: string; @@ -56,17 +58,22 @@ const ContentCard: React.FC = ({ return (
- {type === "images" ? ( - {file_name} - ) : ( - - )} + + + {type === "images" ? ( + {file_name} + ) : ( + + )} + + +

{file_name}

{/* Non-destructive buttons */} @@ -81,14 +88,12 @@ const ContentCard: React.FC = ({ toast.success("clipboard saved"); }} aria-label="Copy Link" - aria-labelledby="Copy Link" > @@ -101,10 +106,8 @@ const ContentCard: React.FC = ({ diff --git a/ui/src/components/file-data-modal.tsx b/ui/src/components/file-data-modal.tsx new file mode 100644 index 0000000..0994b48 --- /dev/null +++ b/ui/src/components/file-data-modal.tsx @@ -0,0 +1,81 @@ +import { useEffect, useState } from "react"; +import { DialogContent, DialogHeader, DialogTitle } from "./ui/dialog"; +import axios from "axios"; +import toast from "react-hot-toast"; +import { FileMetadata } from "@/types/fileMetadata"; +import Seperator from "./seperator"; + +type TFileDataModalProps = { + filename?: string; + type?: "images" | "documents"; +}; + +const FileDataModal: React.FC = ({ filename, type }) => { + const [data, setData] = useState(); + useEffect(() => { + if (filename && type) { + axios + .get( + `/api/cdn/${type === "documents" ? "doc" : "image"}/${filename}` + ) + .then((res) => { + toast.dismiss(); + if (res.status === 200) { + setData(res.data); + } + }) + .catch((err) => { + toast.dismiss(); + toast.error(err.message); + }); + } + axios; + }, [filename, type]); + if (!data) + return ( + + + {filename} + + Error fetching file data. + + ); + return ( + + + {filename} + + + +
+ Filename +

{data.filename}

+
+
+ File Size +

+ {data.file_size < 1000 && `${data.file_size} b`} + {999999 > data.file_size && data.file_size >= 1000 && `${Math.round(data.file_size / 100) / 10} KB`} + {1000000000 > data.file_size && + data.file_size >= 1000000 && + `${Math.round(data.file_size / 100000) / 10} MB`} + {1000000000000 > data.file_size && + data.file_size >= 1000000000 && + `${Math.round(data.file_size / 100000000) / 10} GB`} + {data.file_size >= 1000000000000 && + `${Math.round(data.file_size / 100000000000) / 10} TB`} +

+
+ {type === "images" && ( +
+ Dimensions +

Height: {data.height}px

+

Width: {data.width}px

+
+ + )} +
+ ); +}; + +export default FileDataModal; diff --git a/ui/src/components/files.tsx b/ui/src/components/files.tsx index 7b5dfd9..6bad48f 100644 --- a/ui/src/components/files.tsx +++ b/ui/src/components/files.tsx @@ -14,7 +14,7 @@ const Files: React.FC = ({ type }) => { useEffect(() => { getFiles(type, setFiles) - }, [type]); + }, [type, setFiles]); return (
diff --git a/ui/src/types/fileMetadata.ts b/ui/src/types/fileMetadata.ts new file mode 100644 index 0000000..fb19d15 --- /dev/null +++ b/ui/src/types/fileMetadata.ts @@ -0,0 +1,7 @@ +export type FileMetadata = { + download_url: string; + file_size: number; + filename: string; + height?: number; + width?: number; +}; diff --git a/ui/src/types/setAtom.ts b/ui/src/types/setAtom.ts new file mode 100644 index 0000000..c354545 --- /dev/null +++ b/ui/src/types/setAtom.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type SetAtom = (...args: Args) => Result; \ No newline at end of file