From 2ac3c39a9c80305bb959d88561e78f65a1cd1be1 Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Thu, 22 Feb 2024 17:33:12 +0000 Subject: feature: Adding some loading card while the link is getting crawled --- .../app/dashboard/bookmarks/components/AddLink.tsx | 16 +--- .../bookmarks/components/BookmarksGrid.tsx | 7 -- .../dashboard/bookmarks/components/LinkCard.tsx | 85 +++++++++++++++++----- .../web/app/signin/components/CredentialsForm.tsx | 4 +- packages/web/lib/hooks/use-loading-card.ts | 9 --- packages/web/lib/types/api/bookmarks.ts | 3 +- packages/web/server/api/routers/bookmarks.ts | 25 +++++++ 7 files changed, 100 insertions(+), 49 deletions(-) delete mode 100644 packages/web/lib/hooks/use-loading-card.ts (limited to 'packages/web') diff --git a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx index 7663543f..242a52a5 100644 --- a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx +++ b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx @@ -9,32 +9,24 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { toast } from "@/components/ui/use-toast"; import { api } from "@/lib/trpc"; import { ActionButton } from "@/components/ui/action-button"; -import { useLoadingCard } from "@/lib/hooks/use-loading-card"; const formSchema = z.object({ url: z.string().url({ message: "The link must be a valid URL" }), }); export default function AddLink() { - const { setLoading } = useLoadingCard(); + const form = useForm>({ + resolver: zodResolver(formSchema), + }); + const invalidateBookmarksCache = api.useUtils().bookmarks.invalidate; const bookmarkLinkMutator = api.bookmarks.bookmarkLink.useMutation({ - onMutate: () => { - setLoading(true); - }, onSuccess: () => { invalidateBookmarksCache(); }, onError: () => { toast({ description: "Something went wrong", variant: "destructive" }); }, - onSettled: () => { - setLoading(false); - }, - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), }); const onError: SubmitErrorHandler> = (errors) => { diff --git a/packages/web/app/dashboard/bookmarks/components/BookmarksGrid.tsx b/packages/web/app/dashboard/bookmarks/components/BookmarksGrid.tsx index c1c8f3e0..dc98472e 100644 --- a/packages/web/app/dashboard/bookmarks/components/BookmarksGrid.tsx +++ b/packages/web/app/dashboard/bookmarks/components/BookmarksGrid.tsx @@ -1,7 +1,5 @@ "use client"; -import { useLoadingCard } from "@/lib/hooks/use-loading-card"; -import BookmarkCardSkeleton from "./BookmarkCardSkeleton"; import LinkCard from "./LinkCard"; import { ZBookmark, ZGetBookmarksRequest } from "@/lib/types/api/bookmarks"; import { api } from "@/lib/trpc"; @@ -13,8 +11,6 @@ function renderBookmark(bookmark: ZBookmark) { } } -export const dynamic = "force-dynamic"; - export default function BookmarksGrid({ query, bookmarks: initialBookmarks, @@ -24,12 +20,9 @@ export default function BookmarksGrid({ }) { const { data } = api.bookmarks.getBookmarks.useQuery(query, { initialData: { bookmarks: initialBookmarks }, - staleTime: Infinity, }); - const { loading } = useLoadingCard(); return (
- {loading && } {data.bookmarks.map((b) => renderBookmark(b))}
); diff --git a/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx b/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx index 7413c2fe..35696134 100644 --- a/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx +++ b/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx @@ -1,3 +1,5 @@ +"use client"; + import { Badge } from "@/components/ui/badge"; import { ImageCard, @@ -10,9 +12,71 @@ import { import { ZBookmark } from "@/lib/types/api/bookmarks"; import Link from "next/link"; import BookmarkOptions from "./BookmarkOptions"; +import { api } from "@/lib/trpc"; +import { Skeleton } from "@/components/ui/skeleton"; + +function isStillCrawling(bookmark: ZBookmark) { + return ( + !bookmark.content.crawledAt && Date.now() - bookmark.createdAt < 30 * 1000 + ); +} -export default function LinkCard({ bookmark }: { bookmark: ZBookmark }) { +function TagList(bookmark: ZBookmark, stillCrawling: boolean) { + if (stillCrawling) { + return ( + + + + + + ); + } + return ( + + {bookmark.tags.map((t) => ( + + + #{t.name} + + + ))} + + ); +} + +export default function LinkCard({ + bookmark: initialData, +}: { + bookmark: ZBookmark; +}) { + const { data: bookmark } = api.bookmarks.getBookmark.useQuery( + { + id: initialData.id, + }, + { + initialData, + refetchInterval: (query) => { + const data = query.state.data; + if (!data) { + return false; + } + // If the link is not crawled and + if (isStillCrawling(data)) { + return 1000; + } + return false; + }, + }, + ); const link = bookmark.content; + const isCrawling = isStillCrawling(bookmark); const parsedUrl = new URL(link.url); // A dummy white pixel for when there's no image. @@ -28,7 +92,7 @@ export default function LinkCard({ bookmark }: { bookmark: ZBookmark }) { } > - + @@ -38,22 +102,7 @@ export default function LinkCard({ bookmark }: { bookmark: ZBookmark }) { {/* There's a hack here. Every tag has the full hight of the container itself. That why, when we enable flex-wrap, the overflowed don't show up. */} - - {bookmark.tags.map((t) => ( - - - #{t.name} - - - ))} - + {TagList(bookmark, isCrawling)}
diff --git a/packages/web/app/signin/components/CredentialsForm.tsx b/packages/web/app/signin/components/CredentialsForm.tsx index 60b61156..f47708f6 100644 --- a/packages/web/app/signin/components/CredentialsForm.tsx +++ b/packages/web/app/signin/components/CredentialsForm.tsx @@ -84,7 +84,7 @@ function SignIn() { ); }} /> - + Sign In
@@ -195,7 +195,7 @@ function SignUp() { ); }} /> - + Sign Up
diff --git a/packages/web/lib/hooks/use-loading-card.ts b/packages/web/lib/hooks/use-loading-card.ts deleted file mode 100644 index bb32a8f1..00000000 --- a/packages/web/lib/hooks/use-loading-card.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { create } from "zustand"; - -export const useLoadingCard = create<{ - loading: boolean; - setLoading: (val: boolean) => void; -}>((set) => ({ - loading: false, - setLoading: (val: boolean) => set({ loading: val }), -})); diff --git a/packages/web/lib/types/api/bookmarks.ts b/packages/web/lib/types/api/bookmarks.ts index 772ef153..94f89e55 100644 --- a/packages/web/lib/types/api/bookmarks.ts +++ b/packages/web/lib/types/api/bookmarks.ts @@ -8,6 +8,7 @@ export const zBookmarkedLinkSchema = z.object({ description: z.string().nullish(), imageUrl: z.string().url().nullish(), favicon: z.string().url().nullish(), + crawledAt: z.date().nullish(), }); export type ZBookmarkedLink = z.infer; @@ -18,7 +19,7 @@ export type ZBookmarkContent = z.infer; export const zBookmarkSchema = z.object({ id: z.string(), - createdAt: z.coerce.date(), + createdAt: z.date(), archived: z.boolean(), favourited: z.boolean(), tags: z.array(zBookmarkTagSchema), diff --git a/packages/web/server/api/routers/bookmarks.ts b/packages/web/server/api/routers/bookmarks.ts index 37d12eb9..65f20ef5 100644 --- a/packages/web/server/api/routers/bookmarks.ts +++ b/packages/web/server/api/routers/bookmarks.ts @@ -26,6 +26,7 @@ const defaultBookmarkFields = { description: true, imageUrl: true, favicon: true, + crawledAt: true, }, }, tags: { @@ -153,6 +154,30 @@ export const bookmarksAppRouter = router({ bookmarkId: input.bookmarkId, }); }), + getBookmark: authedProcedure + .input( + z.object({ + id: z.string(), + }), + ) + .output(zBookmarkSchema) + .query(async ({ input, ctx }) => { + const bookmark = await prisma.bookmark.findUnique({ + where: { + userId: ctx.user.id, + id: input.id, + }, + select: defaultBookmarkFields, + }); + if (!bookmark) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Bookmark not found", + }); + } + + return toZodSchema(bookmark); + }), getBookmarks: authedProcedure .input(zGetBookmarksRequestSchema) .output(zGetBookmarksResponseSchema) -- cgit v1.2.3-70-g09d2