diff --git a/.env.sample b/.env.sample index 74e91920..10ed7eef 100644 --- a/.env.sample +++ b/.env.sample @@ -13,6 +13,7 @@ STORAGE_FOLDER= AUTOSCROLL_TIMEOUT= NEXT_PUBLIC_DISABLE_REGISTRATION= RE_ARCHIVE_LIMIT= +NEXT_PUBLIC_MAX_UPLOAD_SIZE= # AWS S3 Settings SPACES_KEY= diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} diff --git a/components/CollectionCard.tsx b/components/CollectionCard.tsx index 0d904a53..97e0b63c 100644 --- a/components/CollectionCard.tsx +++ b/components/CollectionCard.tsx @@ -65,7 +65,7 @@ export default function CollectionCard({ collection, className }: Props) { return (
-
+
) : undefined} {collection.members @@ -131,13 +131,14 @@ export default function CollectionCard({ collection, className }: Props) { ); }) .slice(0, 3)} {collection.members.length - 3 > 0 ? ( -
+
+{collection.members.length - 3}
diff --git a/components/InputSelect/CollectionSelection.tsx b/components/InputSelect/CollectionSelection.tsx index b514f79f..71dc075f 100644 --- a/components/InputSelect/CollectionSelection.tsx +++ b/components/InputSelect/CollectionSelection.tsx @@ -13,14 +13,9 @@ type Props = { value?: number; } | undefined; - id?: string; }; -export default function CollectionSelection({ - onChange, - defaultValue, - id, -}: Props) { +export default function CollectionSelection({ onChange, defaultValue }: Props) { const { collections } = useCollectionStore(); const router = useRouter(); @@ -49,7 +44,6 @@ export default function CollectionSelection({ return ( { const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0]; @@ -81,23 +87,6 @@ export default function LinkCard({ link, count, className }: Props) { toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`); }; - const updateArchive = async () => { - const load = toast.loading("Sending request..."); - - const response = await fetch(`/api/v1/links/${link.id}/archive`, { - method: "PUT", - }); - - const data = await response.json(); - - toast.dismiss(load); - - if (response.ok) { - toast.success(`Link is being archived...`); - getLink(link.id as number); - } else toast.error(data.response); - }; - const deleteLink = async () => { const load = toast.loading("Deleting..."); @@ -122,6 +111,8 @@ export default function LinkCard({ link, count, className }: Props) { const [editLinkModal, setEditLinkModal] = useState(false); const [deleteLinkModal, setDeleteLinkModal] = useState(false); + const [preservedFormatsModal, setPreservedFormatsModal] = useState(false); + const [expandedLink, setExpandedLink] = useState(false); return (
{ (document?.activeElement as HTMLElement)?.blur(); - updateArchive(); + setPreservedFormatsModal(true); + // updateArchive(); }} > - Refresh Link + Preserved Formats
) : undefined} @@ -208,52 +200,82 @@ export default function LinkCard({ link, count, className }: Props) {
) : undefined} -
router.push("/links/" + link.id)} - className="flex items-start cursor-pointer gap-5 sm:gap-10 h-full w-full p-4" + router.push("/links/" + link.id) + // // setExpandedLink(true) + // } + className="flex flex-col justify-between cursor-pointer h-full w-full gap-1 p-3" > - {url && account.displayLinkIcons && ( + {link.url && url ? ( { const target = e.target as HTMLElement; target.style.display = "none"; }} /> + ) : link.type === "pdf" ? ( + + ) : link.type === "image" ? ( + + ) : undefined} + +
+

{count + 1}

+

+ {unescapeString(link.name || link.description) || shortendURL} +

+
+ + {link.url ? ( +
{ + e.preventDefault(); + window.open(link.url || "", "_blank"); + }} + className="flex items-center gap-1 max-w-full w-fit text-neutral hover:opacity-60 duration-100" + > + +

{shortendURL}

+
+ ) : ( +
{link.type}
)} -
-
-
-

{count + 1}

-

- {unescapeString(link.name || link.description)} -

-
- { - e.stopPropagation(); - }} - className="flex items-center gap-1 max-w-full w-fit my-1 hover:opacity-70 duration-100" - > - -

{collection?.name}

- +
{ + e.preventDefault(); + router.push(`/collections/${link.collection.id}`); + }} + className="flex items-center gap-1 max-w-full w-fit hover:opacity-70 duration-100" + > + +

{collection?.name}

+
- {/* {link.tags[0] ? ( -
+
+ +

{formattedDate}

+
+ {/* {link.tags[0] ? ( +
{link.tags.map((e, i) => ( { e.stopPropagation(); }} - className="btn btn-xs btn-outline truncate max-w-[19rem]" + className="btn btn-xs btn-ghost truncate max-w-[19rem]" > - {e.name} + #{e.name} ))}
- ) : undefined} */} - - { - e.stopPropagation(); - }} - className="flex items-center gap-1 max-w-full w-fit text-neutral hover:opacity-70 duration-100" - > - -

{shortendURL}

- -
- -

{formattedDate}

-
-
-
-
+ ) : ( +

No Tags

+ )} */} + {editLinkModal ? ( setEditLinkModal(false)} @@ -302,6 +308,15 @@ export default function LinkCard({ link, count, className }: Props) { activeLink={link} /> ) : undefined} + {preservedFormatsModal ? ( + setPreservedFormatsModal(false)} + activeLink={link} + /> + ) : undefined} + {/* {expandedLink ? ( + setExpandedLink(false)} link={link} /> + ) : undefined} */}
); } diff --git a/components/Modal.tsx b/components/Modal.tsx index cf9ccdca..c7f4da51 100644 --- a/components/Modal.tsx +++ b/components/Modal.tsx @@ -11,12 +11,14 @@ type Props = { export default function Modal({ toggleModal, className, children }: Props) { return ( -
+
-
+
} className="absolute top-3 right-3 btn btn-sm outline-none btn-circle btn-ghost" diff --git a/components/Modal/Link/PreservedFormats.tsx b/components/Modal/Link/PreservedFormats.tsx index d1bc5bf1..4017c322 100644 --- a/components/Modal/Link/PreservedFormats.tsx +++ b/components/Modal/Link/PreservedFormats.tsx @@ -92,7 +92,7 @@ export default function PreservedFormats() { {link?.screenshotPath && link?.screenshotPath !== "pending" ? (
-
+
@@ -131,7 +131,7 @@ export default function PreservedFormats() { {link?.pdfPath && link.pdfPath !== "pending" ? (
-
+
@@ -166,7 +166,7 @@ export default function PreservedFormats() {
{link?.collection.ownerId === session.data?.user.id ? (
updateArchive()} > -

Update Preserved Formats

-

(Refresh Link)

+
+

Update Preserved Formats

+

(Refresh Link)

+
) : undefined} -

+

{permissions === true ? "Delete" : "Leave"} Collection

+
+
{permissions === true ? ( <> diff --git a/components/ModalContent/DeleteLinkModal.tsx b/components/ModalContent/DeleteLinkModal.tsx index 3a2654e8..ebbe5ae2 100644 --- a/components/ModalContent/DeleteLinkModal.tsx +++ b/components/ModalContent/DeleteLinkModal.tsx @@ -11,6 +11,7 @@ import Link from "next/link"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faLink, faTrashCan } from "@fortawesome/free-solid-svg-icons"; import Modal from "../Modal"; +import { useRouter } from "next/router"; type Props = { onClose: Function; @@ -21,17 +22,11 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) { const [link, setLink] = useState(activeLink); - let shortendURL; - - try { - shortendURL = new URL(link.url).host.toLowerCase(); - } catch (error) { - console.log(error); - } - const { removeLink } = useLinkStore(); const [submitLoader, setSubmitLoader] = useState(false); + const router = useRouter(); + useEffect(() => { setLink(activeLink); }, []); @@ -45,12 +40,19 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) { response.ok && toast.success(`Link Deleted.`); + if (router.pathname.startsWith("/links/[id]")) { + router.push("/dashboard"); + } + onClose(); }; return ( -

Delete Link

+

Delete Link

+ +
+

Are you sure you want to delete this Link?

diff --git a/components/ModalContent/EditCollectionModal.tsx b/components/ModalContent/EditCollectionModal.tsx index 5d156877..34d2502d 100644 --- a/components/ModalContent/EditCollectionModal.tsx +++ b/components/ModalContent/EditCollectionModal.tsx @@ -49,7 +49,9 @@ export default function EditCollectionModal({ return ( -

Edit Collection Info

+

Edit Collection Info

+ +
@@ -108,7 +110,10 @@ export default function EditCollectionModal({
-
diff --git a/components/ModalContent/EditCollectionSharingModal.tsx b/components/ModalContent/EditCollectionSharingModal.tsx index 7523e34f..2bd7a41a 100644 --- a/components/ModalContent/EditCollectionSharingModal.tsx +++ b/components/ModalContent/EditCollectionSharingModal.tsx @@ -95,10 +95,12 @@ export default function EditCollectionSharingModal({ return ( -

+

{permissions === true ? "Share and Collaborate" : "Team"}

+
+
{permissions === true && (
@@ -178,7 +180,7 @@ export default function EditCollectionSharingModal({ setMemberState ) } - className="btn btn-primary text-white btn-square" + className="btn btn-accent text-white btn-square btn-sm h-10 w-10" >
@@ -201,7 +203,7 @@ export default function EditCollectionSharingModal({
@@ -209,6 +211,7 @@ export default function EditCollectionSharingModal({ src={ collectionOwner.image ? collectionOwner.image : undefined } + name={collectionOwner.name} />
@@ -257,6 +260,7 @@ export default function EditCollectionSharingModal({

{e.user.name}

@@ -323,7 +327,7 @@ export default function EditCollectionSharingModal({ }} /> Save diff --git a/components/ModalContent/EditLinkModal.tsx b/components/ModalContent/EditLinkModal.tsx index 6c47db43..d626e709 100644 --- a/components/ModalContent/EditLinkModal.tsx +++ b/components/ModalContent/EditLinkModal.tsx @@ -24,7 +24,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) { let shortendURL; try { - shortendURL = new URL(link.url).host.toLowerCase(); + shortendURL = new URL(link.url || "").host.toLowerCase(); } catch (error) { console.log(error); } @@ -78,20 +78,24 @@ export default function EditLinkModal({ onClose, activeLink }: Props) { return ( -

Edit Link

+

Edit Link

- - -

{shortendURL}

- +
+ + {link.url ? ( + + +

{shortendURL}

+ + ) : undefined}

Name

@@ -155,7 +159,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
-
diff --git a/components/ModalContent/ExpandedLink.tsx b/components/ModalContent/ExpandedLink.tsx new file mode 100644 index 00000000..464d5684 --- /dev/null +++ b/components/ModalContent/ExpandedLink.tsx @@ -0,0 +1,254 @@ +import { + CollectionIncludingMembersAndLinkCount, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import Image from "next/image"; +import ColorThief, { RGBColor } from "colorthief"; +import { useEffect, useState } from "react"; +import Link from "next/link"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faArrowUpRightFromSquare, + faBoxArchive, + faCloudArrowDown, + faFolder, +} from "@fortawesome/free-solid-svg-icons"; +import useCollectionStore from "@/store/collections"; +import { + faCalendarDays, + faFileImage, + faFilePdf, +} from "@fortawesome/free-regular-svg-icons"; +import isValidUrl from "@/lib/shared/isValidUrl"; +import unescapeString from "@/lib/client/unescapeString"; +import useLocalSettingsStore from "@/store/localSettings"; +import Modal from "../Modal"; + +type Props = { + link: LinkIncludingShortenedCollectionAndTags; + onClose: Function; +}; + +export default function LinkDetails({ link, onClose }: Props) { + const { + settings: { theme }, + } = useLocalSettingsStore(); + + const [imageError, setImageError] = useState(false); + const formattedDate = new Date(link.createdAt as string).toLocaleString( + "en-US", + { + year: "numeric", + month: "short", + day: "numeric", + } + ); + + const { collections } = useCollectionStore(); + + const [collection, setCollection] = + useState( + collections.find( + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount + ); + + useEffect(() => { + setCollection( + collections.find( + (e) => e.id === link.collection.id + ) as CollectionIncludingMembersAndLinkCount + ); + }, [collections]); + + const [colorPalette, setColorPalette] = useState(); + + const colorThief = new ColorThief(); + + const url = link.url && isValidUrl(link.url) ? new URL(link.url) : undefined; + + const handleDownload = (format: "png" | "pdf") => { + const path = `/api/v1/archives/${link.collection.id}/${link.id}.${format}`; + fetch(path) + .then((response) => { + if (response.ok) { + // Create a temporary link and click it to trigger the download + const link = document.createElement("a"); + link.href = path; + link.download = format === "pdf" ? "PDF" : "Screenshot"; + link.click(); + } else { + console.error("Failed to download file"); + } + }) + .catch((error) => { + console.error("Error:", error); + }); + }; + + return ( + +
+ {!imageError && url && ( + { + try { + const color = colorThief.getPalette( + e.target as HTMLImageElement, + 4 + ); + + setColorPalette(color); + } catch (err) { + console.log(err); + } + }} + onError={(e) => { + setImageError(true); + }} + /> + )} +
+

+ {unescapeString(link.name)} +

+ + {url ? url.host : link.url} + +
+
+
+ + +

+ {collection?.name} +

+ + {link.tags.map((e, i) => ( + +

+ {e.name} +

+ + ))} +
+ {link.description && ( + <> +
+ {unescapeString(link.description)} +
+ + )} + +
+
+ +

Archived Formats:

+
+
+ +

{formattedDate}

+
+
+
+
+
+
+ +
+ +

Screenshot

+
+ +
+ + + + +
handleDownload("png")} + className="cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-2 rounded-md" + > + +
+
+
+ +
+
+
+ +
+ +

PDF

+
+ +
+ + + + +
handleDownload("pdf")} + className="cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-2 rounded-md" + > + +
+
+
+
+
+ ); +} diff --git a/components/ModalContent/NewCollectionModal.tsx b/components/ModalContent/NewCollectionModal.tsx index 27936e4d..0773cb1a 100644 --- a/components/ModalContent/NewCollectionModal.tsx +++ b/components/ModalContent/NewCollectionModal.tsx @@ -54,7 +54,9 @@ export default function NewCollectionModal({ onClose }: Props) { return ( -

Create a New Collection

+

Create a New Collection

+ +
@@ -113,7 +115,10 @@ export default function NewCollectionModal({ onClose }: Props) {
-
diff --git a/components/ModalContent/NewLinkModal.tsx b/components/ModalContent/NewLinkModal.tsx index cd7cd53b..7a0087ce 100644 --- a/components/ModalContent/NewLinkModal.tsx +++ b/components/ModalContent/NewLinkModal.tsx @@ -23,6 +23,7 @@ export default function NewLinkModal({ onClose }: Props) { name: "", url: "", description: "", + type: "url", tags: [], screenshotPath: "", pdfPath: "", @@ -32,7 +33,7 @@ export default function NewLinkModal({ onClose }: Props) { name: "", ownerId: data?.user.id as number, }, - }; + } as LinkIncludingShortenedCollectionAndTags; const [link, setLink] = useState(initial); @@ -40,8 +41,6 @@ export default function NewLinkModal({ onClose }: Props) { const { addLink } = useLinkStore(); const [submitLoader, setSubmitLoader] = useState(false); - const [resetCollectionSelection, setResetCollectionSelection] = useState(""); - const [optionsExpanded, setOptionsExpanded] = useState(false); const router = useRouter(); @@ -65,10 +64,6 @@ export default function NewLinkModal({ onClose }: Props) { }; useEffect(() => { - setResetCollectionSelection(Date.now().toString()); - console.log(link); - - setOptionsExpanded(false); if (router.query.id) { const currentCollection = collections.find( (e) => e.id == Number(router.query.id) @@ -122,12 +117,15 @@ export default function NewLinkModal({ onClose }: Props) { return ( -

Create a New Link

+

Create a New Link

+ +
+

Link

setLink({ ...link, url: e.target.value })} placeholder="e.g. http://example.com/" className="bg-base-200" @@ -142,7 +140,6 @@ export default function NewLinkModal({ onClose }: Props) { label: link.collection.name, value: link.collection.id, }} - id={resetCollectionSelection} /> ) : null}
@@ -195,7 +192,7 @@ export default function NewLinkModal({ onClose }: Props) {

{optionsExpanded ? "Hide" : "More"} Options

-
diff --git a/components/ModalContent/PreservedFormatsModal.tsx b/components/ModalContent/PreservedFormatsModal.tsx new file mode 100644 index 00000000..951e9cac --- /dev/null +++ b/components/ModalContent/PreservedFormatsModal.tsx @@ -0,0 +1,237 @@ +import React, { useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; +import CollectionSelection from "@/components/InputSelect/CollectionSelection"; +import TagSelection from "@/components/InputSelect/TagSelection"; +import TextInput from "@/components/TextInput"; +import unescapeString from "@/lib/client/unescapeString"; +import useLinkStore from "@/store/links"; +import { + ArchivedFormat, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import toast from "react-hot-toast"; +import Link from "next/link"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faArrowUpRightFromSquare, + faCloudArrowDown, + faLink, + faTrashCan, + faUpRightFromSquare, +} from "@fortawesome/free-solid-svg-icons"; +import Modal from "../Modal"; +import { faFileImage, faFilePdf } from "@fortawesome/free-regular-svg-icons"; +import { useRouter } from "next/router"; +import { useSession } from "next-auth/react"; + +type Props = { + onClose: Function; + activeLink: LinkIncludingShortenedCollectionAndTags; +}; + +export default function PreservedFormatsModal({ onClose, activeLink }: Props) { + const session = useSession(); + const { links, getLink } = useLinkStore(); + + const [link, setLink] = + useState(activeLink); + + const router = useRouter(); + + useEffect(() => { + let isPublicRoute = router.pathname.startsWith("/public") + ? true + : undefined; + + (async () => { + const data = await getLink(link.id as number, isPublicRoute); + setLink( + (data as any).response as LinkIncludingShortenedCollectionAndTags + ); + })(); + + let interval: NodeJS.Timer | undefined; + if (link?.screenshotPath === "pending" || link?.pdfPath === "pending") { + interval = setInterval(async () => { + const data = await getLink(link.id as number, isPublicRoute); + setLink( + (data as any).response as LinkIncludingShortenedCollectionAndTags + ); + }, 5000); + } else { + if (interval) { + clearInterval(interval); + } + } + + return () => { + if (interval) { + clearInterval(interval); + } + }; + }, [link?.screenshotPath, link?.pdfPath, link?.readabilityPath]); + + const updateArchive = async () => { + const load = toast.loading("Sending request..."); + + const response = await fetch(`/api/v1/links/${link?.id}/archive`, { + method: "PUT", + }); + + const data = await response.json(); + + toast.dismiss(load); + + if (response.ok) { + toast.success(`Link is being archived...`); + getLink(link?.id as number); + } else toast.error(data.response); + }; + + const handleDownload = (format: ArchivedFormat) => { + const path = `/api/v1/archives/${link?.id}?format=${format}`; + fetch(path) + .then((response) => { + if (response.ok) { + // Create a temporary link and click it to trigger the download + const link = document.createElement("a"); + link.href = path; + link.download = format === ArchivedFormat.png ? "Screenshot" : "PDF"; + link.click(); + } else { + console.error("Failed to download file"); + } + }) + .catch((error) => { + console.error("Error:", error); + }); + }; + + return ( + +

Preserved Formats

+ +
+ +
+ {link?.screenshotPath && link?.screenshotPath !== "pending" ? ( +
+
+
+ +
+ +

Screenshot

+
+ +
+
handleDownload(ArchivedFormat.png)} + className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md" + > + +
+ + + + +
+
+ ) : undefined} + + {link?.pdfPath && link.pdfPath !== "pending" ? ( +
+
+
+ +
+ +

PDF

+
+ +
+
handleDownload(ArchivedFormat.pdf)} + className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md" + > + +
+ + + + +
+
+ ) : undefined} + +
+ {link?.collection.ownerId === session.data?.user.id ? ( +
updateArchive()} + > +
+

Update Preserved Formats

+

(Refresh Link)

+
+
+ ) : undefined} + +

+ View Latest Snapshot on archive.org +

+ + +
+
+
+ ); +} diff --git a/components/ModalContent/UploadFileModal.tsx b/components/ModalContent/UploadFileModal.tsx new file mode 100644 index 00000000..887ce679 --- /dev/null +++ b/components/ModalContent/UploadFileModal.tsx @@ -0,0 +1,246 @@ +import React, { useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; +import CollectionSelection from "@/components/InputSelect/CollectionSelection"; +import TagSelection from "@/components/InputSelect/TagSelection"; +import TextInput from "@/components/TextInput"; +import unescapeString from "@/lib/client/unescapeString"; +import useCollectionStore from "@/store/collections"; +import useLinkStore from "@/store/links"; +import { + ArchivedFormat, + LinkIncludingShortenedCollectionAndTags, +} from "@/types/global"; +import { useSession } from "next-auth/react"; +import { useRouter } from "next/router"; +import toast from "react-hot-toast"; +import Modal from "../Modal"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faQuestion } from "@fortawesome/free-solid-svg-icons"; +import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons"; + +type Props = { + onClose: Function; +}; + +export default function UploadFileModal({ onClose }: Props) { + const { data } = useSession(); + + const initial = { + name: "", + url: "", + description: "", + type: "url", + tags: [], + screenshotPath: "", + pdfPath: "", + readabilityPath: "", + textContent: "", + collection: { + name: "", + ownerId: data?.user.id as number, + }, + } as LinkIncludingShortenedCollectionAndTags; + + const [link, setLink] = + useState(initial); + + const [file, setFile] = useState(); + + const { addLink } = useLinkStore(); + const [submitLoader, setSubmitLoader] = useState(false); + + const [optionsExpanded, setOptionsExpanded] = useState(false); + + const router = useRouter(); + const { collections } = useCollectionStore(); + + const setCollection = (e: any) => { + if (e?.__isNew__) e.value = null; + + setLink({ + ...link, + collection: { id: e?.value, name: e?.label, ownerId: e?.ownerId }, + }); + }; + + const setTags = (e: any) => { + const tagNames = e.map((e: any) => { + return { name: e.label }; + }); + + setLink({ ...link, tags: tagNames }); + }; + + useEffect(() => { + setOptionsExpanded(false); + if (router.query.id) { + const currentCollection = collections.find( + (e) => e.id == Number(router.query.id) + ); + + if ( + currentCollection && + currentCollection.ownerId && + router.asPath.startsWith("/collections/") + ) + setLink({ + ...initial, + collection: { + id: currentCollection.id, + name: currentCollection.name, + ownerId: currentCollection.ownerId, + }, + }); + } else + setLink({ + ...initial, + collection: { + name: "Unorganized", + ownerId: data?.user.id as number, + }, + }); + }, []); + + const submit = async () => { + if (!submitLoader && file) { + let fileType: ArchivedFormat | null = null; + let linkType: "url" | "image" | "pdf" | null = null; + + if (file?.type === "image/jpg" || file.type === "image/jpeg") { + fileType = ArchivedFormat.jpeg; + linkType = "image"; + } else if (file.type === "image/png") { + fileType = ArchivedFormat.png; + linkType = "image"; + } else if (file.type === "application/pdf") { + fileType = ArchivedFormat.pdf; + linkType = "pdf"; + } + + if (fileType !== null && linkType !== null) { + setSubmitLoader(true); + + let response; + + const load = toast.loading("Creating..."); + + response = await addLink({ + ...link, + type: linkType, + name: link.name ? link.name : file.name.replace(/\.[^/.]+$/, ""), + }); + + toast.dismiss(load); + + if (response.ok) { + const formBody = new FormData(); + file && formBody.append("file", file); + + await fetch( + `/api/v1/archives/${ + (response.data as LinkIncludingShortenedCollectionAndTags).id + }?format=${fileType}`, + { + body: formBody, + method: "POST", + } + ); + toast.success(`Created!`); + onClose(); + } else toast.error(response.data as string); + + setSubmitLoader(false); + + return response; + } + } + }; + + return ( + +
+

Upload File

+
+
+
+
+

File

+ +

+ PDF, PNG, JPG (Up to {process.env.NEXT_PUBLIC_MAX_UPLOAD_SIZE || 30} + MB) +

+
+
+

Collection

+ {link.collection.name ? ( + + ) : null} +
+
+ {optionsExpanded ? ( +
+ {/*
*/} +
+
+

Name

+ setLink({ ...link, name: e.target.value })} + placeholder="e.g. Example Link" + className="bg-base-200" + /> +
+ +
+

Tags

+ { + return { label: e.name, value: e.id }; + })} + /> +
+ +
+

Description

+