parent
b48a3807ea
commit
3edf43af7c
|
|
@ -0,0 +1,26 @@
|
|||
import { getTweet, Tweet } from "react-tweet/api"
|
||||
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
|
||||
export async function GET(req: Request): Promise<Response> {
|
||||
let { id: tweetId } = getQuery(req)
|
||||
const res = new NextServerResponse()
|
||||
|
||||
if (!tweetId) {
|
||||
return res.status(400).json({ error: "Bad request." })
|
||||
}
|
||||
|
||||
try {
|
||||
const tweet = (await cacheGet({
|
||||
key: `tweet/${tweetId}`,
|
||||
noUpdate: true,
|
||||
expireTime: 60 * 60 * 24 * 3,
|
||||
getValueFun: async () => await getTweet(tweetId),
|
||||
})) as Tweet | undefined
|
||||
return res.status(tweet ? 200 : 404).json({ data: tweet ?? null })
|
||||
} catch (error: any) {
|
||||
console.error(error)
|
||||
return res.status(400).json({ error: error?.message ?? "Bad request." })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import dynamic from "next/dynamic"
|
||||
import Image from "next/image"
|
||||
import type { TwitterComponents } from "react-tweet"
|
||||
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
|
||||
const ReactTweet = dynamic(async () => (await import("react-tweet")).Tweet)
|
||||
|
||||
const components: TwitterComponents = {
|
||||
AvatarImg: (props) => <Image {...props} alt="avatar" />,
|
||||
MediaImg: (props) => <Image {...props} fill unoptimized alt="tweet-media" />,
|
||||
}
|
||||
|
||||
export default function Tweet({ id }: { id: string }) {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<ReactTweet
|
||||
id={id}
|
||||
components={components}
|
||||
apiUrl={`${SITE_URL}/api/tweet?id=${id}`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { match } from "path-to-regexp"
|
||||
|
||||
import type { Transformer } from "../rehype-embed"
|
||||
import { isHostIncludes } from "./utils"
|
||||
|
||||
export const TweetTransformer: Transformer = {
|
||||
name: "Tweet",
|
||||
shouldTransform(url) {
|
||||
const { host, pathname } = url
|
||||
|
||||
return isHostIncludes("twitter.com", host) && pathname.includes("/status/")
|
||||
},
|
||||
getHTML(url) {
|
||||
const { pathname } = url
|
||||
const matched = match<{ id: string }>("/:user/status/:id")(pathname)
|
||||
if (!matched) return
|
||||
return `<tweet id="${matched.params.id}" />`
|
||||
},
|
||||
}
|
||||
|
|
@ -4,11 +4,13 @@ import { CodeSandboxTransformer } from "./CodeSandBox"
|
|||
import { GitHubRepoTransformer } from "./GitHub"
|
||||
import { NetEaseMusicTransformer } from "./NetEaseMusic"
|
||||
import { SpotifyTransformer } from "./Spotify"
|
||||
import { TweetTransformer } from "./Tweet"
|
||||
import { XLogPostTransformer } from "./XLogPost"
|
||||
import { YouTubeTransformer } from "./YouTube"
|
||||
|
||||
export const transformers: Transformer[] = [
|
||||
CodeSandboxTransformer,
|
||||
TweetTransformer,
|
||||
GitHubRepoTransformer,
|
||||
YouTubeTransformer,
|
||||
XLogPostTransformer,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import sanitizeScheme from "./sanitize-schema"
|
|||
const Style = dynamic(() => import("~/components/common/Style"))
|
||||
const Mention = dynamic(() => import("~/components/ui/Mention"))
|
||||
const Mermaid = dynamic(() => import("~/components/ui/Mermaid"))
|
||||
const Tweet = dynamic(() => import("~/components/ui/Tweet"))
|
||||
const GithubRepo = dynamic(() => import("~/components/ui/GithubRepo"))
|
||||
const XLogPost = dynamic(() => import("~/components/ui/XLogPost"))
|
||||
const APlayer = dynamic(() => import("~/components/ui/APlayer"))
|
||||
|
|
@ -233,6 +234,7 @@ export const renderPageContent = (
|
|||
mermaid: Mermaid,
|
||||
audio: APlayer,
|
||||
video: DPlayer,
|
||||
tweet: Tweet,
|
||||
"github-repo": GithubRepo,
|
||||
"xlog-post": XLogPost,
|
||||
style: Style,
|
||||
|
|
|
|||
Loading…
Reference in New Issue