From c807d208d8f0518f6390f9f0f3d0eb00c12d213b Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Tue, 14 Mar 2023 12:09:21 +0100 Subject: [PATCH] feat: add preview modal --- frontend/src/components/share/FileList.tsx | 96 +++++------ frontend/src/components/share/FilePreview.tsx | 154 ++++++++++++++++++ .../share/modals/showFilePreviewModal.tsx | 21 +++ .../share/[shareId]/preview/[fileId].tsx | 94 ----------- frontend/src/services/share.service.ts | 3 +- 5 files changed, 226 insertions(+), 142 deletions(-) create mode 100644 frontend/src/components/share/FilePreview.tsx create mode 100644 frontend/src/components/share/modals/showFilePreviewModal.tsx delete mode 100644 frontend/src/pages/share/[shareId]/preview/[fileId].tsx diff --git a/frontend/src/components/share/FileList.tsx b/frontend/src/components/share/FileList.tsx index b822f6aa..9918c630 100644 --- a/frontend/src/components/share/FileList.tsx +++ b/frontend/src/components/share/FileList.tsx @@ -1,5 +1,6 @@ import { ActionIcon, + Box, Group, Skeleton, Stack, @@ -8,9 +9,6 @@ import { } from "@mantine/core"; import { useClipboard } from "@mantine/hooks"; import { useModals } from "@mantine/modals"; -import mime from "mime-types"; - -import Link from "next/link"; import { TbDownload, TbEye, TbLink } from "react-icons/tb"; import useConfig from "../../hooks/config.hook"; import shareService from "../../services/share.service"; @@ -18,6 +16,7 @@ import { FileMetaData } from "../../types/File.type"; import { Share } from "../../types/share.type"; import { byteToHumanSizeString } from "../../utils/fileSize.util"; import toast from "../../utils/toast.util"; +import showFilePreviewModal from "./modals/showFilePreviewModal"; const FileList = ({ files, @@ -53,54 +52,57 @@ const FileList = ({ }; return ( - - - - - - - - - - {isLoading - ? skeletonRows - : files!.map((file) => ( - - - - + + ))} + +
NameSize
{file.name}{byteToHumanSizeString(parseInt(file.size))} - - {shareService.doesFileSupportPreview(file.name) && ( + + + + + + + + + + + {isLoading + ? skeletonRows + : files!.map((file) => ( + + + + - - ))} - -
NameSize
{file.name}{byteToHumanSizeString(parseInt(file.size))} + + {shareService.doesFileSupportPreview(file.name) && ( + + showFilePreviewModal(share.id, file, modals) + } + size={25} + > + + + )} + {!share.hasPassword && ( + copyFileLink(file)} + > + + + )} { + await shareService.downloadFile(share.id, file.id); + }} > - + - )} - {!share.hasPassword && ( - copyFileLink(file)}> - - - )} - { - await shareService.downloadFile(share.id, file.id); - }} - > - - - -
+
+
+ ); }; diff --git a/frontend/src/components/share/FilePreview.tsx b/frontend/src/components/share/FilePreview.tsx new file mode 100644 index 00000000..789b8f2c --- /dev/null +++ b/frontend/src/components/share/FilePreview.tsx @@ -0,0 +1,154 @@ +import { Button, Center, Stack, Text, Title } from "@mantine/core"; +import { modals } from "@mantine/modals"; +import Link from "next/link"; +import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; + +const FilePreviewContext = React.createContext<{ + shareId: string; + fileId: string; + mimeType: string; + setIsNotSupported: Dispatch>; +}>({ + shareId: "", + fileId: "", + mimeType: "", + setIsNotSupported: () => {}, +}); + +const FilePreview = ({ + shareId, + fileId, + mimeType, +}: { + shareId: string; + fileId: string; + mimeType: string; +}) => { + const [isNotSupported, setIsNotSupported] = useState(false); + if (isNotSupported) return ; + + return ( + + + + + + + ); +}; + +const FileDecider = () => { + const { mimeType, setIsNotSupported } = React.useContext(FilePreviewContext); + + if (mimeType == "application/pdf") { + return ; + } else if (mimeType.startsWith("video/")) { + return ; + } else if (mimeType.startsWith("image/")) { + return ; + } else if (mimeType.startsWith("audio/")) { + return ; + } else if (mimeType == "text/plain") { + return ; + } else { + setIsNotSupported(true); + return null; + } +}; + +const AudioPreview = () => { + const { shareId, fileId, setIsNotSupported } = + React.useContext(FilePreviewContext); + return ( +
+ + + +
+ ); +}; + +const VideoPreview = () => { + const { shareId, fileId, setIsNotSupported } = + React.useContext(FilePreviewContext); + return ( + + ); +}; + +const ImagePreview = () => { + const { shareId, fileId, setIsNotSupported } = + React.useContext(FilePreviewContext); + return ( + // eslint-disable-next-line @next/next/no-img-element + {`${fileId}_preview`} setIsNotSupported(true)} + /> + ); +}; + +const TextPreview = () => { + const { shareId, fileId } = React.useContext(FilePreviewContext); + const [text, setText] = useState(null); + + useEffect(() => { + fetch(`/api/shares/${shareId}/files/${fileId}?download=false`) + .then((res) => res.text()) + .then((text) => setText(text)); + }, [shareId, fileId]); + + return ( +
+ + {text} + +
+ ); +}; + +const PdfPreview = () => { + const { shareId, fileId } = React.useContext(FilePreviewContext); + if (typeof window !== "undefined") { + window.location.href = `/api/shares/${shareId}/files/${fileId}?download=false`; + } + return null; +}; + +const UnSupportedFile = () => { + return ( +
+ + Preview not supported + + A preview for thise file type is unsupported. Please download the file + to view it. + + +
+ ); +}; + +export default FilePreview; diff --git a/frontend/src/components/share/modals/showFilePreviewModal.tsx b/frontend/src/components/share/modals/showFilePreviewModal.tsx new file mode 100644 index 00000000..ef5a1c90 --- /dev/null +++ b/frontend/src/components/share/modals/showFilePreviewModal.tsx @@ -0,0 +1,21 @@ +import { ModalsContextProps } from "@mantine/modals/lib/context"; +import mime from "mime-types"; +import { FileMetaData } from "../../../types/File.type"; +import FilePreview from "../FilePreview"; + +const showFilePreviewModal = ( + shareId: string, + file: FileMetaData, + modals: ModalsContextProps +) => { + const mimeType = (mime.contentType(file.name) || "").split(";")[0]; + return modals.openModal({ + size: "xl", + title: file.name, + children: ( + + ), + }); +}; + +export default showFilePreviewModal; diff --git a/frontend/src/pages/share/[shareId]/preview/[fileId].tsx b/frontend/src/pages/share/[shareId]/preview/[fileId].tsx deleted file mode 100644 index 6d6c00b7..00000000 --- a/frontend/src/pages/share/[shareId]/preview/[fileId].tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { Center, Stack, Text, Title } from "@mantine/core"; -import { GetServerSidePropsContext } from "next"; -import { useState } from "react"; - -export function getServerSideProps(context: GetServerSidePropsContext) { - const { shareId, fileId } = context.params!; - - const mimeType = context.query.type as string; - - return { - props: { shareId, fileId, mimeType }, - }; -} - -const UnSupportedFile = () => { - return ( -
- - Preview not supported - - A preview for thise file type is unsupported. Please download the file - to view it. - - -
- ); -}; - -const FilePreview = ({ - shareId, - fileId, - mimeType, -}: { - shareId: string; - fileId: string; - mimeType: string; -}) => { - const [isNotSupported, setIsNotSupported] = useState(false); - - if (isNotSupported) return ; - - if (mimeType == "application/pdf") { - if (typeof window !== "undefined") { - window.location.href = `/api/shares/${shareId}/files/${fileId}?download=false`; - } - return null; - } else if (mimeType.startsWith("video/")) { - return ( - - ); - } else if (mimeType.startsWith("image/")) { - return ( - // eslint-disable-next-line @next/next/no-img-element - { - setIsNotSupported(true); - }} - src={`/api/shares/${shareId}/files/${fileId}?download=false`} - alt={`${fileId}_preview`} - width="100%" - /> - ); - } else if (mimeType.startsWith("audio/")) { - return ( -
- - - -
- ); - } else { - return ; - } -}; - -export default FilePreview; diff --git a/frontend/src/services/share.service.ts b/frontend/src/services/share.service.ts index a7454312..0e045a29 100644 --- a/frontend/src/services/share.service.ts +++ b/frontend/src/services/share.service.ts @@ -53,7 +53,7 @@ const isShareIdAvailable = async (id: string): Promise => { }; const doesFileSupportPreview = (fileName: string) => { - const mimeType = mime.contentType(fileName); + const mimeType = (mime.contentType(fileName) || "").split(";")[0]; if (!mimeType) return false; @@ -61,6 +61,7 @@ const doesFileSupportPreview = (fileName: string) => { mimeType.startsWith("video/"), mimeType.startsWith("image/"), mimeType.startsWith("audio/"), + mimeType == "text/plain", mimeType == "application/pdf", ];