Skip to content

Commit

Permalink
feat(bookmarks): empty bookmark search state
Browse files Browse the repository at this point in the history
  • Loading branch information
JaleelB committed Aug 1, 2024
1 parent 2ad7976 commit d89b35e
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 90 deletions.
3 changes: 3 additions & 0 deletions app/article/article-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { getUrlWithoutPaywall } from "./actions/url";
export const getCachedArticle = unstable_cache(
async (url) => scrapeArticleContent(url),
["url"],
{
revalidate: 60 * 60, // 1 hour
},
);

async function ArticleLoader({ url }: { url: string }) {
Expand Down
225 changes: 135 additions & 90 deletions app/bookmarks/components/bookmark-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import {
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Bookmark } from "../bookmark-wrapper";
import { usePathname, useSearchParams } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import * as cheerio from "cheerio";
import { useEffect, useMemo, useState } from "react";
import { BookmarksSearchBox } from "./bookmarks-search-box";
import { BookmarksDisplayMenu } from "./bookmarks-display";
import { BookmarkButton } from "./bookmark-button";
import Image from "next/image";

export type Layout = "grid" | "rows";
export type OrderBy = "date" | "readTime" | "title";
Expand Down Expand Up @@ -56,6 +57,8 @@ export default function BookmarksList({
userId: number;
}) {
const { toast } = useToast();

const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const searchTerm = searchParams.get("search") || "";
Expand Down Expand Up @@ -124,105 +127,147 @@ export default function BookmarksList({

<div
className={cn(
"grid w-full gap-3",
"relative grid h-fit w-full gap-3",
layout === "grid"
? "grid-cols-1 sm:grid-cols-2 xl:grid-cols-3"
: "grid-cols-1 gap-2",

sortedAndFilteredBookmarks.length === 0 &&
"h-full items-center justify-center",
)}
>
{sortedAndFilteredBookmarks.map((bookmark) => (
<Card
key={bookmark.id}
className={cn(
`relative flex w-full transform-gpu cursor-pointer flex-col gap-2 rounded-xl border bg-white p-2 shadow-sm transition-all duration-200 ease-in-out [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)] hover:scale-[103%] dark:bg-transparent dark:backdrop-blur-md dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]`,
)}
>
<CardContent className="p-2">
<div className="flex w-full justify-between">
<div className="flex w-full gap-3">
<Avatar className="h-11 w-11 rounded-md">
<AvatarImage
src={
bookmark.authorImageURL ||
"https://illustrations.popsy.co/white/genius.svg"
}
/>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<Link
href={(bookmark.authorProfileURL as string) || "#"}
className="font-medium"
>
{bookmark.authorName}
</Link>
<div className="text-sm text-muted-foreground">
{bookmark.readTime}
{bookmark.publishDate && <span className="px-2">·</span>}
{bookmark.publishDate && formatDate(bookmark.publishDate)}
{sortedAndFilteredBookmarks.length > 0 ? (
sortedAndFilteredBookmarks.map((bookmark) => (
<Card
key={bookmark.id}
className={cn(
`relative flex w-full transform-gpu cursor-pointer flex-col gap-2 rounded-xl border bg-white p-2 shadow-sm transition-all duration-200 ease-in-out [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)] hover:scale-[103%] dark:bg-transparent dark:backdrop-blur-md dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]`,
)}
>
<CardContent className="p-2">
<div className="flex w-full justify-between">
<div className="flex w-full gap-3">
<Avatar className="h-11 w-11 rounded-md">
<AvatarImage
src={
bookmark.authorImageURL ||
"https://illustrations.popsy.co/white/genius.svg"
}
/>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<Link
href={(bookmark.authorProfileURL as string) || "#"}
className="font-medium"
>
{bookmark.authorName}
</Link>
<div className="text-sm text-muted-foreground">
{bookmark.readTime}
{bookmark.publishDate && (
<span className="px-2">·</span>
)}
{bookmark.publishDate &&
formatDate(bookmark.publishDate)}
</div>
</div>
</div>
</div>
<AlertDialog>
<AlertDialogTrigger>
<Button
variant="outline"
size="icon"
className="h-10 w-10 rounded-full"
>
<Icons.bookmark className={`h-5 w-5 fill-current`} />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you absolutely sure?
</AlertDialogTitle>
<AlertDialogDescription>
This action will remove the bookmark from your list.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
const [_, error] = await deleteBookmarkAction({
path: pathname,
id: bookmark.id,
userId: userId,
});

if (error) {
<AlertDialog>
<AlertDialogTrigger>
<Button
variant="outline"
size="icon"
className="h-10 w-10 rounded-full"
>
<Icons.bookmark className={`h-5 w-5 fill-current`} />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you absolutely sure?
</AlertDialogTitle>
<AlertDialogDescription>
This action will remove the bookmark from your list.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
const [_, error] = await deleteBookmarkAction({
path: pathname,
id: bookmark.id,
userId: userId,
});

if (error) {
toast({
title: "Error",
description: "Failed to delete bookmark",
variant: "destructive",
});
return;
}

toast({
title: "Error",
description: "Failed to delete bookmark",
variant: "destructive",
title: "Bookmark deleted",
description:
"Bookmark has been successfully deleted",
});
return;
}

toast({
title: "Bookmark deleted",
description:
"Bookmark has been successfully deleted",
});
}}
>
Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
<Balancer as="h3" className="pt-4 text-xl font-medium">
{bookmark.title}
</Balancer>
<CardDescription className={cn("pt-2")}>
{extractFirstSentence(bookmark.content)}
</CardDescription>
</CardContent>
}}
>
Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
<Balancer as="h3" className="pt-4 text-xl font-medium">
{bookmark.title}
</Balancer>
<CardDescription className={cn("pt-2")}>
{extractFirstSentence(bookmark.content)}
</CardDescription>
</CardContent>
</Card>
))
) : (
<Card
className={cn(
"absolute inset-0 m-auto h-fit w-full max-w-2xl space-y-4 border-2 bg-accent p-8",
)}
>
<Image
src="https://illustrations.popsy.co/white/abstract-art-4.svg"
alt="Error"
className="mx-auto"
width={300}
height={200}
/>
<div className="flex flex-col items-center space-y-2">
<h3 className="font-heading text-xl">Bookmark not found!</h3>
<p className="max-w-md pb-2 text-center text-base text-muted-foreground">
Bummer! The bookmark you are looking for does not exist. You
either typed in the wrong article name or you didn&apos;t
bookmark the article.
</p>
<Button
className="w-full max-w-[180px] px-10 font-bold"
onClick={() => {
const params = new URLSearchParams(searchParams);
params.delete("search");
router.push(`${pathname}?${params.toString()}`, {
scroll: false,
});
}}
>
Back to my bookmarks
</Button>
</div>
</Card>
))}
)}
</div>
</div>
);
Expand Down

0 comments on commit d89b35e

Please sign in to comment.