diff options
| author | MohamedBassem <me@mbassem.com> | 2024-02-09 16:47:17 +0000 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-02-09 16:51:24 +0000 |
| commit | 7400914396eea0c9a1fb7bc59e022babc2186f42 (patch) | |
| tree | 36e4a3d05b43d15254d75fddebc0594747b70a91 /packages/web/app | |
| parent | 5c0fd0143e5ded1d8e957ec5e59e21d7c629d036 (diff) | |
| download | karakeep-7400914396eea0c9a1fb7bc59e022babc2186f42.tar.zst | |
[feature] Add the ability to favourite and archive bookmarks
Diffstat (limited to 'packages/web/app')
4 files changed, 126 insertions, 56 deletions
diff --git a/packages/web/app/api/v1/bookmarks/[bookmarkId]/route.ts b/packages/web/app/api/v1/bookmarks/[bookmarkId]/route.ts index 6adcf771..0963cf94 100644 --- a/packages/web/app/api/v1/bookmarks/[bookmarkId]/route.ts +++ b/packages/web/app/api/v1/bookmarks/[bookmarkId]/route.ts @@ -1,9 +1,44 @@ import { authOptions } from "@/lib/auth"; -import { deleteBookmark } from "@/lib/services/bookmarks"; +import { deleteBookmark, updateBookmark } from "@/lib/services/bookmarks"; +import { ZBookmark, zUpdateBookmarksRequestSchema } from "@/lib/types/api/bookmarks"; import { Prisma } from "@remember/db"; import { getServerSession } from "next-auth"; -import { NextRequest } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; + +export async function PATCH( + request: NextRequest, + { params }: { params: { bookmarkId: string } }, +) { + const session = await getServerSession(authOptions); + if (!session) { + return new Response(null, { status: 401 }); + } + + const updateJson = await request.json(); + const update = zUpdateBookmarksRequestSchema.safeParse(updateJson); + if (!update.success) { + return new Response(null, { status: 400 }); + } + + try { + const bookmark: ZBookmark = await updateBookmark( + params.bookmarkId, + session.user.id, + update.data, + ); + return NextResponse.json(bookmark); + } catch (e: unknown) { + if ( + e instanceof Prisma.PrismaClientKnownRequestError && + e.code === "P2025" // RecordNotFound + ) { + return new Response(null, { status: 404 }); + } else { + throw e; + } + } +} export async function DELETE( _request: NextRequest, @@ -28,5 +63,5 @@ export async function DELETE( } } - return new Response(null, { status: 201 }); + return new Response(null, { status: 204 }); } diff --git a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx new file mode 100644 index 00000000..15ce64c7 --- /dev/null +++ b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { useToast } from "@/components/ui/use-toast"; +import APIClient from "@/lib/api"; +import { ZBookmark, ZUpdateBookmarksRequest } from "@/lib/types/api/bookmarks"; +import { useRouter } from "next/navigation"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Archive, MoreHorizontal, Star, Trash2 } from "lucide-react"; + +export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { + const { toast } = useToast(); + const router = useRouter(); + const linkId = bookmark.id; + + const unbookmarkLink = async () => { + const [_, error] = await APIClient.deleteBookmark(linkId); + + if (error) { + toast({ + variant: "destructive", + title: "Something went wrong", + description: "There was a problem with your request.", + }); + } else { + toast({ + description: "The bookmark has been deleted!", + }); + } + + router.refresh(); + }; + + const updateBookmark = async (req: ZUpdateBookmarksRequest) => { + const [_, error] = await APIClient.updateBookmark(linkId, req); + + if (error) { + toast({ + variant: "destructive", + title: "Something went wrong", + description: "There was a problem with your request.", + }); + } else { + toast({ + description: "The bookmark has been updated!", + }); + } + + router.refresh(); + }; + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="ghost"> + <MoreHorizontal /> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent className="w-fit"> + <DropdownMenuItem + onClick={() => updateBookmark({ favourited: !bookmark.favourited })} + > + <Star className="mr-2 size-4" /> + <span>{bookmark.favourited ? "Un-favourite" : "Favourite"}</span> + </DropdownMenuItem> + <DropdownMenuItem + onClick={() => updateBookmark({ archived: !bookmark.archived })} + > + <Archive className="mr-2 size-4" /> + <span>{bookmark.archived ? "Un-archive" : "Archive"}</span> + </DropdownMenuItem> + <DropdownMenuItem className="text-destructive" onClick={unbookmarkLink}> + <Trash2 className="mr-2 size-4" /> + <span>Delete</span> + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ); +} diff --git a/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx b/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx index aeef6bae..039cb156 100644 --- a/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx +++ b/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx @@ -1,63 +1,14 @@ -"use client"; - import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; import { ImageCard, ImageCardBody, ImageCardFooter, ImageCardTitle, } from "@/components/ui/imageCard"; -import { useToast } from "@/components/ui/use-toast"; -import APIClient from "@/lib/api"; import { ZBookmark } from "@/lib/types/api/bookmarks"; -import { MoreHorizontal, Trash2 } from "lucide-react"; import Link from "next/link"; -import { useRouter } from "next/navigation"; - -export function LinkOptions({ linkId }: { linkId: string }) { - const { toast } = useToast(); - const router = useRouter(); - - const unbookmarkLink = async () => { - const [_, error] = await APIClient.deleteBookmark(linkId); +import BookmarkOptions from "./BookmarkOptions"; - if (error) { - toast({ - variant: "destructive", - title: "Something went wrong", - description: "There was a problem with your request.", - }); - } else { - toast({ - description: "The link has been deleted!", - }); - } - - router.refresh(); - }; - return ( - <DropdownMenu> - <DropdownMenuTrigger asChild> - <Button variant="ghost"> - <MoreHorizontal /> - </Button> - </DropdownMenuTrigger> - <DropdownMenuContent className="w-fit"> - <DropdownMenuItem className="text-destructive" onClick={unbookmarkLink}> - <Trash2 className="mr-2 size-4" /> - <span>Delete</span> - </DropdownMenuItem> - </DropdownMenuContent> - </DropdownMenu> - ); -} export default function LinkCard({ bookmark }: { bookmark: ZBookmark }) { const link = bookmark.content; @@ -71,7 +22,7 @@ export default function LinkCard({ bookmark }: { bookmark: ZBookmark }) { image={link?.imageUrl ?? undefined} > <ImageCardTitle> - <Link className="line-clamp-3" href={link.url}> + <Link className="line-clamp-2" href={link.url}> {link?.title ?? parsedUrl.host} </Link> </ImageCardTitle> @@ -93,7 +44,7 @@ export default function LinkCard({ bookmark }: { bookmark: ZBookmark }) { {parsedUrl.host} </Link> </div> - <LinkOptions linkId={bookmark.id} /> + <BookmarkOptions bookmark={bookmark} /> </div> </ImageCardFooter> </ImageCard> diff --git a/packages/web/app/dashboard/bookmarks/page.tsx b/packages/web/app/dashboard/bookmarks/page.tsx index 3d125fad..517dc184 100644 --- a/packages/web/app/dashboard/bookmarks/page.tsx +++ b/packages/web/app/dashboard/bookmarks/page.tsx @@ -1,5 +1,5 @@ import Bookmarks from "./components/Bookmarks"; export default async function BookmarksPage() { - return <Bookmarks title="Bookmarks" archived={false} favourited={false} />; + return <Bookmarks title="Bookmarks" archived={false} />; } |
