diff options
| -rw-r--r-- | apps/web/components/dashboard/preview/AttachmentBox.tsx | 85 | ||||
| -rw-r--r-- | apps/web/lib/attachments.tsx | 2 | ||||
| -rw-r--r-- | packages/db/schema.ts | 2 | ||||
| -rw-r--r-- | packages/open-api/karakeep-openapi-spec.json | 11 | ||||
| -rw-r--r-- | packages/open-api/lib/bookmarks.ts | 6 | ||||
| -rw-r--r-- | packages/shared/types/bookmarks.ts | 2 | ||||
| -rw-r--r-- | packages/trpc/lib/attachments.ts | 5 | ||||
| -rw-r--r-- | packages/trpc/models/bookmarks.ts | 1 | ||||
| -rw-r--r-- | packages/trpc/routers/assets.ts | 17 | ||||
| -rw-r--r-- | packages/trpc/routers/bookmarks.ts | 1 |
10 files changed, 101 insertions, 31 deletions
diff --git a/apps/web/components/dashboard/preview/AttachmentBox.tsx b/apps/web/components/dashboard/preview/AttachmentBox.tsx index 87c56eb8..e24cc646 100644 --- a/apps/web/components/dashboard/preview/AttachmentBox.tsx +++ b/apps/web/components/dashboard/preview/AttachmentBox.tsx @@ -101,7 +101,11 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { prefetch={false} > {ASSET_TYPE_TO_ICON[asset.assetType]} - <p>{humanFriendlyNameForAssertType(asset.assetType)}</p> + <p> + {asset.assetType === "userUploaded" && asset.fileName + ? asset.fileName + : humanFriendlyNameForAssertType(asset.assetType)} + </p> </Link> <div className="flex gap-2"> <Link @@ -109,35 +113,40 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { target="_blank" href={getAssetUrl(asset.id)} className="flex items-center gap-1" - download={humanFriendlyNameForAssertType(asset.assetType)} + download={ + asset.assetType === "userUploaded" && asset.fileName + ? asset.fileName + : humanFriendlyNameForAssertType(asset.assetType) + } prefetch={false} > <Download className="size-4" /> </Link> - {isAllowedToAttachAsset(asset.assetType) && ( - <FilePickerButton - title="Replace" - loading={isReplacing} - accept=".jgp,.JPG,.jpeg,.png,.webp" - multiple={false} - variant="none" - size="none" - className="flex items-center gap-2" - onFileSelect={(file) => - uploadAsset(file, { - onSuccess: (resp) => { - replaceAsset({ - bookmarkId: bookmark.id, - oldAssetId: asset.id, - newAssetId: resp.assetId, - }); - }, - }) - } - > - <Pencil className="size-4" /> - </FilePickerButton> - )} + {isAllowedToAttachAsset(asset.assetType) && + asset.assetType !== "userUploaded" && ( + <FilePickerButton + title="Replace" + loading={isReplacing} + accept=".jgp,.JPG,.jpeg,.png,.webp" + multiple={false} + variant="none" + size="none" + className="flex items-center gap-2" + onFileSelect={(file) => + uploadAsset(file, { + onSuccess: (resp) => { + replaceAsset({ + bookmarkId: bookmark.id, + oldAssetId: asset.id, + newAssetId: resp.assetId, + }); + }, + }) + } + > + <Pencil className="size-4" /> + </FilePickerButton> + )} {isAllowedToDetachAsset(asset.assetType) && ( <ActionConfirmingDialog title="Delete Attachment?" @@ -194,6 +203,30 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { Attach a Banner </FilePickerButton> )} + <FilePickerButton + title="Upload File" + loading={isAttaching} + multiple={false} + variant="ghost" + size="none" + className="flex w-full items-center justify-center gap-2" + onFileSelect={(file) => + uploadAsset(file, { + onSuccess: (resp) => { + attachAsset({ + bookmarkId: bookmark.id, + asset: { + id: resp.assetId, + assetType: "userUploaded", + }, + }); + }, + }) + } + > + <Plus className="size-4" /> + Upload File + </FilePickerButton> </CollapsibleContent> </Collapsible> ); diff --git a/apps/web/lib/attachments.tsx b/apps/web/lib/attachments.tsx index ce34b295..67941098 100644 --- a/apps/web/lib/attachments.tsx +++ b/apps/web/lib/attachments.tsx @@ -4,6 +4,7 @@ import { FileCode, Image, Paperclip, + Upload, Video, } from "lucide-react"; @@ -18,5 +19,6 @@ export const ASSET_TYPE_TO_ICON: Record<ZAssetType, React.ReactNode> = { video: <Video className="size-4" />, bookmarkAsset: <Paperclip className="size-4" />, linkHtmlContent: <FileCode className="size-4" />, + userUploaded: <Upload className="size-4" />, unknown: <Paperclip className="size-4" />, }; diff --git a/packages/db/schema.ts b/packages/db/schema.ts index c0433d6a..5f523d21 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -228,6 +228,7 @@ export const enum AssetTypes { LINK_VIDEO = "linkVideo", LINK_HTML_CONTENT = "linkHtmlContent", BOOKMARK_ASSET = "bookmarkAsset", + USER_UPLOADED = "userUploaded", UNKNOWN = "unknown", } @@ -246,6 +247,7 @@ export const assets = sqliteTable( AssetTypes.LINK_VIDEO, AssetTypes.LINK_HTML_CONTENT, AssetTypes.BOOKMARK_ASSET, + AssetTypes.USER_UPLOADED, AssetTypes.UNKNOWN, ], }).notNull(), diff --git a/packages/open-api/karakeep-openapi-spec.json b/packages/open-api/karakeep-openapi-spec.json index 0b520b00..2e791fbf 100644 --- a/packages/open-api/karakeep-openapi-spec.json +++ b/packages/open-api/karakeep-openapi-spec.json @@ -317,8 +317,13 @@ "video", "bookmarkAsset", "precrawledArchive", + "userUploaded", "unknown" ] + }, + "fileName": { + "type": "string", + "nullable": true } }, "required": [ @@ -1703,6 +1708,7 @@ "video", "bookmarkAsset", "precrawledArchive", + "userUploaded", "unknown" ] } @@ -1737,8 +1743,13 @@ "video", "bookmarkAsset", "precrawledArchive", + "userUploaded", "unknown" ] + }, + "fileName": { + "type": "string", + "nullable": true } }, "required": [ diff --git a/packages/open-api/lib/bookmarks.ts b/packages/open-api/lib/bookmarks.ts index d49a20ff..0a3806b9 100644 --- a/packages/open-api/lib/bookmarks.ts +++ b/packages/open-api/lib/bookmarks.ts @@ -6,6 +6,7 @@ import { z } from "zod"; import { zAssetSchema, + zAssetTypesSchema, zBareBookmarkSchema, zManipulatedTagSchema, zNewBookmarkRequestSchema, @@ -418,7 +419,10 @@ registry.registerPath({ description: "The asset to attach", content: { "application/json": { - schema: zAssetSchema, + schema: z.object({ + id: z.string(), + assetType: zAssetTypesSchema, + }), }, }, }, diff --git a/packages/shared/types/bookmarks.ts b/packages/shared/types/bookmarks.ts index f108d006..d72525d1 100644 --- a/packages/shared/types/bookmarks.ts +++ b/packages/shared/types/bookmarks.ts @@ -24,6 +24,7 @@ export const zAssetTypesSchema = z.enum([ "video", "bookmarkAsset", "precrawledArchive", + "userUploaded", "unknown", ]); export type ZAssetType = z.infer<typeof zAssetTypesSchema>; @@ -31,6 +32,7 @@ export type ZAssetType = z.infer<typeof zAssetTypesSchema>; export const zAssetSchema = z.object({ id: z.string(), assetType: zAssetTypesSchema, + fileName: z.string().nullish(), }); export const zBookmarkedLinkSchema = z.object({ diff --git a/packages/trpc/lib/attachments.ts b/packages/trpc/lib/attachments.ts index af484462..7a4e2668 100644 --- a/packages/trpc/lib/attachments.ts +++ b/packages/trpc/lib/attachments.ts @@ -16,6 +16,7 @@ export function mapDBAssetTypeToUserType(assetType: AssetTypes): ZAssetType { [AssetTypes.LINK_VIDEO]: "video", [AssetTypes.LINK_HTML_CONTENT]: "linkHtmlContent", [AssetTypes.BOOKMARK_ASSET]: "bookmarkAsset", + [AssetTypes.USER_UPLOADED]: "userUploaded", [AssetTypes.UNKNOWN]: "bannerImage", }; return map[assetType]; @@ -33,6 +34,7 @@ export function mapSchemaAssetTypeToDB( video: AssetTypes.LINK_VIDEO, bookmarkAsset: AssetTypes.BOOKMARK_ASSET, linkHtmlContent: AssetTypes.LINK_HTML_CONTENT, + userUploaded: AssetTypes.USER_UPLOADED, unknown: AssetTypes.UNKNOWN, }; return map[assetType]; @@ -48,6 +50,7 @@ export function humanFriendlyNameForAssertType(type: ZAssetType) { video: "Video", bookmarkAsset: "Bookmark Asset", linkHtmlContent: "HTML Content", + userUploaded: "User Uploaded File", unknown: "Unknown", }; return map[type]; @@ -63,6 +66,7 @@ export function isAllowedToAttachAsset(type: ZAssetType) { video: true, bookmarkAsset: false, linkHtmlContent: false, + userUploaded: true, unknown: false, }; return map[type]; @@ -78,6 +82,7 @@ export function isAllowedToDetachAsset(type: ZAssetType) { video: true, bookmarkAsset: false, linkHtmlContent: false, + userUploaded: true, unknown: false, }; return map[type]; diff --git a/packages/trpc/models/bookmarks.ts b/packages/trpc/models/bookmarks.ts index e0864ec1..c689f64d 100644 --- a/packages/trpc/models/bookmarks.ts +++ b/packages/trpc/models/bookmarks.ts @@ -295,6 +295,7 @@ export class Bookmark implements PrivacyAware { acc[bookmarkId].assets.push({ id: row.assets.id, assetType: mapDBAssetTypeToUserType(row.assets.assetType), + fileName: row.assets.fileName, }); } diff --git a/packages/trpc/routers/assets.ts b/packages/trpc/routers/assets.ts index af508a4e..7be85446 100644 --- a/packages/trpc/routers/assets.ts +++ b/packages/trpc/routers/assets.ts @@ -99,7 +99,10 @@ export const assetsAppRouter = router({ .input( z.object({ bookmarkId: z.string(), - asset: zAssetSchema, + asset: z.object({ + id: z.string(), + assetType: zAssetTypesSchema, + }), }), ) .output(zAssetSchema) @@ -112,7 +115,7 @@ export const assetsAppRouter = router({ message: "You can't attach this type of asset", }); } - await ctx.db + const [updatedAsset] = await ctx.db .update(assets) .set({ assetType: mapSchemaAssetTypeToDB(input.asset.assetType), @@ -120,8 +123,14 @@ export const assetsAppRouter = router({ }) .where( and(eq(assets.id, input.asset.id), eq(assets.userId, ctx.user.id)), - ); - return input.asset; + ) + .returning(); + + return { + id: updatedAsset.id, + assetType: mapDBAssetTypeToUserType(updatedAsset.assetType), + fileName: updatedAsset.fileName, + }; }), replaceAsset: authedProcedure .input( diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts index 5a2fc1eb..72c6c1d1 100644 --- a/packages/trpc/routers/bookmarks.ts +++ b/packages/trpc/routers/bookmarks.ts @@ -253,6 +253,7 @@ async function toZodSchema( assets: assets.map((a) => ({ id: a.id, assetType: mapDBAssetTypeToUserType(a.assetType), + fileName: a.fileName, })), ...rest, }; |
