From db9e3a38c5090bb483318cb42baa2d670eb704e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Rialland?= Date: Thu, 17 Oct 2024 15:49:10 +0000 Subject: [PATCH 1/2] refactor(web): Add button to upload files on mobile --- .../components/dashboard/UploadDropzone.tsx | 23 +++++-- .../dashboard/bookmarks/EditorCard.tsx | 62 ++++++++++++++++--- 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/apps/web/components/dashboard/UploadDropzone.tsx b/apps/web/components/dashboard/UploadDropzone.tsx index 335ac72a..461418e3 100644 --- a/apps/web/components/dashboard/UploadDropzone.tsx +++ b/apps/web/components/dashboard/UploadDropzone.tsx @@ -81,11 +81,7 @@ function useUploadAssets({ }; } -export default function UploadDropzone({ - children, -}: { - children: React.ReactNode; -}) { +export function useFileUploader() { const [numUploading, setNumUploading] = useState(0); const [numUploaded, setNumUploaded] = useState(0); const uploadAssets = useUploadAssets({ @@ -102,6 +98,23 @@ export default function UploadDropzone({ }, }); + return { + numUploading, + setNumUploading, + numUploaded, + setNumUploaded, + uploadAssets, + }; +} + +export default function UploadDropzone({ + children, +}: { + children: React.ReactNode; +}) { + const { numUploading, setNumUploading, numUploaded, uploadAssets } = + useFileUploader(); + const [isDragging, setDragging] = useState(false); const onDrop = (acceptedFiles: File[]) => { uploadAssets(acceptedFiles); diff --git a/apps/web/components/dashboard/bookmarks/EditorCard.tsx b/apps/web/components/dashboard/bookmarks/EditorCard.tsx index d1489b09..8343c480 100644 --- a/apps/web/components/dashboard/bookmarks/EditorCard.tsx +++ b/apps/web/components/dashboard/bookmarks/EditorCard.tsx @@ -15,13 +15,14 @@ import { } from "@/lib/userLocalSettings/bookmarksLayout"; import { cn, getOS } from "@/lib/utils"; import { zodResolver } from "@hookform/resolvers/zod"; +import { FilePlus } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useCreateBookmarkWithPostHook } from "@hoarder/shared-react/hooks/bookmarks"; import { BookmarkTypes } from "@hoarder/shared/types/bookmarks"; -import { useUploadAsset } from "../UploadDropzone"; +import { useFileUploader, useUploadAsset } from "../UploadDropzone"; function useFocusOnKeyPress(inputRef: React.RefObject) { useEffect(() => { @@ -167,6 +168,16 @@ export default function EditorCard({ className }: { className?: string }) { } }; + const uploadFile = useRef(null); + + const { numUploading, setNumUploading, numUploaded, uploadAssets } = + useFileUploader(); + + const handleFileUpload = (acceptedFiles: File[]) => { + uploadAssets(acceptedFiles); + setNumUploading(acceptedFiles.length); + }; + const OS = getOS(); return ( @@ -219,13 +230,48 @@ export default function EditorCard({ className }: { className?: string }) { /> - - {form.formState.dirtyFields.text - ? demoMode - ? "Submissions are disabled" - : `Save (${OS === "macos" ? "⌘" : "Ctrl"} + Enter)` - : "Save"} - +
+ + {form.formState.dirtyFields.text + ? demoMode + ? "Submissions are disabled" + : `Save (${OS === "macos" ? "⌘" : "Ctrl"} + Enter)` + : "Save"} + + +
+ {numUploading > 0 && ( + + {numUploaded} / {numUploading} + + )} + + { + if (e.target.files) { + handleFileUpload(Array.from(e.target.files)); + } + }} + > + uploadFile.current?.click()} + > + + +
+
{multiUrlImportState && ( Date: Thu, 17 Oct 2024 15:56:32 +0000 Subject: [PATCH 2/2] refactor(web): Use asset types from @hoarder/shared for the input file --- apps/web/app/api/assets/route.ts | 7 ++--- .../dashboard/bookmarks/EditorCard.tsx | 3 +- apps/workers/crawlerWorker.ts | 8 +++-- packages/shared/assetTypes.ts | 29 +++++++++++++++++++ packages/shared/assetdb.ts | 27 +---------------- 5 files changed, 39 insertions(+), 35 deletions(-) create mode 100644 packages/shared/assetTypes.ts diff --git a/apps/web/app/api/assets/route.ts b/apps/web/app/api/assets/route.ts index 0e52ff93..603aa159 100644 --- a/apps/web/app/api/assets/route.ts +++ b/apps/web/app/api/assets/route.ts @@ -3,11 +3,8 @@ import { TRPCError } from "@trpc/server"; import type { ZUploadResponse } from "@hoarder/shared/types/uploads"; import { assets, AssetTypes } from "@hoarder/db/schema"; -import { - newAssetId, - saveAsset, - SUPPORTED_UPLOAD_ASSET_TYPES, -} from "@hoarder/shared/assetdb"; +import { newAssetId, saveAsset } from "@hoarder/shared/assetdb"; +import { SUPPORTED_UPLOAD_ASSET_TYPES } from "@hoarder/shared/assetTypes"; import serverConfig from "@hoarder/shared/config"; const MAX_UPLOAD_SIZE_BYTES = serverConfig.maxAssetSizeMb * 1024 * 1024; diff --git a/apps/web/components/dashboard/bookmarks/EditorCard.tsx b/apps/web/components/dashboard/bookmarks/EditorCard.tsx index 8343c480..2cbccee2 100644 --- a/apps/web/components/dashboard/bookmarks/EditorCard.tsx +++ b/apps/web/components/dashboard/bookmarks/EditorCard.tsx @@ -20,6 +20,7 @@ import { useForm } from "react-hook-form"; import { z } from "zod"; import { useCreateBookmarkWithPostHook } from "@hoarder/shared-react/hooks/bookmarks"; +import { SUPPORTED_UPLOAD_ASSET_TYPES } from "@hoarder/shared/assetTypes"; import { BookmarkTypes } from "@hoarder/shared/types/bookmarks"; import { useFileUploader, useUploadAsset } from "../UploadDropzone"; @@ -254,7 +255,7 @@ export default function EditorCard({ className }: { className?: string }) { { diff --git a/apps/workers/crawlerWorker.ts b/apps/workers/crawlerWorker.ts index 74413c63..56738851 100644 --- a/apps/workers/crawlerWorker.ts +++ b/apps/workers/crawlerWorker.ts @@ -34,15 +34,17 @@ import { } from "@hoarder/db/schema"; import { DequeuedJob, Runner } from "@hoarder/queue"; import { - ASSET_TYPES, deleteAsset, getAssetSize, - IMAGE_ASSET_TYPES, newAssetId, saveAsset, saveAssetFromFile, - SUPPORTED_UPLOAD_ASSET_TYPES, } from "@hoarder/shared/assetdb"; +import { + ASSET_TYPES, + IMAGE_ASSET_TYPES, + SUPPORTED_UPLOAD_ASSET_TYPES, +} from "@hoarder/shared/assetTypes"; import serverConfig from "@hoarder/shared/config"; import logger from "@hoarder/shared/logger"; import { diff --git a/packages/shared/assetTypes.ts b/packages/shared/assetTypes.ts new file mode 100644 index 00000000..2b51417d --- /dev/null +++ b/packages/shared/assetTypes.ts @@ -0,0 +1,29 @@ +export const enum ASSET_TYPES { + IMAGE_JPEG = "image/jpeg", + IMAGE_PNG = "image/png", + IMAGE_WEBP = "image/webp", + APPLICATION_PDF = "application/pdf", + TEXT_HTML = "text/html", +} + +export const IMAGE_ASSET_TYPES: Set = new Set([ + ASSET_TYPES.IMAGE_JPEG, + ASSET_TYPES.IMAGE_PNG, + ASSET_TYPES.IMAGE_WEBP, +]); + +export const FILE_ASSET_TYPES: Set = new Set([ + ASSET_TYPES.APPLICATION_PDF, +]); + +// The assets that we allow the users to upload +export const SUPPORTED_UPLOAD_ASSET_TYPES: Set = new Set([ + ...IMAGE_ASSET_TYPES, + ...FILE_ASSET_TYPES, +]); + +// The assets that we support saving in the asset db +export const SUPPORTED_ASSET_TYPES: Set = new Set([ + ...SUPPORTED_UPLOAD_ASSET_TYPES, + ASSET_TYPES.TEXT_HTML, +]); diff --git a/packages/shared/assetdb.ts b/packages/shared/assetdb.ts index 64413e9f..0c01db1b 100644 --- a/packages/shared/assetdb.ts +++ b/packages/shared/assetdb.ts @@ -3,36 +3,11 @@ import * as path from "path"; import { Glob } from "glob"; import { z } from "zod"; +import { SUPPORTED_ASSET_TYPES } from "./assetTypes"; import serverConfig from "./config"; const ROOT_PATH = path.join(serverConfig.dataDir, "assets"); -export const enum ASSET_TYPES { - IMAGE_JPEG = "image/jpeg", - IMAGE_PNG = "image/png", - IMAGE_WEBP = "image/webp", - APPLICATION_PDF = "application/pdf", - TEXT_HTML = "text/html", -} - -export const IMAGE_ASSET_TYPES: Set = new Set([ - ASSET_TYPES.IMAGE_JPEG, - ASSET_TYPES.IMAGE_PNG, - ASSET_TYPES.IMAGE_WEBP, -]); - -// The assets that we allow the users to upload -export const SUPPORTED_UPLOAD_ASSET_TYPES: Set = new Set([ - ...IMAGE_ASSET_TYPES, - ASSET_TYPES.APPLICATION_PDF, -]); - -// The assets that we support saving in the asset db -export const SUPPORTED_ASSET_TYPES: Set = new Set([ - ...SUPPORTED_UPLOAD_ASSET_TYPES, - ASSET_TYPES.TEXT_HTML, -]); - function getAssetDir(userId: string, assetId: string) { return path.join(ROOT_PATH, userId, assetId); }