From 785a5b574992296e187a66412dd42f7b4a686353 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Tue, 19 Mar 2024 00:33:11 +0000 Subject: 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 --- packages/trpc/routers/bookmarks.ts | 34 ++++++++++++++++++++++++++++++++-- packages/trpc/types/bookmarks.ts | 8 ++++++++ packages/trpc/types/uploads.ts | 13 +++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 packages/trpc/types/uploads.ts (limited to 'packages/trpc') diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts index c2d78c7f..f0abda84 100644 --- a/packages/trpc/routers/bookmarks.ts +++ b/packages/trpc/routers/bookmarks.ts @@ -5,6 +5,7 @@ import { z } from "zod"; import { db as DONT_USE_db } from "@hoarder/db"; import { + bookmarkAssets, bookmarkLinks, bookmarks, bookmarksInLists, @@ -75,6 +76,7 @@ async function dummyDrizzleReturnType() { }, link: true, text: true, + asset: true, }, }); if (!x) { @@ -88,13 +90,19 @@ type BookmarkQueryReturnType = Awaited< >; function toZodSchema(bookmark: BookmarkQueryReturnType): ZBookmark { - const { tagsOnBookmarks, link, text, ...rest } = bookmark; + const { tagsOnBookmarks, link, text, asset, ...rest } = bookmark; let content: ZBookmarkContent; if (link) { content = { type: "link", ...link }; } else if (text) { content = { type: "text", text: text.text ?? "" }; + } else if (asset) { + content = { + type: "asset", + assetType: asset.assetType, + assetId: asset.assetId, + }; } else { throw new Error("Unknown content type"); } @@ -157,6 +165,22 @@ export const bookmarksAppRouter = router({ }; break; } + case "asset": { + const [asset] = await tx + .insert(bookmarkAssets) + .values({ + id: bookmark.id, + assetType: input.assetType, + assetId: input.assetId, + }) + .returning(); + content = { + type: "asset", + assetType: asset.assetType, + assetId: asset.assetId, + }; + break; + } } return { @@ -176,7 +200,8 @@ export const bookmarksAppRouter = router({ }); break; } - case "text": { + case "text": + case "asset": { await OpenAIQueue.add("openai", { bookmarkId: bookmark.id, }); @@ -292,6 +317,7 @@ export const bookmarksAppRouter = router({ }, link: true, text: true, + asset: true, }, }); if (!bookmark) { @@ -341,6 +367,7 @@ export const bookmarksAppRouter = router({ }, link: true, text: true, + asset: true, }, }); @@ -412,6 +439,7 @@ export const bookmarksAppRouter = router({ .leftJoin(bookmarkTags, eq(tagsOnBookmarks.tagId, bookmarkTags.id)) .leftJoin(bookmarkLinks, eq(bookmarkLinks.id, sq.id)) .leftJoin(bookmarkTexts, eq(bookmarkTexts.id, sq.id)) + .leftJoin(bookmarkAssets, eq(bookmarkAssets.id, sq.id)) .orderBy(desc(sq.createdAt)); const bookmarksRes = results.reduce>( @@ -423,6 +451,8 @@ export const bookmarksAppRouter = router({ content = { type: "link", ...row.bookmarkLinks }; } else if (row.bookmarkTexts) { content = { type: "text", text: row.bookmarkTexts.text ?? "" }; + } else if (row.bookmarkAssets) { + content = { type: "asset", assetId: row.bookmarkAssets.assetId, assetType: row.bookmarkAssets.assetType }; } else { throw new Error("Unknown content type"); } diff --git a/packages/trpc/types/bookmarks.ts b/packages/trpc/types/bookmarks.ts index e366859e..f8848d35 100644 --- a/packages/trpc/types/bookmarks.ts +++ b/packages/trpc/types/bookmarks.ts @@ -19,9 +19,17 @@ export const zBookmarkedTextSchema = z.object({ }); export type ZBookmarkedText = z.infer; +export const zBookmarkedAssetSchema = z.object({ + type: z.literal("asset"), + assetType: z.enum(["image"]), + assetId: z.string(), +}); +export type ZBookmarkedAsset = z.infer; + export const zBookmarkContentSchema = z.discriminatedUnion("type", [ zBookmarkedLinkSchema, zBookmarkedTextSchema, + zBookmarkedAssetSchema, ]); export type ZBookmarkContent = z.infer; diff --git a/packages/trpc/types/uploads.ts b/packages/trpc/types/uploads.ts new file mode 100644 index 00000000..a378f802 --- /dev/null +++ b/packages/trpc/types/uploads.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; + +export const zUploadErrorSchema = z.object({ + error: z.string(), +}); + +export const zUploadResponseSchema = z.object({ + assetId: z.string(), + contentType: z.string(), + size: z.number(), +}); + +export type ZUploadResponse = z.infer; -- cgit v1.2.3-70-g09d2