aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/UploadDropzone.tsx
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2024-03-19 00:33:11 +0000
committerGitHub <noreply@github.com>2024-03-19 00:33:11 +0000
commit785a5b574992296e187a66412dd42f7b4a686353 (patch)
tree64b608927cc63d7494395f639636fd4b36e5a977 /apps/web/components/dashboard/UploadDropzone.tsx
parent549520919c482e72cdf7adae5ba852d1b6cbe5aa (diff)
downloadkarakeep-785a5b574992296e187a66412dd42f7b4a686353.tar.zst
Feature: Add support for uploading images and automatically inferring their tags (#2)
* feature: Experimental support for asset uploads * feature(web): Add new bookmark type asset * feature: Add support for automatically tagging images * fix: Add support for image assets in preview page * use next Image for fetching the images * Fix auth and error codes in the route handlers * Add support for image uploads on mobile * Fix typing of upload requests * Remove the ugly dragging box * Bump mobile version to 1.3 * Change the editor card placeholder to mention uploading images * Fix a typo * Change ios icon for photo library * Silence typescript error
Diffstat (limited to 'apps/web/components/dashboard/UploadDropzone.tsx')
-rw-r--r--apps/web/components/dashboard/UploadDropzone.tsx79
1 files changed, 79 insertions, 0 deletions
diff --git a/apps/web/components/dashboard/UploadDropzone.tsx b/apps/web/components/dashboard/UploadDropzone.tsx
new file mode 100644
index 00000000..61db8dc5
--- /dev/null
+++ b/apps/web/components/dashboard/UploadDropzone.tsx
@@ -0,0 +1,79 @@
+"use client";
+
+import React, { useState } from "react";
+import { api } from "@/lib/trpc";
+import { useMutation } from "@tanstack/react-query";
+import DropZone from "react-dropzone";
+
+import {
+ zUploadErrorSchema,
+ zUploadResponseSchema,
+} from "@hoarder/trpc/types/uploads";
+
+import { toast } from "../ui/use-toast";
+
+export default function UploadDropzone({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const invalidateAllBookmarks =
+ api.useUtils().bookmarks.getBookmarks.invalidate;
+
+ const { mutate: createBookmark } = api.bookmarks.createBookmark.useMutation({
+ onSuccess: () => {
+ toast({ description: "Bookmark uploaded" });
+ invalidateAllBookmarks();
+ },
+ onError: () => {
+ toast({ description: "Something went wrong", variant: "destructive" });
+ },
+ });
+
+ const { mutate: uploadAsset } = useMutation({
+ mutationFn: async (file: File) => {
+ const formData = new FormData();
+ formData.append("image", file);
+ const resp = await fetch("/api/assets", {
+ method: "POST",
+ body: formData,
+ });
+ if (!resp.ok) {
+ throw new Error(await resp.text());
+ }
+ return zUploadResponseSchema.parse(await resp.json());
+ },
+ onSuccess: async (resp) => {
+ const assetId = resp.assetId;
+ createBookmark({ type: "asset", assetId, assetType: "image" });
+ },
+ onError: (error) => {
+ const err = zUploadErrorSchema.parse(JSON.parse(error.message));
+ toast({ description: err.error, variant: "destructive" });
+ },
+ });
+
+ const [_isDragging, setDragging] = useState(false);
+ const onDrop = (acceptedFiles: File[]) => {
+ const file = acceptedFiles[0];
+ setDragging(false);
+ uploadAsset(file);
+ };
+
+ return (
+ <DropZone
+ multiple={false}
+ noClick
+ onDrop={onDrop}
+ onDragEnter={() => setDragging(true)}
+ onDragLeave={() => setDragging(false)}
+ >
+ {({ getRootProps, getInputProps }) => (
+ <div {...getRootProps()}>
+ <input {...getInputProps()} hidden />
+ {children}
+ </div>
+ )}
+ </DropZone>
+ );
+}