progressed file uploads feature (almost done!)

This commit is contained in:
daniel31x13
2024-04-01 02:56:54 -04:00
parent c659711181
commit e67fef1d04
14 changed files with 292 additions and 176 deletions
+5 -14
View File
@@ -19,6 +19,7 @@ import { generateLinkHref } from "@/lib/client/generateLinkHref";
import useAccountStore from "@/store/account";
import usePermissions from "@/hooks/usePermissions";
import toast from "react-hot-toast";
import LinkTypeBadge from "./LinkComponents/LinkTypeBadge";
type Props = {
link: LinkIncludingShortenedCollectionAndTags;
@@ -53,7 +54,9 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
let shortendURL;
try {
shortendURL = new URL(link.url || "").host.toLowerCase();
if (link.url) {
shortendURL = new URL(link.url).host.toLowerCase();
}
} catch (error) {
console.log(error);
}
@@ -109,7 +112,6 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
editMode &&
(permissions === true || permissions?.canCreate || permissions?.canDelete);
// window.open ('www.yourdomain.com', '_ blank');
return (
<div
ref={ref}
@@ -162,18 +164,7 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
{unescapeString(link.name || link.description) || link.url}
</p>
<Link
href={link.url || ""}
target="_blank"
title={link.url || ""}
onClick={(e) => {
e.stopPropagation();
}}
className="flex gap-1 item-center select-none text-neutral mt-1 hover:opacity-70 duration-100"
>
<i className="bi-link-45deg text-lg mt-[0.10rem] leading-none"></i>
<p className="text-sm truncate">{shortendURL}</p>
</Link>
<LinkTypeBadge link={link} />
</div>
<hr className="divider mt-2 mb-1 last:hidden border-t border-neutral-content h-[1px]" />
@@ -122,18 +122,20 @@ export default function LinkActions({
</div>
</li>
) : undefined}
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setPreservedFormatsModal(true);
}}
>
Preserved Formats
</div>
</li>
{link.type === "url" && (
<li>
<div
role="button"
tabIndex={0}
onClick={() => {
(document?.activeElement as HTMLElement)?.blur();
setPreservedFormatsModal(true);
}}
>
Preserved Formats
</div>
</li>
)}
{permissions === true || permissions?.canDelete ? (
<li>
<div
@@ -6,9 +6,11 @@ import React from "react";
export default function LinkIcon({
link,
width,
className,
}: {
link: LinkIncludingShortenedCollectionAndTags;
width?: string;
className?: string;
}) {
const url =
isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined;
@@ -16,33 +18,55 @@ export default function LinkIcon({
const iconClasses: string =
"bg-white shadow rounded-md border-[2px] flex item-center justify-center border-white select-none z-10" +
" " +
(width || "w-12");
(width || "w-12") +
" " +
(className || "");
const [showFavicon, setShowFavicon] = React.useState<boolean>(true);
return (
<>
{link.url && url && showFavicon ? (
<Image
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${link.url}&size=32`}
width={64}
height={64}
alt=""
className={iconClasses}
draggable="false"
onError={() => {
setShowFavicon(false);
}}
/>
) : showFavicon === false ? (
<div className={iconClasses}>
<i className="bi-link-45deg text-4xl text-black"></i>
</div>
{link.type === "url" && url ? (
showFavicon ? (
<Image
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${link.url}&size=32`}
width={64}
height={64}
alt=""
className={iconClasses}
draggable="false"
onError={() => {
setShowFavicon(false);
}}
/>
) : (
<LinkPlaceholderIcon iconClasses={iconClasses} icon="bi-link-45deg" />
)
) : link.type === "pdf" ? (
<i className={`bi-file-earmark-pdf ${iconClasses}`}></i>
<LinkPlaceholderIcon
iconClasses={iconClasses}
icon="bi-file-earmark-pdf"
/>
) : link.type === "image" ? (
<i className={`bi-file-earmark-image ${iconClasses}`}></i>
<LinkPlaceholderIcon
iconClasses={iconClasses}
icon="bi-file-earmark-image"
/>
) : undefined}
</>
);
}
const LinkPlaceholderIcon = ({
iconClasses,
icon,
}: {
iconClasses: string;
icon: string;
}) => {
return (
<div className={`text-4xl text-black aspect-square ${iconClasses}`}>
<i className={`${icon} m-auto`}></i>
</div>
);
};
@@ -0,0 +1,38 @@
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import Link from "next/link";
import React from "react";
export default function LinkTypeBadge({
link,
}: {
link: LinkIncludingShortenedCollectionAndTags;
}) {
let shortendURL;
if (link.type === "url" && link.url) {
try {
shortendURL = new URL(link.url).host.toLowerCase();
} catch (error) {
console.log(error);
}
}
return link.url && shortendURL ? (
<Link
href={link.url || ""}
target="_blank"
title={link.url || ""}
onClick={(e) => {
e.stopPropagation();
}}
className="flex gap-1 item-center select-none text-neutral mt-1 hover:opacity-70 duration-100"
>
<i className="bi-link-45deg text-lg mt-[0.1rem] leading-none"></i>
<p className="text-sm truncate">{shortendURL}</p>
</Link>
) : (
<div className="badge badge-primary badge-sm my-1 select-none">
{link.type}
</div>
);
}
+7 -27
View File
@@ -16,6 +16,7 @@ import { generateLinkHref } from "@/lib/client/generateLinkHref";
import useAccountStore from "@/store/account";
import usePermissions from "@/hooks/usePermissions";
import toast from "react-hot-toast";
import LinkTypeBadge from "./LinkComponents/LinkTypeBadge";
type Props = {
link: LinkIncludingShortenedCollectionAndTags;
@@ -56,14 +57,6 @@ export default function LinkCardCompact({
}
};
let shortendURL;
try {
shortendURL = new URL(link.url || "").host.toLowerCase();
} catch (error) {
console.log(error);
}
const [collection, setCollection] =
useState<CollectionIncludingMembersAndLinkCount>(
collections.find(
@@ -130,7 +123,11 @@ export default function LinkCardCompact({
}
>
<div className="shrink-0">
<LinkIcon link={link} width="sm:w-12 w-8 mt-1 sm:mt-0" />
<LinkIcon
link={link}
width="sm:w-12 w-8"
className="mt-1 sm:mt-0"
/>
</div>
<div className="w-[calc(100%-56px)] ml-2">
@@ -143,24 +140,7 @@ export default function LinkCardCompact({
{collection ? (
<LinkCollection link={link} collection={collection} />
) : undefined}
{link.url ? (
<Link
href={link.url || ""}
target="_blank"
title={link.url || ""}
onClick={(e) => {
e.stopPropagation();
}}
className="flex gap-1 item-center select-none text-neutral mt-1 hover:opacity-70 duration-100"
>
<i className="bi-link-45deg text-lg mt-[0.1rem] leading-none"></i>
<p className="text-sm truncate">{shortendURL}</p>
</Link>
) : (
<div className="badge badge-primary badge-sm my-1 select-none">
{link.type}
</div>
)}
<LinkTypeBadge link={link} />
<LinkDate link={link} />
</div>
</div>
+12 -46
View File
@@ -43,7 +43,7 @@ export default function UploadFileModal({ onClose }: Props) {
const [file, setFile] = useState<File>();
const { addLink } = useLinkStore();
const { uploadFile } = useLinkStore();
const [submitLoader, setSubmitLoader] = useState(false);
const [optionsExpanded, setOptionsExpanded] = useState(false);
@@ -100,56 +100,22 @@ export default function UploadFileModal({ onClose }: Props) {
const submit = async () => {
if (!submitLoader && file) {
let fileType: ArchivedFormat | null = null;
let linkType: "url" | "image" | "pdf" | null = null;
setSubmitLoader(true);
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";
}
const load = toast.loading("Creating...");
if (fileType !== null && linkType !== null) {
setSubmitLoader(true);
const response = await uploadFile(link, file);
let response;
toast.dismiss(load);
const load = toast.loading("Creating...");
if (response.ok) {
toast.success(`Created!`);
onClose();
} else toast.error(response.data as string);
response = await addLink({
...link,
type: linkType,
name: link.name ? link.name : file.name.replace(/\.[^/.]+$/, ""),
});
setSubmitLoader(false);
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 response;
}
};
@@ -238,7 +204,7 @@ export default function UploadFileModal({ onClose }: Props) {
className="btn btn-accent dark:border-violet-400 text-white"
onClick={submit}
>
Create Link
Upload File
</button>
</div>
</Modal>