feat: posts cover
This commit is contained in:
parent
b0fb6a64a8
commit
86f4496d75
|
|
@ -17,16 +17,28 @@ export const SiteHome: React.FC<{
|
|||
const excerpt = post.summary?.content
|
||||
return (
|
||||
<Link key={post.id} href={`/${post.slug || post.id}`}>
|
||||
<a className="xlog-post block hover:bg-zinc-100 transition-colors p-5 -mx-5 first:-mt-5 md:rounded-xl">
|
||||
<h3 className="xlog-post-title text-2xl font-bold">
|
||||
{post.title}
|
||||
</h3>
|
||||
<div className="xlog-post-date text-sm text-zinc-400 mt-1">
|
||||
{formatDate(post.date_published)}
|
||||
<a className="xlog-post hover:bg-zinc-100 transition-colors p-5 -mx-5 first:-mt-5 md:rounded-xl flex">
|
||||
<div className="flex-1 flex justify-center flex-col">
|
||||
<h3 className="xlog-post-title text-2xl font-bold">
|
||||
{post.title}
|
||||
</h3>
|
||||
<div className="xlog-post-date text-sm text-zinc-400 mt-1">
|
||||
{formatDate(post.date_published)}
|
||||
</div>
|
||||
<div className="xlog-post-excerpt mt-3 text-zinc-500">
|
||||
{excerpt}
|
||||
{excerpt && "..."}
|
||||
</div>
|
||||
</div>
|
||||
<div className="xlog-post-excerpt mt-3 text-zinc-500">
|
||||
{excerpt}
|
||||
{excerpt && "..."}
|
||||
<div className="xlog-post-cover flex items-center">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
{post.cover && (
|
||||
<img
|
||||
className="object-cover w-24 h-24 rounded ml-4"
|
||||
alt="cover"
|
||||
src={post.cover}
|
||||
></img>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export const SiteLayout: React.FC<SiteLayoutProps> = ({
|
|||
description={
|
||||
ogDescription ?? site?.description?.replace(/<[^>]*>/g, "")
|
||||
}
|
||||
image={getUserContentsUrl(site?.avatars?.[0])}
|
||||
image={page?.cover || getUserContentsUrl(site?.avatars?.[0])}
|
||||
/>
|
||||
<SiteHeader site={site} />
|
||||
<style>{site?.css}</style>
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export type SiteNavigationItem = {
|
|||
export type Note = UniNote & {
|
||||
slug?: string
|
||||
character?: Profile
|
||||
cover?: string
|
||||
}
|
||||
|
||||
export type Notes = {
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ export type MarkdownEnv = {
|
|||
excerpt: string
|
||||
frontMatter: Record<string, any>
|
||||
__internal: Record<string, any>
|
||||
cover: string
|
||||
}
|
||||
|
||||
export type Rendered = {
|
||||
contentHTML: string
|
||||
excerpt: string
|
||||
frontMatter: Record<string, any>
|
||||
cover: string
|
||||
}
|
||||
|
||||
refractor.alias("html", ["svelte", "vue"])
|
||||
|
|
@ -33,7 +35,12 @@ refractor.alias("html", ["svelte", "vue"])
|
|||
const rehypePrism = rehypePrismGenerator(refractor)
|
||||
|
||||
export const renderPageContent = async (content: string): Promise<Rendered> => {
|
||||
const env: MarkdownEnv = { excerpt: "", __internal: {}, frontMatter: {} }
|
||||
const env: MarkdownEnv = {
|
||||
excerpt: "",
|
||||
__internal: {},
|
||||
frontMatter: {},
|
||||
cover: "",
|
||||
}
|
||||
|
||||
const result = await unified()
|
||||
.use(remarkParse)
|
||||
|
|
@ -55,7 +62,7 @@ export const renderPageContent = async (content: string): Promise<Rendered> => {
|
|||
.use(remarkCallout)
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypeRaw)
|
||||
.use(rehypeImage)
|
||||
.use(rehypeImage, { env })
|
||||
.use(rehypeSanitize, {
|
||||
...defaultSchema,
|
||||
tagNames: [...(defaultSchema.tagNames || []), "video", "iframe"],
|
||||
|
|
@ -94,5 +101,10 @@ export const renderPageContent = async (content: string): Promise<Rendered> => {
|
|||
|
||||
const contentHTML = result.toString()
|
||||
|
||||
return { contentHTML, excerpt: env.excerpt, frontMatter: env.frontMatter }
|
||||
return {
|
||||
contentHTML,
|
||||
excerpt: env.excerpt,
|
||||
frontMatter: env.frontMatter,
|
||||
cover: env.cover,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,15 @@ import { Plugin } from "unified"
|
|||
import { visit } from "unist-util-visit"
|
||||
import { getUserContentsUrl } from "~/lib/user-contents"
|
||||
import { toGateway } from "~/lib/ipfs-parser"
|
||||
import { MarkdownEnv } from "."
|
||||
|
||||
const isExternLink = (url: string) => /^https?:\/\//.test(url)
|
||||
|
||||
export const rehypeImage: Plugin<Array<void>, Root> = () => {
|
||||
export const rehypeImage: Plugin<Array<{ env: MarkdownEnv }>, Root> = ({
|
||||
env,
|
||||
}) => {
|
||||
return (tree) => {
|
||||
let first = true
|
||||
visit(tree, { tagName: "img" }, (node) => {
|
||||
if (!node.properties) return
|
||||
|
||||
|
|
@ -21,6 +25,11 @@ export const rehypeImage: Plugin<Array<void>, Root> = () => {
|
|||
node.properties.src = ipfsUrl
|
||||
url = ipfsUrl
|
||||
|
||||
if (first) {
|
||||
env.cover = url
|
||||
first = false
|
||||
}
|
||||
|
||||
if (isExternLink(url)) {
|
||||
if (!url.startsWith("https:")) {
|
||||
throw new Error(`External image url must start with https`)
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ const expandPage = async (page: Note, render: boolean) => {
|
|||
mime_type: "text/html",
|
||||
}
|
||||
}
|
||||
page.cover = rendered.cover
|
||||
}
|
||||
page.slug =
|
||||
page.attributes?.find((a) => a.trait_type === "xlog_slug")?.value ||
|
||||
|
|
|
|||
Loading…
Reference in New Issue