feat: Add shorts embedding.

This commit is contained in:
Caspian 2023-12-20 13:12:49 +00:00
parent 7e48f03917
commit b51d9afc76
5 changed files with 191 additions and 1 deletions

View File

@ -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}

View File

@ -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

View File

@ -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}"/>`
},
}

View File

@ -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,

View File

@ -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,
})