SEO: OG por post, RSS feed, sitemap, FTS5 busqueda, View Transitions, Cloudflare Polish
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import Link from "next/link"
|
||||
import { Image, Search } from "lucide-react"
|
||||
import { Image, Search, Rss } from "lucide-react"
|
||||
import { SITE_NAME, SITE_TAGLINE, SITE_LINKS } from "@/lib/site"
|
||||
import { AgeGate } from "@/components/age-gate"
|
||||
|
||||
@@ -37,6 +37,17 @@ export default function PublicLayout({ children }: { children: React.ReactNode }
|
||||
{SITE_NAME} · {SITE_TAGLINE}
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<a
|
||||
href="/rss.xml"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 text-[var(--muted)] hover:text-[var(--accent)] transition-colors"
|
||||
title="RSS feed"
|
||||
>
|
||||
<Rss className="h-3.5 w-3.5" />
|
||||
RSS
|
||||
</a>
|
||||
<span className="text-[var(--border)]">|</span>
|
||||
{SITE_LINKS.twitter && (
|
||||
<>
|
||||
<a
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { notFound } from "next/navigation"
|
||||
import Link from "next/link"
|
||||
import type { Metadata } from "next"
|
||||
import { getPostBySlug, getAllPosts } from "@/lib/db"
|
||||
import { PostCard } from "@/components/post-card"
|
||||
import { TagBadge } from "@/components/tag-badge"
|
||||
@@ -7,6 +8,42 @@ import { ArrowLeft, BookOpen, ExternalLink } from "lucide-react"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
// Metadata dinámica: al compartir en Discord/X/WhatsApp muestra portada,
|
||||
// título y descripción del post (Open Graph + Twitter Card).
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params
|
||||
const post = getPostBySlug(slug)
|
||||
if (!post) return { title: "No encontrado" }
|
||||
|
||||
const title = post.title
|
||||
const description =
|
||||
post.summary && !post.summary.startsWith("**") && post.summary.length > 40
|
||||
? post.summary.slice(0, 160)
|
||||
: `${post.num_pages} páginas${post.artist ? ` por ${post.artist}` : ""} — worst-scan fansub`
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
type: "article",
|
||||
url: `https://worstscan.xyz/p/${post.slug}`,
|
||||
images: [{ url: `https://worstscan.xyz/api/cover/${post.gid}`, width: 1200, height: 1600, alt: title }],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title,
|
||||
description,
|
||||
images: [`https://worstscan.xyz/api/cover/${post.gid}`],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function PostDetailPage({
|
||||
params,
|
||||
}: {
|
||||
|
||||
Reference in New Issue
Block a user