diff --git a/app/books/bbd/[chapter]/page.tsx b/app/books/bbd/[chapter]/page.tsx index 17dca253..f85c48e5 100644 --- a/app/books/bbd/[chapter]/page.tsx +++ b/app/books/bbd/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type BbdChapter, type BbdSidebarItem, } from "@/lib/bbd"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,25 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["bbd"], quote: hlQuote }); + } + return ogForChapter("bbd", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/bg/[chapter]/[verse]/page.tsx b/app/books/bg/[chapter]/[verse]/page.tsx index 48d96290..aea29bd7 100644 --- a/app/books/bg/[chapter]/[verse]/page.tsx +++ b/app/books/bg/[chapter]/[verse]/page.tsx @@ -1,9 +1,36 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { PageHeader } from "@/components/page-header"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; + +export const dynamic = "force-dynamic"; + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Bhagavad-gītā As It Is", + detail: `Chapter ${chapter}, Verse ${verse}`, + quote, + }); + } + return ogForVerse("bg", { + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} type BgMeta = { book: string; diff --git a/app/books/bk/[chapter]/page.tsx b/app/books/bk/[chapter]/page.tsx index 1fdaa08b..5373b088 100644 --- a/app/books/bk/[chapter]/page.tsx +++ b/app/books/bk/[chapter]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +7,8 @@ import { type BkChapter, type BkSidebarItem, } from "@/lib/bk"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -15,6 +18,21 @@ import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; export const dynamic = "force-dynamic"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["bk"], quote: hlQuote }); + } + return ogForChapter("bk", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/bs/[chapter]/[verse]/page.tsx b/app/books/bs/[chapter]/[verse]/page.tsx index d1fa3873..3af73cc2 100644 --- a/app/books/bs/[chapter]/[verse]/page.tsx +++ b/app/books/bs/[chapter]/[verse]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import fs from "fs"; import path from "path"; import Link from "next/link"; @@ -12,6 +13,9 @@ import { } from "@/lib/api"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; + +export const dynamic = "force-dynamic"; type BsMeta = { book: string; @@ -110,6 +114,29 @@ async function getVerseWithFallback( return null; } +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Śrī Brahma-saṁhitā", + detail: `Chapter ${chapter}, Verse ${parseInt(verse)}`, + quote, + }); + } + return ogForVerse("bs", { + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} + export default async function VersePage({ params, }: { diff --git a/app/books/cbhc/[chapter]/page.tsx b/app/books/cbhc/[chapter]/page.tsx index b972a887..61ba6971 100644 --- a/app/books/cbhc/[chapter]/page.tsx +++ b/app/books/cbhc/[chapter]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +7,8 @@ import { type CbhcChapter, type CbhcSidebarItem, } from "@/lib/cbhc"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -15,6 +18,21 @@ import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; export const dynamic = "force-dynamic"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["cbhc"], quote: hlQuote }); + } + return ogForChapter("cbhc", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/cc/[lila]/[chapter]/[verse]/page.tsx b/app/books/cc/[lila]/[chapter]/[verse]/page.tsx index edd3035c..7807e6bc 100644 --- a/app/books/cc/[lila]/[chapter]/[verse]/page.tsx +++ b/app/books/cc/[lila]/[chapter]/[verse]/page.tsx @@ -1,9 +1,13 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { PageHeader } from "@/components/page-header"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; type ChapterInfo = { number: number; @@ -19,6 +23,31 @@ type LilaInfo = { chapters: ChapterInfo[]; }; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ lila: string; chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { lila, chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + const bookTitle = "Śrī Caitanya-caritāmṛta"; + return buildOgMeta({ + title: bookTitle, + detail: `${lila.charAt(0).toUpperCase() + lila.slice(1)}, Chapter ${chapter}, Verse ${verse}`, + quote, + }); + } + return ogForVerse("cc", { + division: lila, + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} + export default async function CcVersePage({ params, }: { diff --git a/app/books/ds/[chapter]/page.tsx b/app/books/ds/[chapter]/page.tsx index f7548d5e..44e6f1ee 100644 --- a/app/books/ds/[chapter]/page.tsx +++ b/app/books/ds/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type DsChapter, type DsSidebarItem, } from "@/lib/ds"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,25 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["ds"], quote: hlQuote }); + } + return ogForChapter("ds", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/ej/[chapter]/page.tsx b/app/books/ej/[chapter]/page.tsx index ea826491..aa298f70 100644 --- a/app/books/ej/[chapter]/page.tsx +++ b/app/books/ej/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type EjChapter, type EjSidebarItem, } from "@/lib/ej"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,25 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["ej"], quote: hlQuote }); + } + return ogForChapter("ej", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/ek/[chapter]/page.tsx b/app/books/ek/[chapter]/page.tsx index 2e36787d..b3ecbf17 100644 --- a/app/books/ek/[chapter]/page.tsx +++ b/app/books/ek/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type EkChapter, type EkSidebarItem, } from "@/lib/ek"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,25 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["ek"], quote: hlQuote }); + } + return ogForChapter("ek", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/gg/[chapter]/page.tsx b/app/books/gg/[chapter]/page.tsx index 369a7cc2..66aacbe3 100644 --- a/app/books/gg/[chapter]/page.tsx +++ b/app/books/gg/[chapter]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +7,8 @@ import { type GgChapter, type GgSidebarItem, } from "@/lib/gg"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -15,6 +18,21 @@ import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; export const dynamic = "force-dynamic"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["gg"], quote: hlQuote }); + } + return ogForChapter("gg", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/hkh/[chapter]/page.tsx b/app/books/hkh/[chapter]/page.tsx index 933f4130..ef0b99b5 100644 --- a/app/books/hkh/[chapter]/page.tsx +++ b/app/books/hkh/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type HkhChapter, type HkhSidebarItem, } from "@/lib/hkh"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,25 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["hkh"], quote: hlQuote }); + } + return ogForChapter("hkh", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/iso/[mantra]/page.tsx b/app/books/iso/[mantra]/page.tsx index a601fcc6..d6542447 100644 --- a/app/books/iso/[mantra]/page.tsx +++ b/app/books/iso/[mantra]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -7,6 +10,7 @@ import { type IsoIntroSegment, type IsoSidebarItem, } from "@/lib/iso"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -28,6 +32,40 @@ function isIntroSegment( return "paragraphs" in content && "order" in content; } +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ mantra: string }>; + searchParams: Promise>; +}): Promise { + const { mantra: slug } = await params; + const sp = await searchParams; + + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Śrī Īśopaniṣad", + detail: `Mantra ${slug}`, + quote, + }); + } + + const content = await getIsoContent(slug); + const isVerse_ = content && "mantra" in content && "devanagari" in content; + const title = isVerse_ + ? `${(content as IsoVerse).reference}: ${(content as IsoVerse).title}` + : content && "title" in content + ? (content as IsoIntroSegment).title + : `Mantra ${slug}`; + + return ogForChapter("iso", title); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/kb/[volume]/[slug]/page.tsx b/app/books/kb/[volume]/[slug]/page.tsx index 7fd7a47a..b58bad23 100644 --- a/app/books/kb/[volume]/[slug]/page.tsx +++ b/app/books/kb/[volume]/[slug]/page.tsx @@ -1,6 +1,11 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { getProseChapter, getProseChapters } from "@/lib/api"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -8,6 +13,31 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ volume: string; slug: string }>; + searchParams: Promise>; +}): Promise { + const { volume, slug } = await params; + const sp = await searchParams; + const vol = parseInt(volume); + const chapter = !isNaN(vol) ? await getProseChapter("kb", vol, slug) : null; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ + title: BOOK_NAMES["kb"], + detail: chapter ? `Volume ${vol} — ${chapter.title}` : `Volume ${vol}`, + quote: hlQuote, + }); + } + return ogForChapter( + "kb", + chapter ? `Volume ${vol} — ${chapter.title}` : `Volume ${vol}`, + ); +} + export default async function KbChapterPage({ params, }: { diff --git a/app/books/lcl/[chapter]/page.tsx b/app/books/lcl/[chapter]/page.tsx index 05c6bb2b..d9a8d141 100644 --- a/app/books/lcl/[chapter]/page.tsx +++ b/app/books/lcl/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type LclChapter, type LclSidebarItem, } from "@/lib/lcl"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,25 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["lcl"], quote: hlQuote }); + } + return ogForChapter("lcl", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/lob/[chapter]/page.tsx b/app/books/lob/[chapter]/page.tsx index ea6e5d62..92f0a41f 100644 --- a/app/books/lob/[chapter]/page.tsx +++ b/app/books/lob/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type LobChapter, type LobSidebarItem, } from "@/lib/lob"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,21 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["lob"], quote: hlQuote }); + } + return ogForChapter("lob", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- @@ -76,7 +96,7 @@ export default async function LobChapterPage({
- {content.paragraphs.map((paragraph, i) => ( + {content.paragraphs.map((paragraph, i) => paragraph.startsWith("") ? (
- ) - ))} + ), + )}
diff --git a/app/books/mg/[chapter]/page.tsx b/app/books/mg/[chapter]/page.tsx index 4e7ca4ab..d1f446e3 100644 --- a/app/books/mg/[chapter]/page.tsx +++ b/app/books/mg/[chapter]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -6,6 +9,8 @@ import { type MgChapter, type MgSidebarItem, } from "@/lib/mg"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; +import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,6 +18,21 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string }>; + searchParams: Promise>; +}): Promise { + const sp = await searchParams; + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ title: BOOK_NAMES["mg"], quote: hlQuote }); + } + return ogForChapter("mg", ""); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/app/books/mm/[chapter]/[verse]/page.tsx b/app/books/mm/[chapter]/[verse]/page.tsx index 6a938eea..3f98fd1a 100644 --- a/app/books/mm/[chapter]/[verse]/page.tsx +++ b/app/books/mm/[chapter]/[verse]/page.tsx @@ -12,6 +12,33 @@ import { } from "@/lib/api"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; +import type { Metadata } from "next"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; + +export const dynamic = "force-dynamic"; + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Mukunda-mālā-stotra", + detail: `Chapter ${chapter}, Verse ${verse}`, + quote, + }); + } + return ogForVerse("mm", { + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} type MmMeta = { book: string; diff --git a/app/books/nbs/[chapter]/[verse]/page.tsx b/app/books/nbs/[chapter]/[verse]/page.tsx index 46b904c9..e16fadc8 100644 --- a/app/books/nbs/[chapter]/[verse]/page.tsx +++ b/app/books/nbs/[chapter]/[verse]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import fs from "fs"; import path from "path"; import Link from "next/link"; @@ -10,6 +13,7 @@ import { type VerseIndexEntry, type VerseRecord, } from "@/lib/api"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; @@ -110,6 +114,29 @@ async function getVerseWithFallback( return null; } +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Nārada-bhakti-sūtra", + detail: `Chapter ${chapter}, Verse ${verse}`, + quote, + }); + } + return ogForVerse("nbs", { + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} + export default async function VersePage({ params, }: { diff --git a/app/books/noi/[text]/page.tsx b/app/books/noi/[text]/page.tsx index a3c85df7..ce984301 100644 --- a/app/books/noi/[text]/page.tsx +++ b/app/books/noi/[text]/page.tsx @@ -1,3 +1,6 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -7,6 +10,7 @@ import { type NoiIntroSegment, type NoiSidebarItem, } from "@/lib/noi"; +import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -32,6 +36,28 @@ function isIntroSegment( // Page // --------------------------------------------------------------------------- +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ text: string }>; + searchParams: Promise>; +}): Promise { + const { text: slug } = await params; + const sp = await searchParams; + + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "The Nectar of Instruction", + detail: `Text ${slug}`, + quote, + }); + } + + return ogForChapter("noi", `Text ${slug}`); +} + export default async function NoiTextPage({ params, }: { diff --git a/app/books/sb/[canto]/[chapter]/[verse]/page.tsx b/app/books/sb/[canto]/[chapter]/[verse]/page.tsx index e779ac35..225431f6 100644 --- a/app/books/sb/[canto]/[chapter]/[verse]/page.tsx +++ b/app/books/sb/[canto]/[chapter]/[verse]/page.tsx @@ -1,9 +1,13 @@ +export const dynamic = "force-dynamic"; + +import type { Metadata } from "next"; import Link from "next/link"; import { PageHeader } from "@/components/page-header"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; type ChapterInfo = { number: number; @@ -18,6 +22,30 @@ type CantoInfo = { chapters: ChapterInfo[]; }; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ canto: string; chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { canto, chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Śrīmad-Bhāgavatam", + detail: `Canto ${canto}, Chapter ${chapter}, Verse ${verse}`, + quote, + }); + } + return ogForVerse("sb", { + division: canto, + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} + export default async function SbVersePage({ params, }: { diff --git a/app/books/sb1962/[chapter]/[verse]/page.tsx b/app/books/sb1962/[chapter]/[verse]/page.tsx index 119bd6cc..a5e50a6b 100644 --- a/app/books/sb1962/[chapter]/[verse]/page.tsx +++ b/app/books/sb1962/[chapter]/[verse]/page.tsx @@ -1,9 +1,13 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { PageHeader } from "@/components/page-header"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; import { ChapterViewer } from "@/app/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/mobile-nav"; +import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; + +export const dynamic = "force-dynamic"; type Sb1962Meta = { book: string; @@ -13,6 +17,29 @@ type Sb1962Meta = { chapters: { number: number; title: string; totalVerses: number }[]; }; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ chapter: string; verse: string }>; + searchParams: Promise>; +}): Promise { + const { chapter, verse } = await params; + const sp = await searchParams; + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: "Śrīmad-Bhāgavatam (1962)", + detail: `Chapter ${chapter}, Verse ${verse}`, + quote, + }); + } + return ogForVerse("sb1962", { + chapter: parseInt(chapter), + verse: parseInt(verse), + }); +} + export default async function VersePage({ params, }: { diff --git a/app/essays/[slug]/page.tsx b/app/essays/[slug]/page.tsx index d5ad845b..7f060d7e 100644 --- a/app/essays/[slug]/page.tsx +++ b/app/essays/[slug]/page.tsx @@ -1,7 +1,9 @@ export const dynamic = "force-dynamic"; +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; +import { ogForEssay, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -90,6 +92,30 @@ async function getEssaySidebar(): Promise { // Page // --------------------------------------------------------------------------- +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ slug: string }>; + searchParams: Promise>; +}): Promise { + const { slug } = await params; + const sp = await searchParams; + const essay = await getEssay(slug); + if (!essay) return {}; + + const hlQuote = extractQuoteFromHl(sp); + if (hlQuote) { + return buildOgMeta({ + title: essay.title, + detail: "Essay by Śrīla Prabhupāda", + quote: hlQuote, + }); + } + + return ogForEssay(essay.title); +} + export default async function EssayPage({ params, }: { diff --git a/app/journals/[slug]/[chapter]/page.tsx b/app/journals/[slug]/[chapter]/page.tsx index 9cb4ab60..a7b20cf0 100644 --- a/app/journals/[slug]/[chapter]/page.tsx +++ b/app/journals/[slug]/[chapter]/page.tsx @@ -1,5 +1,6 @@ export const dynamic = "force-dynamic"; +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -9,6 +10,7 @@ import { slugToBookCode, type JournalSidebarItem, } from "@/lib/journals"; +import { ogForJournal, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -16,6 +18,37 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ slug: string; chapter: string }>; + searchParams: Promise>; +}): Promise { + const { slug, chapter: chapterSlug } = await params; + const sp = await searchParams; + const bookCode = slugToBookCode(slug); + + const [meta, content] = await Promise.all([ + getJournalMeta(bookCode), + getJournalContent(bookCode, chapterSlug), + ]); + + const journalTitle = meta?.title || slug; + const entryTitle = content?.title || chapterSlug; + + const quote = extractQuoteFromHl(sp); + if (quote) { + return buildOgMeta({ + title: journalTitle, + detail: entryTitle, + quote, + }); + } + + return ogForJournal(journalTitle, entryTitle); +} + export default async function JournalChapterPage({ params, }: { diff --git a/app/letters/[slug]/page.tsx b/app/letters/[slug]/page.tsx index a9c59c28..6edd062b 100644 --- a/app/letters/[slug]/page.tsx +++ b/app/letters/[slug]/page.tsx @@ -4,7 +4,7 @@ import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { getLetter, getAdjacentLetters } from "@/lib/letters"; -import { ogForLetter } from "@/lib/og"; +import { ogForLetter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -13,14 +13,27 @@ import { cn } from "@/lib/utils"; export async function generateMetadata({ params, + searchParams, }: { params: Promise<{ slug: string }>; + searchParams: Promise>; }): Promise { const { slug } = await params; + const sp = await searchParams; const letter = await getLetter(slug); if (!letter) return {}; const meta = [letter.date, letter.location].filter(Boolean).join(" · "); + const hlQuote = extractQuoteFromHl(sp); + + if (hlQuote) { + return buildOgMeta({ + title: `Letter to ${letter.recipientDisplay}`, + detail: meta || undefined, + quote: hlQuote, + }); + } + return ogForLetter(letter.recipientDisplay, meta || undefined); } diff --git a/app/magazines/[slug]/[issue]/page.tsx b/app/magazines/[slug]/[issue]/page.tsx index a4d3fcea..9ab951fd 100644 --- a/app/magazines/[slug]/[issue]/page.tsx +++ b/app/magazines/[slug]/[issue]/page.tsx @@ -1,5 +1,6 @@ export const dynamic = "force-dynamic"; +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -9,6 +10,7 @@ import { slugToBookCode, type MagazineSidebarItem, } from "@/lib/magazines"; +import { ogForMagazine, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -16,6 +18,35 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ slug: string; issue: string }>; + searchParams: Promise>; +}): Promise { + const { slug, issue: issueSlug } = await params; + const sp = await searchParams; + const bookCode = slugToBookCode(slug); + + const quote = extractQuoteFromHl(sp); + if (quote) { + const meta = await getMagazineMeta(bookCode); + const title = meta?.title ?? slug; + return buildOgMeta({ title, quote }); + } + + const [meta, content] = await Promise.all([ + getMagazineMeta(bookCode), + getMagazineContent(bookCode, issueSlug), + ]); + + const magTitle = meta?.title ?? slug; + const issueTitle = content?.title ?? undefined; + + return ogForMagazine(magTitle, issueTitle); +} + export default async function MagazineIssuePage({ params, }: { diff --git a/app/other-documents/[slug]/[chapter]/page.tsx b/app/other-documents/[slug]/[chapter]/page.tsx index 95d66103..41b33ccb 100644 --- a/app/other-documents/[slug]/[chapter]/page.tsx +++ b/app/other-documents/[slug]/[chapter]/page.tsx @@ -1,5 +1,6 @@ export const dynamic = "force-dynamic"; +import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { @@ -9,6 +10,7 @@ import { slugToBookCode, type OtherDocumentSidebarItem, } from "@/lib/other-documents"; +import { ogForOtherDocument, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { PageHeader } from "@/components/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { buttonVariants } from "@/components/ui/button"; @@ -16,6 +18,38 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { cn } from "@/lib/utils"; import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav"; +// --------------------------------------------------------------------------- +// Metadata +// --------------------------------------------------------------------------- + +export async function generateMetadata({ + params, + searchParams, +}: { + params: Promise<{ slug: string; chapter: string }>; + searchParams: Promise>; +}): Promise { + const { slug, chapter: chapterSlug } = await params; + const sp = await searchParams; + const bookCode = slugToBookCode(slug); + + const quote = extractQuoteFromHl(sp); + if (quote) { + const meta = await getOtherDocumentMeta(bookCode); + const content = await getOtherDocumentContent(bookCode, chapterSlug); + const title = meta?.title ?? slug; + const detail = content?.title ?? `Chapter ${chapterSlug}`; + return buildOgMeta({ title, detail, quote }); + } + + const [meta, content] = await Promise.all([ + getOtherDocumentMeta(bookCode), + getOtherDocumentContent(bookCode, chapterSlug), + ]); + + return ogForOtherDocument(meta?.title ?? slug, content?.title ?? undefined); +} + // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- diff --git a/lib/og.ts b/lib/og.ts index eaf61086..978cefdf 100644 --- a/lib/og.ts +++ b/lib/og.ts @@ -6,6 +6,7 @@ */ import { BOOK_NAMES, CC_LILAS } from "@/lib/constants"; +import { decodeTextFragment } from "@/lib/text-highlight"; const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://prabhupadasays.org"; @@ -297,6 +298,43 @@ export function ogForLettersByLocation(location: string, count: number) { }); } +// --------------------------------------------------------------------------- +// hl= query param → OG quote extraction +// --------------------------------------------------------------------------- + +/** + * Extract a display quote from the `?hl=` search param. + * + * The `hl` param encodes `prefix,suffix,length`. We reconstruct a readable + * snippet from the prefix + suffix: "first few words ... last few words" + * + * Returns null if hl is missing or invalid. + */ +export function extractQuoteFromHl( + searchParams: Record | URLSearchParams, +): string | null { + const raw = + searchParams instanceof URLSearchParams + ? searchParams.get("hl") + : typeof searchParams.hl === "string" + ? searchParams.hl + : null; + + if (!raw) return null; + + const fragment = decodeTextFragment(raw); + if (!fragment) return null; + + const { prefix, suffix } = fragment; + if (!prefix) return null; + + if (suffix && suffix !== prefix) { + return `${prefix} ... ${suffix}`; + } + + return prefix; +} + /** Default OG metadata for the site. */ export function ogDefault() { return buildOgMeta({