From 79d61be7e15dc5d23fb687a5f71e0097088a99ac Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Sun, 7 Apr 2024 18:30:00 +0100 Subject: feature: Extract hook logic into separate package and add a new action bar in bookmark preview --- apps/browser-extension/package.json | 1 + apps/browser-extension/src/BookmarkSavedPage.tsx | 20 +- apps/mobile/components/bookmarks/BookmarkCard.tsx | 25 +-- apps/mobile/package.json | 1 + .../app/dashboard/preview/[bookmarkId]/page.tsx | 2 +- .../dashboard/bookmarks/BookmarkActionBar.tsx | 11 +- .../dashboard/bookmarks/BookmarkOptions.tsx | 52 ++--- .../dashboard/bookmarks/BookmarkPreview.tsx | 216 --------------------- .../components/dashboard/bookmarks/NoteEditor.tsx | 48 ----- apps/web/components/dashboard/bookmarks/icons.tsx | 29 +++ .../web/components/dashboard/preview/ActionBar.tsx | 115 +++++++++++ .../dashboard/preview/AssetContentSection.tsx | 31 +++ .../dashboard/preview/BookmarkPreview.tsx | 154 +++++++++++++++ .../components/dashboard/preview/NoteEditor.tsx | 42 ++++ .../dashboard/preview/TextContentSection.tsx | 39 ++++ apps/web/components/ui/button.tsx | 2 + apps/web/package.json | 1 + 17 files changed, 459 insertions(+), 330 deletions(-) delete mode 100644 apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx delete mode 100644 apps/web/components/dashboard/bookmarks/NoteEditor.tsx create mode 100644 apps/web/components/dashboard/bookmarks/icons.tsx create mode 100644 apps/web/components/dashboard/preview/ActionBar.tsx create mode 100644 apps/web/components/dashboard/preview/AssetContentSection.tsx create mode 100644 apps/web/components/dashboard/preview/BookmarkPreview.tsx create mode 100644 apps/web/components/dashboard/preview/NoteEditor.tsx create mode 100644 apps/web/components/dashboard/preview/TextContentSection.tsx (limited to 'apps') diff --git a/apps/browser-extension/package.json b/apps/browser-extension/package.json index 35c899bc..20cc843c 100644 --- a/apps/browser-extension/package.json +++ b/apps/browser-extension/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@hoarder/trpc": "workspace:^0.1.0", + "@hoarder/shared-react": "workspace:^0.1.0", "@tanstack/react-query": "^5.24.8", "@trpc/client": "11.0.0-next-beta.308", "@trpc/next": "11.0.0-next-beta.308", diff --git a/apps/browser-extension/src/BookmarkSavedPage.tsx b/apps/browser-extension/src/BookmarkSavedPage.tsx index 54f62796..3535ade8 100644 --- a/apps/browser-extension/src/BookmarkSavedPage.tsx +++ b/apps/browser-extension/src/BookmarkSavedPage.tsx @@ -2,24 +2,24 @@ import { useState } from "react"; import { ArrowUpRightFromSquare, Trash } from "lucide-react"; import { Link, useNavigate, useParams } from "react-router-dom"; +import { useDeleteBookmark } from "@hoarder/shared-react/hooks/bookmarks"; + import Spinner from "./Spinner"; import usePluginSettings from "./utils/settings"; -import { api } from "./utils/trpc"; export default function BookmarkSavedPage() { const { bookmarkId } = useParams(); const navigate = useNavigate(); const [error, setError] = useState(""); - const { mutate: deleteBookmark, isPending } = - api.bookmarks.deleteBookmark.useMutation({ - onSuccess: () => { - navigate("/bookmarkdeleted"); - }, - onError: (e) => { - setError(e.message); - }, - }); + const { mutate: deleteBookmark, isPending } = useDeleteBookmark({ + onSuccess: () => { + navigate("/bookmarkdeleted"); + }, + onError: (e) => { + setError(e.message); + }, + }); const { settings } = usePluginSettings(); diff --git a/apps/mobile/components/bookmarks/BookmarkCard.tsx b/apps/mobile/components/bookmarks/BookmarkCard.tsx index 89ce7924..d4fbcb58 100644 --- a/apps/mobile/components/bookmarks/BookmarkCard.tsx +++ b/apps/mobile/components/bookmarks/BookmarkCard.tsx @@ -17,6 +17,10 @@ import { MenuView } from "@react-native-menu/menu"; import { Ellipsis, Star } from "lucide-react-native"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; +import { + useDeleteBookmark, + useUpdateBookmark, +} from "@hoarder/shared-react/hooks/bookmarks"; import { Divider } from "../ui/Divider"; import { Skeleton } from "../ui/Skeleton"; @@ -45,7 +49,6 @@ export function isBookmarkStillLoading(bookmark: ZBookmark) { function ActionBar({ bookmark }: { bookmark: ZBookmark }) { const { toast } = useToast(); - const apiUtils = api.useUtils(); const onError = () => { toast({ @@ -56,37 +59,27 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) { }; const { mutate: deleteBookmark, isPending: isDeletionPending } = - api.bookmarks.deleteBookmark.useMutation({ + useDeleteBookmark({ onSuccess: () => { toast({ message: "The bookmark has been deleted!", showProgress: false, }); - apiUtils.bookmarks.getBookmarks.invalidate(); - apiUtils.bookmarks.searchBookmarks.invalidate(); }, onError, }); - const { mutate: favouriteBookmark, variables } = - api.bookmarks.updateBookmark.useMutation({ - onSuccess: () => { - apiUtils.bookmarks.getBookmarks.invalidate(); - apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id }); - }, - onError, - }); + const { mutate: favouriteBookmark, variables } = useUpdateBookmark({ + onError, + }); const { mutate: archiveBookmark, isPending: isArchivePending } = - api.bookmarks.updateBookmark.useMutation({ + useUpdateBookmark({ onSuccess: (resp) => { toast({ message: `The bookmark has been ${resp.archived ? "archived" : "un-archived"}!`, showProgress: false, }); - apiUtils.bookmarks.getBookmarks.invalidate(); - apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id }); - apiUtils.bookmarks.searchBookmarks.invalidate(); }, onError, }); diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 6ccd5622..9f170040 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@hoarder/trpc": "workspace:^0.1.0", + "@hoarder/shared-react": "workspace:^0.1.0", "@react-native-menu/menu": "^0.9.1", "@tanstack/react-query": "^5.24.8", "class-variance-authority": "^0.7.0", diff --git a/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx b/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx index 201cbcaf..6cac7377 100644 --- a/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx +++ b/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx @@ -1,4 +1,4 @@ -import BookmarkPreview from "@/components/dashboard/bookmarks/BookmarkPreview"; +import BookmarkPreview from "@/components/dashboard/preview/BookmarkPreview"; import { api } from "@/server/api/client"; export default async function BookmarkPreviewPage({ diff --git a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx index d2beb8d4..420fed84 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx @@ -1,11 +1,12 @@ +import BookmarkPreview from "@/components/dashboard/preview/BookmarkPreview"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { Maximize2, Star } from "lucide-react"; +import { Maximize2 } from "lucide-react"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; import BookmarkOptions from "./BookmarkOptions"; -import BookmarkPreview from "./BookmarkPreview"; +import { FavouritedActionIcon } from "./icons"; export default function BookmarkActionBar({ bookmark, @@ -15,11 +16,7 @@ export default function BookmarkActionBar({ return (
{bookmark.favourited && ( - + )} diff --git a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx index e95ec9a2..e3cfc796 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx @@ -11,24 +11,28 @@ import { import { useToast } from "@/components/ui/use-toast"; import { useClientConfig } from "@/lib/clientConfig"; import { BookmarkListContext } from "@/lib/hooks/list-context"; -import { api } from "@/lib/trpc"; import { - Archive, Link, List, ListX, MoreHorizontal, Pencil, RotateCw, - Star, Tags, Trash2, } from "lucide-react"; import type { ZBookmark, ZBookmarkedLink } from "@hoarder/trpc/types/bookmarks"; +import { + useDeleteBookmark, + useRecrawlBookmark, + useUpdateBookmark, +} from "@hoarder/shared-react/hooks//bookmarks"; +import { useRemoveBookmarkFromList } from "@hoarder/shared-react/hooks//lists"; import { useAddToListModal } from "./AddToListModal"; import { BookmarkedTextEditor } from "./BookmarkedTextEditor"; +import { ArchivedActionIcon, FavouritedActionIcon } from "./icons"; import { useTagModel } from "./TagModal"; export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { @@ -46,15 +50,6 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { const { listId } = useContext(BookmarkListContext); - const invalidateAllBookmarksCache = - api.useUtils().bookmarks.getBookmarks.invalidate; - - const invalidateBookmarkCache = - api.useUtils().bookmarks.getBookmark.invalidate; - - const invalidateSearchCache = - api.useUtils().bookmarks.searchBookmarks.invalidate; - const onError = () => { toast({ variant: "destructive", @@ -62,48 +57,35 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { description: "There was a problem with your request.", }); }; - const deleteBookmarkMutator = api.bookmarks.deleteBookmark.useMutation({ + const deleteBookmarkMutator = useDeleteBookmark({ onSuccess: () => { toast({ description: "The bookmark has been deleted!", }); }, onError, - onSettled: () => { - invalidateAllBookmarksCache(); - invalidateSearchCache(); - }, }); - const updateBookmarkMutator = api.bookmarks.updateBookmark.useMutation({ + const updateBookmarkMutator = useUpdateBookmark({ onSuccess: () => { toast({ description: "The bookmark has been updated!", }); }, onError, - onSettled: () => { - invalidateBookmarkCache({ bookmarkId: bookmark.id }); - invalidateAllBookmarksCache(); - invalidateSearchCache(); - }, }); - const crawlBookmarkMutator = api.bookmarks.recrawlBookmark.useMutation({ + const crawlBookmarkMutator = useRecrawlBookmark({ onSuccess: () => { toast({ description: "Re-fetch has been enqueued!", }); }, onError, - onSettled: () => { - invalidateBookmarkCache({ bookmarkId: bookmark.id }); - }, }); - const removeFromListMutator = api.lists.removeFromList.useMutation({ - onSuccess: (_resp, req) => { - invalidateAllBookmarksCache({ listId: req.listId }); + const removeFromListMutator = useRemoveBookmarkFromList({ + onSuccess: () => { toast({ description: "The bookmark has been deleted from the list", }); @@ -145,7 +127,10 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { }) } > - + {bookmark.favourited ? "Un-favourite" : "Favourite"} - + {bookmark.archived ? "Un-archive" : "Archive"} {bookmark.content.type === "link" && ( diff --git a/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx b/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx deleted file mode 100644 index 102e0788..00000000 --- a/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx +++ /dev/null @@ -1,216 +0,0 @@ -"use client"; - -import Image from "next/image"; -import Link from "next/link"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Separator } from "@/components/ui/separator"; -import { Skeleton } from "@/components/ui/skeleton"; -import { - Tooltip, - TooltipContent, - TooltipPortal, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { - isBookmarkStillCrawling, - isBookmarkStillLoading, -} from "@/lib/bookmarkUtils"; -import { api } from "@/lib/trpc"; -import dayjs from "dayjs"; -import relativeTime from "dayjs/plugin/relativeTime"; -import { CalendarDays, ExternalLink } from "lucide-react"; -import Markdown from "react-markdown"; - -import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; - -import { NoteEditor } from "./NoteEditor"; -import { TagsEditor } from "./TagsEditor"; - -dayjs.extend(relativeTime); - -function ContentLoading() { - return ( -
- - - -
- ); -} - -function CreationTime({ createdAt }: { createdAt: Date }) { - return ( - - - - - {dayjs(createdAt).fromNow()} - - - - {createdAt.toLocaleString()} - - - - ); -} - -function LinkHeader({ bookmark }: { bookmark: ZBookmark }) { - if (bookmark.content.type !== "link") { - throw new Error("Unexpected content type"); - } - - const title = bookmark.content.title ?? bookmark.content.url; - - return ( -
- - - -

{title}

-
- - - {title} - - -
-
- - View Original - - - -
- ); -} - -function TextContentSection({ bookmark }: { bookmark: ZBookmark }) { - let content; - switch (bookmark.content.type) { - case "link": { - if (!bookmark.content.htmlContent) { - content = ( -
- Failed to fetch link content ... -
- ); - } else { - content = ( -
- ); - } - break; - } - case "text": { - content = ( - - {bookmark.content.text} - - ); - break; - } - } - - return {content}; -} - -function AssetContentSection({ bookmark }: { bookmark: ZBookmark }) { - if (bookmark.content.type != "asset") { - throw new Error("Invalid content type"); - } - - let content; - switch (bookmark.content.assetType) { - case "image": { - switch (bookmark.content.assetType) { - case "image": { - content = ( -
- asset -
- ); - } - } - break; - } - } - return content; -} - -export default function BookmarkPreview({ - initialData, -}: { - initialData: ZBookmark; -}) { - const { data: bookmark } = api.bookmarks.getBookmark.useQuery( - { - bookmarkId: initialData.id, - }, - { - initialData, - refetchInterval: (query) => { - const data = query.state.data; - if (!data) { - return false; - } - // If the link is not crawled or not tagged - if (isBookmarkStillLoading(data)) { - return 1000; - } - return false; - }, - }, - ); - - let content; - switch (bookmark.content.type) { - case "link": - case "text": { - content = ; - break; - } - case "asset": { - content = ; - break; - } - } - - const linkHeader = bookmark.content.type == "link" && ( - - ); - - return ( -
-
- {isBookmarkStillCrawling(bookmark) ? : content} -
-
- {linkHeader} - -
-

Tags

- -
-
-

Note

- -
-
-
- ); -} diff --git a/apps/web/components/dashboard/bookmarks/NoteEditor.tsx b/apps/web/components/dashboard/bookmarks/NoteEditor.tsx deleted file mode 100644 index d712d523..00000000 --- a/apps/web/components/dashboard/bookmarks/NoteEditor.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Textarea } from "@/components/ui/textarea"; -import { toast } from "@/components/ui/use-toast"; -import { useClientConfig } from "@/lib/clientConfig"; -import { api } from "@/lib/trpc"; - -import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; - -export function NoteEditor({ bookmark }: { bookmark: ZBookmark }) { - const demoMode = !!useClientConfig().demoMode; - - const invalidateBookmarkCache = - api.useUtils().bookmarks.getBookmark.invalidate; - - const updateBookmarkMutator = api.bookmarks.updateBookmark.useMutation({ - onSuccess: () => { - toast({ - description: "The bookmark has been updated!", - }); - }, - onError: () => { - toast({ - description: "Something went wrong while saving the note", - variant: "destructive", - }); - }, - onSettled: () => { - invalidateBookmarkCache({ bookmarkId: bookmark.id }); - }, - }); - - return ( -