fix: og image for glossary

This commit is contained in:
gosangam
2026-05-06 15:52:44 +05:30
parent e5fb370997
commit cbd9c55777
3 changed files with 90 additions and 8 deletions
+57 -7
View File
@@ -3,7 +3,7 @@ export const dynamic = "force-dynamic";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
import { getAllGlossaryEntries } from "@/lib/api"; import { getAllGlossaryEntries } from "@/lib/api";
import { buildOgMeta } from "@/lib/og"; import { buildOgMeta, extractQuoteFromHl } from "@/lib/og";
import { GlossaryClient } from "../glossary-client"; import { GlossaryClient } from "../glossary-client";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -11,12 +11,20 @@ import { GlossaryClient } from "../glossary-client";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const DIACRITIC_TO_BASE: Record<string, string> = { const DIACRITIC_TO_BASE: Record<string, string> = {
Ā: "A", Ä: "A", Ā: "A",
Ī: "I", É: "I", Ä: "A",
Ū: "U", Ü: "U", Ī: "I",
Ṛ: "R", Ṝ: "R", É: "I",
Ś: "S", Ç: "S", Ṣ: "S", Ū: "U",
Ñ: "N", Ṇ: "N", Ṅ: "N", Ü: "U",
Ṛ: "R",
Ṝ: "R",
Ś: "S",
Ç: "S",
Ṣ: "S",
Ñ: "N",
Ṇ: "N",
Ṅ: "N",
Ṭ: "T", Ṭ: "T",
Ḍ: "D", Ḍ: "D",
Ḥ: "H", Ḥ: "H",
@@ -34,11 +42,53 @@ function getBaseLetter(term: string): string {
export async function generateMetadata({ export async function generateMetadata({
params, params,
searchParams,
}: { }: {
params: Promise<{ letter: string }>; params: Promise<{ letter: string }>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}): Promise<Metadata> { }): Promise<Metadata> {
const { letter } = await params; const { letter } = await params;
const sp = await searchParams;
const l = letter.toUpperCase(); const l = letter.toUpperCase();
// If a specific term is referenced via ?term= param, show it in the OG image
const termParam = typeof sp.term === "string" ? sp.term : null;
if (termParam) {
const allEntries = await getAllGlossaryEntries();
const entry = allEntries.find(
(e) => e.term.toLowerCase() === termParam.toLowerCase(),
);
if (entry) {
// Strip HTML tags from definition for OG quote
const plainDef = entry.definitionHtml
.replace(/<[^>]*>/g, "")
.replace(/&[^;]+;/g, " ")
.trim();
// Check for ?hl= highlight param — extract actual selected text from definition
const hlQuote = extractQuoteFromHl(sp, [entry.definitionHtml]);
return buildOgMeta({
title: entry.term,
detail: "Glossary — Prabhupāda Says",
quote:
hlQuote ||
(plainDef.length > 260 ? plainDef.slice(0, 257) + "..." : plainDef),
});
}
}
// Check for ?hl= without ?term= — try to extract quote from all entries for this letter
const hlQuote = extractQuoteFromHl(sp);
if (hlQuote) {
return buildOgMeta({
title: `Glossary — ${l}`,
detail: "Sanskrit & Vedic terms",
quote: hlQuote,
});
}
// Default: letter-level OG
return buildOgMeta({ return buildOgMeta({
title: `Glossary — ${l}`, title: `Glossary — ${l}`,
detail: `Sanskrit & Vedic terms starting with ${l}`, detail: `Sanskrit & Vedic terms starting with ${l}`,
+13
View File
@@ -1,6 +1,7 @@
"use client"; "use client";
import { useState, useMemo, useRef, useEffect, useCallback } from "react"; import { useState, useMemo, useRef, useEffect, useCallback } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import { Search, X, Menu, ChevronLeft, ChevronRight } from "lucide-react"; import { Search, X, Menu, ChevronLeft, ChevronRight } from "lucide-react";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -289,6 +290,7 @@ export function GlossaryClient({
letters, letters,
letterCounts, letterCounts,
}: Props) { }: Props) {
const router = useRouter();
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [activeTermRaw, setActiveTerm] = useState(entries[0]?.term || ""); const [activeTermRaw, setActiveTerm] = useState(entries[0]?.term || "");
const contentRef = useRef<HTMLDivElement>(null); const contentRef = useRef<HTMLDivElement>(null);
@@ -347,6 +349,17 @@ export function GlossaryClient({
return idx !== undefined ? entries[idx] : null; return idx !== undefined ? entries[idx] : null;
}, [entries, termIndex, activeTerm]); }, [entries, termIndex, activeTerm]);
// Update URL with ?term= when active term changes (for OG + shareable links)
useEffect(() => {
const url = new URL(window.location.href);
if (activeTerm) {
url.searchParams.set("term", activeTerm);
} else {
url.searchParams.delete("term");
}
window.history.replaceState({}, "", url.toString());
}, [activeTerm]);
const handleSelectTerm = useCallback((term: string) => { const handleSelectTerm = useCallback((term: string) => {
setActiveTerm(term); setActiveTerm(term);
contentRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); contentRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
+20 -1
View File
@@ -164,8 +164,27 @@ export function getSourceReference(): {
return { title: parentTitle, detail: chapterTitle, url }; return { title: parentTitle, detail: chapterTitle, url };
} }
// /glossary — general // /glossary/[letter]?term=...
if (parts[0] === "glossary") { if (parts[0] === "glossary") {
const h1 = document.querySelector("h1");
const termFromH1 = h1?.textContent?.replace(/^Glossary:\s*/, "").trim();
// Try to get the term from the URL ?term= param
const urlParams = new URLSearchParams(window.location.search);
const termFromUrl = urlParams.get("term");
const term = termFromUrl || termFromH1 || null;
if (term) {
return { title: term, detail: "Glossary — Prabhupāda Says", url };
}
// Fallback: letter-level
const letter = parts[1]?.toUpperCase();
if (letter) {
return { title: `Glossary — ${letter}`, detail: null, url };
}
return { title: "Glossary", detail: null, url }; return { title: "Glossary", detail: null, url };
} }