feat: Add shorts embedding.
This commit is contained in:
parent
7e48f03917
commit
b51d9afc76
|
|
@ -15,11 +15,13 @@ export default function PostCover({
|
|||
title,
|
||||
uniqueKey,
|
||||
className,
|
||||
imgClassName,
|
||||
}: {
|
||||
images?: string[]
|
||||
title?: string
|
||||
uniqueKey?: string
|
||||
className?: string
|
||||
imgClassName?: string
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
|
|
@ -47,7 +49,10 @@ export default function PostCover({
|
|||
<SwiperSlide key={image}>
|
||||
<div className="text-[0px] w-full h-full">
|
||||
<Image
|
||||
className="object-cover w-full sm:group-hover:scale-105 sm:transition-transform sm:duration-400 sm:ease-in-out bg-white"
|
||||
className={cn(
|
||||
"object-cover w-full sm:group-hover:scale-105 sm:transition-transform sm:duration-400 sm:ease-in-out bg-white",
|
||||
imgClassName,
|
||||
)}
|
||||
alt="cover"
|
||||
src={image}
|
||||
width={624}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
"use client"
|
||||
|
||||
import { useLocale } from "next-intl"
|
||||
import React, { FC, useCallback } from "react"
|
||||
|
||||
import { Language } from "~/lib/types"
|
||||
import { cn, isMobileDevice } from "~/lib/utils"
|
||||
import { useGetPage } from "~/queries/page"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
|
||||
import { Time } from "../common/Time"
|
||||
import PostCover from "../home/PostCover"
|
||||
import { Avatar } from "./Avatar"
|
||||
|
||||
const XLogShorts: FC<{ slug: string; handle: string }> = ({ slug, handle }) => {
|
||||
const locale = useLocale() as Language
|
||||
// https://xlog.app/site/lca/JOoXQKAtZYYFrnzntDlJ6?content_type=shorts
|
||||
const site = useGetSite(handle)
|
||||
const page = useGetPage({
|
||||
characterId: site.data?.characterId,
|
||||
slug,
|
||||
handle,
|
||||
disableAutofill: true,
|
||||
translateTo: locale,
|
||||
})
|
||||
|
||||
const images = page.data?.metadata?.content?.attachments
|
||||
?.filter((attachment) => attachment.name === "image")
|
||||
.map((img) => img.address || "")
|
||||
.filter(Boolean)
|
||||
|
||||
const isMobile = isMobileDevice()
|
||||
const [isExpanded, setIsExpanded] = React.useState(false)
|
||||
|
||||
const toggleExpand = useCallback(() => {
|
||||
setIsExpanded((prev) => !prev)
|
||||
}, [])
|
||||
|
||||
if (page.isLoading) return <div>Loading...</div>
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={toggleExpand}
|
||||
className={cn(
|
||||
"flex flex-col md:flex-row w-full bg-zinc-50 rounded-xl p-4 gap-x-4 hover:cursor-pointer",
|
||||
isExpanded ? "sm:h-[350px]" : "sm:h-[150px]",
|
||||
"transition-height duration-500 ease-in-out",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className={cn(
|
||||
"w-full h-full mb-2 sm:mb-0 align-middle flex items-center justify-center",
|
||||
isExpanded ? "sm:w-[350px]" : "sm:w-[150px]",
|
||||
"transition-width duration-500 ease-in-out",
|
||||
)}
|
||||
>
|
||||
<PostCover
|
||||
uniqueKey={`short-${page.data?.characterId}-${page.data?.noteId}`}
|
||||
images={images}
|
||||
title={page.data?.metadata?.content?.title}
|
||||
className="rounded-lg w-full aspect-auto border-b-0 h-full"
|
||||
imgClassName="rounded-lg object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col justify-between">
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
{page.data?.metadata?.content?.title && (
|
||||
<div className="font-bold mb-2 text-lg truncate">
|
||||
{page.data?.metadata?.content?.title}
|
||||
</div>
|
||||
)}
|
||||
<div className="prose overflow-y-auto">
|
||||
<p className={`line-clamp-${isExpanded || isMobile ? 6 : 1}`}>
|
||||
{page.data?.metadata?.content?.content}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row w-full justify-between items-end sm:mt-0 mt-4">
|
||||
<span className="text-sm">
|
||||
<Time isoString={page.data?.metadata?.content?.date_published} />
|
||||
</span>
|
||||
|
||||
<span className="p-2 flex w-auto justify-end items-center transition-colors py-1 rounded-lg">
|
||||
<Avatar
|
||||
cid={site.data?.characterId}
|
||||
className="align-middle"
|
||||
images={site.data?.metadata?.content?.avatars || []}
|
||||
name={site.data?.metadata?.content?.name}
|
||||
size={32}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={`flex-1 flex-col min-w-0 ml-2 max-w-[100px] flex sm:mr-6`}
|
||||
>
|
||||
<span
|
||||
className={`text-left leading-none font-medium truncate text-gray-600 text-base`}
|
||||
style={{ marginBottom: "0.15rem" }}
|
||||
>
|
||||
{site.data?.metadata?.content?.name}
|
||||
</span>
|
||||
{site.data?.handle && (
|
||||
<span
|
||||
className={`text-left leading-none text-sm truncate text-gray-400`}
|
||||
>
|
||||
{"@" + site.data?.handle}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<i
|
||||
className={cn(
|
||||
"i-mingcute-arrows-down-line text-xl ml-[2px] text-accent",
|
||||
isExpanded ? "rotate-180" : "rotate-0",
|
||||
"transition-transform duration-500 ease-in-out",
|
||||
"hidden sm:inline-block",
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default XLogShorts
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { match } from "path-to-regexp"
|
||||
|
||||
import type { Transformer } from "../rehype-embed"
|
||||
import { isHostIncludes } from "./utils"
|
||||
|
||||
export const XLogShortsTransformer: Transformer = {
|
||||
name: "XLogShorts",
|
||||
shouldTransform(url) {
|
||||
// There are two patterns for xlog shorts url
|
||||
|
||||
const { host, pathname, searchParams } = url
|
||||
const hasShortsParam = searchParams.get("ct") === "shorts"
|
||||
|
||||
/**
|
||||
* content type param is required
|
||||
* @example ?ct=shorts
|
||||
* */
|
||||
if (!hasShortsParam) return false
|
||||
|
||||
// https://xlog.app/site/caspian-3030/AtOAglnMV_N6xslDbfhz4?ct=shorts
|
||||
if (isHostIncludes("xlog.app", host) && pathname.startsWith("/site/")) {
|
||||
return true
|
||||
}
|
||||
|
||||
// https://caspian-3030.xlog.app/AtOAglnMV_N6xslDbfhz4?ct=shorts
|
||||
return /^[a-z0-9-]+\.xlog\.app/.test(host)
|
||||
},
|
||||
getHTML(url) {
|
||||
let slug = ""
|
||||
let handle = ""
|
||||
|
||||
// import { match } from "path-to-regexp"
|
||||
// https://caspian-3030.xlog.app/AtOAglnMV_N6xslDbfhz4?ct=shorts
|
||||
let matched = match<{ slug: string }>("/:slug")(url.pathname)
|
||||
|
||||
if (matched) {
|
||||
slug = matched.params.slug
|
||||
handle = url.host.split(".")[0]
|
||||
} else {
|
||||
// https://xlog.app/site/caspian-3030/AtOAglnMV_N6xslDbfhz4?ct=shorts
|
||||
matched = match<{ slug: string; handle: string }>("/site/:handle/:slug")(
|
||||
url.pathname,
|
||||
)
|
||||
|
||||
if (!matched) return
|
||||
|
||||
slug = matched?.params.slug || ""
|
||||
// @ts-ignore
|
||||
handle = matched?.params.handle || ""
|
||||
}
|
||||
|
||||
return `<xlog-shorts slug="${slug}" handle="${handle}"/>`
|
||||
},
|
||||
}
|
||||
|
|
@ -4,12 +4,14 @@ import { CodeSandboxTransformer } from "./CodeSandBox"
|
|||
import { GitHubRepoTransformer } from "./GitHub"
|
||||
import { NetEaseMusicTransformer } from "./NetEaseMusic"
|
||||
import { SpotifyTransformer } from "./Spotify"
|
||||
import { XLogShortsTransformer } from "./XLogShorts"
|
||||
import { YouTubeTransformer } from "./YouTube"
|
||||
|
||||
export const transformers: Transformer[] = [
|
||||
CodeSandboxTransformer,
|
||||
GitHubRepoTransformer,
|
||||
YouTubeTransformer,
|
||||
XLogShortsTransformer,
|
||||
NetEaseMusicTransformer,
|
||||
BilibiliTransformer,
|
||||
SpotifyTransformer,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ const Style = dynamic(() => import("~/components/common/Style"))
|
|||
const Mention = dynamic(() => import("~/components/ui/Mention"))
|
||||
const Mermaid = dynamic(() => import("~/components/ui/Mermaid"))
|
||||
const GithubRepo = dynamic(() => import("~/components/ui/GithubRepo"))
|
||||
const XLogShorts = dynamic(() => import("~/components/ui/XLogShorts"))
|
||||
const APlayer = dynamic(() => import("~/components/ui/APlayer"))
|
||||
|
||||
const DPlayer = dynamic(() => import("~/components/ui/DPlayer"))
|
||||
|
|
@ -233,6 +234,7 @@ export const renderPageContent = (
|
|||
audio: APlayer,
|
||||
video: DPlayer,
|
||||
"github-repo": GithubRepo,
|
||||
"xlog-shorts": XLogShorts,
|
||||
style: Style,
|
||||
} as any,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue