this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Book page and Header component

modamo-gh 074bb407 e109fea9

+89 -47
+11
app/book/[bookID]/page.tsx
··· 1 + import Header from "@/components/Header"; 2 + 3 + const Book = () => { 4 + return ( 5 + <> 6 + <Header /> 7 + </> 8 + ); 9 + }; 10 + 11 + export default Book;
+19 -17
app/layout.tsx
··· 3 3 import "./globals.css"; 4 4 5 5 const geistSans = Geist({ 6 - variable: "--font-geist-sans", 7 - subsets: ["latin"], 6 + variable: "--font-geist-sans", 7 + subsets: ["latin"] 8 8 }); 9 9 10 10 const geistMono = Geist_Mono({ 11 - variable: "--font-geist-mono", 12 - subsets: ["latin"], 11 + variable: "--font-geist-mono", 12 + subsets: ["latin"] 13 13 }); 14 14 15 15 export const metadata: Metadata = { 16 - title: "Create Next App", 17 - description: "Generated by create next app", 16 + title: "Create Next App", 17 + description: "Generated by create next app" 18 18 }; 19 19 20 20 export default function RootLayout({ 21 - children, 21 + children 22 22 }: Readonly<{ 23 - children: React.ReactNode; 23 + children: React.ReactNode; 24 24 }>) { 25 - return ( 26 - <html lang="en"> 27 - <body 28 - className={`${geistSans.variable} ${geistMono.variable} antialiased`} 29 - > 30 - {children} 31 - </body> 32 - </html> 33 - ); 25 + return ( 26 + <html lang="en"> 27 + <body 28 + className={`${geistSans.variable} ${geistMono.variable} antialiased`} 29 + > 30 + <main className="bg-amber-100 gap-4 grid grid-rows-10 h-screen p-4 w-screen"> 31 + {children} 32 + </main> 33 + </body> 34 + </html> 35 + ); 34 36 }
+19 -30
app/library/page.tsx
··· 1 + import Header from "@/components/Header"; 1 2 import { getSession } from "@/lib/auth/session"; 2 3 import { Agent } from "@atproto/api"; 3 4 import Image from "next/image"; 5 + import Link from "next/link"; 4 6 import { redirect } from "next/navigation"; 5 7 6 8 const Library = async () => { ··· 12 14 13 15 const agent = new Agent(session); 14 16 15 - let books, error, profile; 17 + let books; 16 18 17 19 try { 18 - const profileResponse = await agent.getProfile({ actor: session.did }); 19 - 20 - profile = profileResponse.data; 21 - 22 20 const booksResponse = await agent.com.atproto.repo.listRecords({ 23 21 collection: "buzz.bookhive.book", 24 22 limit: 100, ··· 38 36 } 39 37 40 38 return ( 41 - <main className="bg-amber-100 gap-4 grid grid-rows-10 h-screen p-4 w-screen"> 42 - <header className="bg-emerald-900 flex items-center justify-between p-4 rounded-2xl row-span-1"> 43 - <h1 className="text-amber-100 text-4xl">bambü</h1> 44 - <div className="h-16 relative w-16"> 45 - <Image 46 - alt={`Profile picture for ${profile?.handle}`} 47 - className="object-cover rounded-full" 48 - fill 49 - priority 50 - src={profile?.avatar || ""} 51 - /> 52 - </div> 53 - </header> 39 + <> 40 + <Header /> 54 41 <div className="auto-rows-min bg-emerald-900 gap-4 grid grid-cols-5 overflow-y-scroll p-4 rounded-2xl row-span-9"> 55 42 {books?.map((book) => { 56 43 return ( 57 - <div 58 - className="aspect-2/3 overflow-hidden relative rounded-2xl" 44 + <Link 45 + href={`/book/${book.value.hiveId.slice(3)}`} 59 46 key={book.value.hiveId} 60 47 > 61 - <Image 62 - alt={`Book cover for ${book.value.title} by ${book.value.authors}`} 63 - className="object-cover" 64 - fill 65 - src={`/api/blob?cid=${book.value.cover?.ref?.toString()}&did=${ 66 - session.did 67 - }`} 68 - /> 69 - </div> 48 + <div className="aspect-2/3 overflow-hidden relative rounded-2xl"> 49 + <Image 50 + alt={`Book cover for ${book.value.title} by ${book.value.authors}`} 51 + className="object-cover" 52 + fill 53 + src={`/api/blob?cid=${book.value.cover?.ref?.toString()}&did=${ 54 + session.did 55 + }`} 56 + /> 57 + </div> 58 + </Link> 70 59 ); 71 60 })} 72 61 </div> 73 - </main> 62 + </> 74 63 ); 75 64 }; 76 65
+40
components/Header.tsx
··· 1 + import { getSession } from "@/lib/auth/session"; 2 + import { Agent } from "@atproto/api"; 3 + import Image from "next/image"; 4 + import { redirect } from "next/navigation"; 5 + 6 + const Header = async () => { 7 + const session = await getSession(); 8 + 9 + if (!session) { 10 + redirect("/"); 11 + } 12 + 13 + const agent = new Agent(session); 14 + 15 + let profile; 16 + 17 + try { 18 + const response = await agent.getProfile({ actor: session.did }); 19 + 20 + profile = response.data; 21 + } catch (error) { 22 + console.error("Error loading profile:", error); 23 + } 24 + return ( 25 + <header className="bg-emerald-900 flex items-center justify-between p-4 rounded-2xl row-span-1"> 26 + <h1 className="text-amber-100 text-4xl">bambü</h1> 27 + <div className="h-16 relative w-16"> 28 + <Image 29 + alt={`Profile picture for ${profile?.handle}`} 30 + className="object-cover rounded-full" 31 + fill 32 + priority 33 + src={profile?.avatar || ""} 34 + /> 35 + </div> 36 + </header> 37 + ); 38 + }; 39 + 40 + export default Header;