Skip to content

Commit

Permalink
add bookmarks, disable certain pages on prod
Browse files Browse the repository at this point in the history
  • Loading branch information
JorrinKievit committed Aug 25, 2024
1 parent 7b9ba23 commit ee86e5a
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 16 deletions.
9 changes: 7 additions & 2 deletions src/components/books/book-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AspectRatio } from "../ui/aspect-ratio";
import { Skeleton } from "../ui/skeleton";
import { BookOpen, DownloadIcon } from "lucide-react";
import { EpubReader } from "./epub-reader";
import { BookmarkButton } from "./bookmark";

type BookItemProps = BookItemResponse;

Expand All @@ -18,7 +19,11 @@ export function BookItem(props: BookItemProps) {

return (
<Card className="shadow-md transition-shadow duration-300 hover:shadow-lg">
<CardContent className="p-4 md:p-6">
<CardContent className="relative p-4 md:p-6">
<div className="absolute right-4 top-4">
<BookmarkButton book={props} />
</div>

<div className="flex flex-col gap-4 md:flex-row md:gap-6">
<div className="mx-2 w-full max-w-[200px] md:w-1/4">
<AspectRatio ratio={5 / 8} className="flex items-center">
Expand All @@ -35,7 +40,7 @@ export function BookItem(props: BookItemProps) {
</div>
<div className="flex flex-1 flex-col justify-between">
<div>
<h2 className="mb-2 text-2xl font-bold">{props.title}</h2>
<h2 className="mb-2 max-w-[90%] text-2xl font-bold">{props.title}</h2>
<p className="text-md dark:text-gray-400">By {props.authors}</p>
<p className="mt-2 text-sm dark:text-gray-400">{props.book_content}</p>
<p className="mt-2 text-sm dark:text-gray-400">Language: {props.book_lang}</p>
Expand Down
27 changes: 27 additions & 0 deletions src/components/books/bookmark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BookItemResponse } from "@/api/backend/types";
import { useBookmarksStore } from "@/stores/bookmarks";
import { Button } from "../ui/button";
import { useMemo } from "react";
import { BookmarkMinus, BookmarkPlus } from "lucide-react";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";

export function BookmarkButton({ book }: { book: BookItemResponse }) {
const bookmarks = useBookmarksStore((state) => state.bookmarks);
const addBookmark = useBookmarksStore((state) => state.addBookmark);
const removeBookmark = useBookmarksStore((state) => state.removeBookmark);

const bookMarkedBook = useMemo(() => bookmarks.find((b) => b.md5 === book.md5), [bookmarks, book.md5]);

return (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Button size="icon" onClick={() => (bookMarkedBook ? removeBookmark(bookMarkedBook.md5) : addBookmark(book))} className="flex items-center gap-2">
{bookMarkedBook ? <BookmarkMinus className="h-5 w-5" /> : <BookmarkPlus className="h-5 w-5" />}
</Button>
</TooltipTrigger>
<TooltipContent>{bookMarkedBook ? "Remove bookmark" : "Add bookmark"}</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
28 changes: 20 additions & 8 deletions src/components/layout/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,34 @@ export function Menu({ isOpen }: MenuProps) {
</Tooltip>
</TooltipProvider>
) : null}
{menus.map(({ href, label, icon: Icon, active, submenus }, index) =>
{menus.map(({ href, label, icon: Icon, active, submenus, disabled }, index) =>
submenus.length === 0 ? (
<div className="w-full" key={index}>
<TooltipProvider disableHoverableContent>
<TooltipProvider>
<Tooltip delayDuration={100}>
<TooltipTrigger asChild>
<Button variant={active ? "secondary" : "ghost"} className="mb-1 h-10 w-full justify-start" asChild>
<Link to={href}>
<span className={cn(isOpen === false ? "" : "mr-4")}>
<Link to={href} disabled={disabled}>
<Button variant={active ? "secondary" : "ghost"} className="!pointer-events-auto mb-1 h-10 w-full justify-start" disabled={disabled}>
<span
className={cn({
"mr-4": isOpen === true,
})}
>
<Icon size={18} />
</span>
<p className={cn("max-w-[200px] truncate", isOpen === false ? "-translate-x-96 opacity-0" : "translate-x-0 opacity-100")}>{label}</p>
</Link>
</Button>
<p
className={cn("max-w-[200px] truncate", {
"translate-x-0 opacity-100": isOpen === true,
"-translate-x-96 opacity-0": isOpen === false,
})}
>
{label}
</p>
</Button>
</Link>
</TooltipTrigger>
{isOpen === false && <TooltipContent side="right">{label}</TooltipContent>}
{disabled && <TooltipContent side="right">Coming soon</TooltipContent>}
</Tooltip>
</TooltipProvider>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const PAGE_TITLES: Partial<Record<RoutePaths<typeof routeTree>, string>>
"/": "Home",
"/about": "About",
"/account": "Account",
"/lists": "Lists",
"/contact": "Contact",
"/featured": "Featured",
"/library": "Library",
Expand Down
13 changes: 12 additions & 1 deletion src/lib/layout.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { routeTree } from "@/routeTree.gen";
import { RoutePaths } from "@tanstack/react-router";
import { Blocks, House, LucideIcon, BookMarked, Star, Upload } from "lucide-react";
import { Blocks, House, LucideIcon, BookMarked, Star, Upload, BookOpenText } from "lucide-react";

export type Submenu = {
href: RoutePaths<typeof routeTree>;
Expand All @@ -14,6 +14,7 @@ type Menu = {
active: boolean;
icon: LucideIcon;
submenus: Submenu[];
disabled?: boolean;
};

type Group = {
Expand All @@ -39,13 +40,15 @@ export function getMenuList(pathname: RoutePaths<typeof routeTree> | string): Gr
active: pathname === "/library",
icon: BookMarked,
submenus: [],
disabled: import.meta.env.PROD,
},
{
href: "/upload",
label: "Upload",
active: pathname === "/upload",
icon: Upload,
submenus: [],
disabled: import.meta.env.PROD,
},
],
},
Expand All @@ -58,6 +61,14 @@ export function getMenuList(pathname: RoutePaths<typeof routeTree> | string): Gr
active: pathname === "/account",
icon: House,
submenus: [],
disabled: import.meta.env.PROD,
},
{
href: "/lists",
label: "Lists",
active: pathname === "/lists",
icon: BookOpenText,
submenus: [],
},
{
href: "/settings",
Expand Down
18 changes: 18 additions & 0 deletions src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Route as UploadImport } from './routes/upload'
import { Route as SettingsImport } from './routes/settings'
import { Route as RegisterImport } from './routes/register'
import { Route as LoginImport } from './routes/login'
import { Route as ListsImport } from './routes/lists'
import { Route as LibraryImport } from './routes/library'
import { Route as FeaturedImport } from './routes/featured'
import { Route as ContactImport } from './routes/contact'
Expand Down Expand Up @@ -44,6 +45,11 @@ const LoginRoute = LoginImport.update({
getParentRoute: () => rootRoute,
} as any)

const ListsRoute = ListsImport.update({
path: '/lists',
getParentRoute: () => rootRoute,
} as any)

const LibraryRoute = LibraryImport.update({
path: '/library',
getParentRoute: () => rootRoute,
Expand Down Expand Up @@ -120,6 +126,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof LibraryImport
parentRoute: typeof rootRoute
}
'/lists': {
id: '/lists'
path: '/lists'
fullPath: '/lists'
preLoaderRoute: typeof ListsImport
parentRoute: typeof rootRoute
}
'/login': {
id: '/login'
path: '/login'
Expand Down Expand Up @@ -160,6 +173,7 @@ export const routeTree = rootRoute.addChildren({
ContactRoute,
FeaturedRoute,
LibraryRoute,
ListsRoute,
LoginRoute,
RegisterRoute,
SettingsRoute,
Expand All @@ -180,6 +194,7 @@ export const routeTree = rootRoute.addChildren({
"/contact",
"/featured",
"/library",
"/lists",
"/login",
"/register",
"/settings",
Expand All @@ -204,6 +219,9 @@ export const routeTree = rootRoute.addChildren({
"/library": {
"filePath": "library.tsx"
},
"/lists": {
"filePath": "lists.tsx"
},
"/login": {
"filePath": "login.tsx"
},
Expand Down
1 change: 1 addition & 0 deletions src/routes/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Trash2 } from "lucide-react";
export const Route = createFileRoute("/account")({
component: Account,
beforeLoad: (ctx) => {
if (import.meta.env.PROD) throw redirect({ to: "/", search: { q: "" } });
if (!ctx.context.auth.isLoggedIn) throw redirect({ to: "/login" });
},
});
Expand Down
6 changes: 4 additions & 2 deletions src/routes/library.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import * as React from "react";
import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { createFileRoute, Link } from "@tanstack/react-router";
import { createFileRoute, Link, redirect } from "@tanstack/react-router";

export const Route = createFileRoute("/library")({
component: Library,
beforeLoad: () => {
if (import.meta.env.PROD) throw redirect({ to: "/", search: { q: "" } });
},
});

function Library() {
Expand Down
25 changes: 25 additions & 0 deletions src/routes/lists.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { BookItem } from "@/components/books/book-item";
import { useBookmarksStore } from "@/stores/bookmarks";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/lists")({
component: Lists,
});

export function Lists() {
const bookmarks = useBookmarksStore((state) => state.bookmarks);

return (
<div className="flex flex-1 justify-center">
<div className="flex w-full flex-col gap-4">
{bookmarks.length > 0 ? <h1 className="text-2xl font-bold">Your Bookmarks</h1> : null}

<div className="grid grid-cols-2 gap-4">
{bookmarks.map((bookmark) => (
<BookItem key={bookmark.md5} {...bookmark} />
))}
</div>
</div>
</div>
);
}
5 changes: 4 additions & 1 deletion src/routes/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/componen
import { Button } from "@/components/ui/button";
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "@/components/ui/input-otp";
import { useIsMobile } from "@/hooks/use-ismobile";
import { createFileRoute, Link } from "@tanstack/react-router";
import { createFileRoute, Link, redirect } from "@tanstack/react-router";
import { useRouter } from "@tanstack/react-router";
import { z } from "zod";
import { useForm } from "react-hook-form";
Expand All @@ -16,6 +16,9 @@ import { TurnstileWidget } from "@/components/layout/turnstile";

export const Route = createFileRoute("/login")({
component: Login,
beforeLoad: () => {
if (import.meta.env.PROD) throw redirect({ to: "/", search: { q: "" } });
},
});

const loginFormSchema = z.object({
Expand Down
5 changes: 4 additions & 1 deletion src/routes/register.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { useNavigate } from "@tanstack/react-router";
import { redirect, useNavigate } from "@tanstack/react-router";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
Expand All @@ -18,6 +18,9 @@ import { TurnstileWidget } from "@/components/layout/turnstile";

export const Route = createFileRoute("/register")({
component: Register,
beforeLoad: () => {
if (import.meta.env.PROD) throw redirect({ to: "/", search: { q: "" } });
},
loader: async (opts) => {
await opts.context.queryClient.ensureQueryData(randomWordsWithNumberQueryOptions);
},
Expand Down
5 changes: 4 additions & 1 deletion src/routes/upload.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as React from "react";
import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { createFileRoute, Link } from "@tanstack/react-router";
import { createFileRoute, Link, redirect } from "@tanstack/react-router";

export const Route = createFileRoute("/upload")({
component: Upload,
beforeLoad: () => {
if (import.meta.env.PROD) throw redirect({ to: "/", search: { q: "" } });
},
});

function Upload() {
Expand Down
23 changes: 23 additions & 0 deletions src/stores/bookmarks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BookItemResponse } from "@/api/backend/types";
import { create } from "zustand";
import { persist } from "zustand/middleware";

interface BookmarksStoreState {
bookmarks: BookItemResponse[];
addBookmark: (bookmark: BookItemResponse) => void;
removeBookmark: (md5: string) => void;
}

export const useBookmarksStore = create<BookmarksStoreState>()(
persist(
(set) => ({
bookmarks: [],
addBookmark: (bookmark) => set((state) => ({ bookmarks: [...state.bookmarks, bookmark] })),
removeBookmark: (md5) => set((state) => ({ bookmarks: state.bookmarks.filter((b) => b.md5 !== md5) })),
}),
{
name: "BR::bookmarks",
getStorage: () => localStorage,
},
),
);

0 comments on commit ee86e5a

Please sign in to comment.