Support naddr/nevent/note slugs for unindexed posts, cache naddr lookups, render Nostr embeds in markdown, and add a consistency check for events mirrors. Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import { Navbar } from "@/components/public/Navbar";
|
|
import { Footer } from "@/components/public/Footer";
|
|
import {
|
|
BlogIndex,
|
|
type BlogPost,
|
|
type BlogCategory,
|
|
} from "@/components/public/BlogIndex";
|
|
import { BreadcrumbJsonLd } from "@/components/public/JsonLd";
|
|
import { apiUrl } from "@/lib/api-base";
|
|
|
|
const LIMIT = 9;
|
|
|
|
// Render at request time so the first page of posts is in the HTML for crawlers.
|
|
export const dynamic = "force-dynamic";
|
|
|
|
async function fetchJson<T>(path: string, fallback: T): Promise<T> {
|
|
try {
|
|
const res = await fetch(apiUrl(path), { cache: "no-store" });
|
|
if (!res.ok) return fallback;
|
|
return (await res.json()) as T;
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
export default async function BlogPage() {
|
|
const [{ posts, total }, categories] = await Promise.all([
|
|
fetchJson<{ posts: BlogPost[]; total: number }>(`/posts?page=1&limit=${LIMIT}`, {
|
|
posts: [],
|
|
total: 0,
|
|
}),
|
|
fetchJson<BlogCategory[]>("/categories", []),
|
|
]);
|
|
|
|
return (
|
|
<>
|
|
<BreadcrumbJsonLd
|
|
items={[
|
|
{ name: "Home", href: "/" },
|
|
{ name: "Blog", href: "/blog" },
|
|
]}
|
|
/>
|
|
<Navbar />
|
|
|
|
<div className="min-h-screen">
|
|
<header className="pt-24 pb-16 px-8">
|
|
<div className="max-w-7xl mx-auto">
|
|
<p className="uppercase tracking-[0.2em] text-primary mb-4 font-semibold text-sm">
|
|
From the Nostr Network
|
|
</p>
|
|
<h1 className="text-5xl md:text-7xl font-black tracking-tighter mb-4">
|
|
Blog
|
|
</h1>
|
|
<p className="text-xl text-on-surface-variant max-w-xl leading-relaxed">
|
|
Curated Bitcoin content from the Nostr network
|
|
</p>
|
|
</div>
|
|
</header>
|
|
|
|
<BlogIndex
|
|
initialPosts={Array.isArray(posts) ? posts : []}
|
|
initialTotal={total ?? 0}
|
|
categories={Array.isArray(categories) ? categories : []}
|
|
limit={LIMIT}
|
|
/>
|
|
</div>
|
|
|
|
<Footer />
|
|
</>
|
|
);
|
|
}
|