diff options
Diffstat (limited to 'apps/web/app')
| -rw-r--r-- | apps/web/app/public/layout.tsx | 16 | ||||
| -rw-r--r-- | apps/web/app/public/lists/[listId]/not-found.tsx | 18 | ||||
| -rw-r--r-- | apps/web/app/public/lists/[listId]/page.tsx | 84 |
3 files changed, 118 insertions, 0 deletions
diff --git a/apps/web/app/public/layout.tsx b/apps/web/app/public/layout.tsx new file mode 100644 index 00000000..4203c44c --- /dev/null +++ b/apps/web/app/public/layout.tsx @@ -0,0 +1,16 @@ +import KarakeepLogo from "@/components/KarakeepIcon"; + +export default function PublicLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <div className="h-screen flex-col overflow-y-auto bg-muted"> + <header className="sticky left-0 right-0 top-0 z-50 flex h-16 items-center justify-between overflow-x-auto overflow-y-hidden bg-background p-4 shadow"> + <KarakeepLogo height={38} /> + </header> + <main className="container mx-3 mt-3 flex-1">{children}</main> + </div> + ); +} diff --git a/apps/web/app/public/lists/[listId]/not-found.tsx b/apps/web/app/public/lists/[listId]/not-found.tsx new file mode 100644 index 00000000..a6fd71dc --- /dev/null +++ b/apps/web/app/public/lists/[listId]/not-found.tsx @@ -0,0 +1,18 @@ +import { X } from "lucide-react"; + +export default function PublicListPageNotFound() { + return ( + <div className="mx-auto flex max-w-md flex-1 flex-col items-center justify-center px-4 py-16 text-center"> + <div className="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-slate-100 dark:bg-slate-700"> + <X className="h-12 w-12 text-gray-300" strokeWidth={1.5} /> + </div> + <h1 className="mb-3 text-2xl font-semibold text-gray-800"> + List not found + </h1> + <p className="text-center text-gray-500"> + The list you're looking for doesn't exist or may have been + removed. + </p> + </div> + ); +} diff --git a/apps/web/app/public/lists/[listId]/page.tsx b/apps/web/app/public/lists/[listId]/page.tsx new file mode 100644 index 00000000..c0495b9f --- /dev/null +++ b/apps/web/app/public/lists/[listId]/page.tsx @@ -0,0 +1,84 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import NoBookmarksBanner from "@/components/dashboard/bookmarks/NoBookmarksBanner"; +import PublicBookmarkGrid from "@/components/public/lists/PublicBookmarkGrid"; +import PublicListHeader from "@/components/public/lists/PublicListHeader"; +import { Separator } from "@/components/ui/separator"; +import { api } from "@/server/api/client"; +import { TRPCError } from "@trpc/server"; + +export async function generateMetadata({ + params, +}: { + params: { listId: string }; +}): Promise<Metadata> { + // TODO: Don't load the entire list, just create an endpoint to get the list name + try { + const resp = await api.publicBookmarks.getPublicBookmarksInList({ + listId: params.listId, + }); + return { + title: `${resp.list.name} - Karakeep`, + }; + } catch (e) { + if (e instanceof TRPCError && e.code === "NOT_FOUND") { + notFound(); + } + } + return { + title: "Karakeep", + }; +} + +export default async function PublicListPage({ + params, +}: { + params: { listId: string }; +}) { + try { + const { list, bookmarks, nextCursor } = + await api.publicBookmarks.getPublicBookmarksInList({ + listId: params.listId, + }); + return ( + <div className="flex flex-col gap-3"> + <div className="flex items-center gap-2"> + <span className="text-2xl"> + {list.icon} {list.name} + {list.description && ( + <span className="mx-2 text-lg text-gray-400"> + {`(${list.description})`} + </span> + )} + </span> + </div> + <Separator /> + <PublicListHeader + list={{ + id: params.listId, + numItems: list.numItems, + }} + /> + {list.numItems > 0 ? ( + <PublicBookmarkGrid + list={{ + id: params.listId, + name: list.name, + description: list.description, + icon: list.icon, + numItems: list.numItems, + }} + bookmarks={bookmarks} + nextCursor={nextCursor} + /> + ) : ( + <NoBookmarksBanner /> + )} + </div> + ); + } catch (e) { + if (e instanceof TRPCError && e.code === "NOT_FOUND") { + notFound(); + } + } +} |
