From b51d9afc76e5cf9c8e2ec9f366dda9eade404eab Mon Sep 17 00:00:00 2001 From: Caspian Date: Wed, 20 Dec 2023 13:12:49 +0000 Subject: [PATCH 1/2] feat: Add shorts embedding. --- src/components/home/PostCover.tsx | 7 +- src/components/ui/XLogShorts.tsx | 127 ++++++++++++++++++ src/markdown/embed-transformers/XLogShorts.ts | 54 ++++++++ src/markdown/embed-transformers/index.ts | 2 + src/markdown/index.ts | 2 + 5 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 src/components/ui/XLogShorts.tsx create mode 100644 src/markdown/embed-transformers/XLogShorts.ts diff --git a/src/components/home/PostCover.tsx b/src/components/home/PostCover.tsx index ccd512fb..e4bbf62e 100644 --- a/src/components/home/PostCover.tsx +++ b/src/components/home/PostCover.tsx @@ -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({
cover = ({ 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
Loading...
+ + return ( +
+
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", + )} + > + +
+
+
+ {page.data?.metadata?.content?.title && ( +
+ {page.data?.metadata?.content?.title} +
+ )} +
+

+ {page.data?.metadata?.content?.content} +

+
+
+ +
+ + + + + + +
+ + {site.data?.metadata?.content?.name} + + {site.data?.handle && ( + + {"@" + site.data?.handle} + + )} +
+ + +
+
+
+
+ ) +} + +export default XLogShorts diff --git a/src/markdown/embed-transformers/XLogShorts.ts b/src/markdown/embed-transformers/XLogShorts.ts new file mode 100644 index 00000000..df4b08c9 --- /dev/null +++ b/src/markdown/embed-transformers/XLogShorts.ts @@ -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 `` + }, +} diff --git a/src/markdown/embed-transformers/index.ts b/src/markdown/embed-transformers/index.ts index cc7bf5db..af1ab5ff 100644 --- a/src/markdown/embed-transformers/index.ts +++ b/src/markdown/embed-transformers/index.ts @@ -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, diff --git a/src/markdown/index.ts b/src/markdown/index.ts index 9d652412..599dfd83 100644 --- a/src/markdown/index.ts +++ b/src/markdown/index.ts @@ -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, }) From 8da18cc360187345827ada16caa233ad234a49e1 Mon Sep 17 00:00:00 2001 From: Caspian Date: Wed, 20 Dec 2023 15:36:00 +0000 Subject: [PATCH 2/2] Enable easy linking of short-form content and ensure navigation to this content is possible when clicking on the short-form content card. --- src/components/ui/XLogShorts.tsx | 44 +++++++++--- src/markdown/embed-transformers/XLogShorts.ts | 71 ++++++++----------- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/components/ui/XLogShorts.tsx b/src/components/ui/XLogShorts.tsx index 1ebb9fbf..6b1957d5 100644 --- a/src/components/ui/XLogShorts.tsx +++ b/src/components/ui/XLogShorts.tsx @@ -11,8 +11,17 @@ import { useGetSite } from "~/queries/site" import { Time } from "../common/Time" import PostCover from "../home/PostCover" import { Avatar } from "./Avatar" +import { UniLink } from "./UniLink" + +interface Props { + slug: string + handle: string + url: string +} + +const XLogShorts: FC = ({ slug, handle, url }) => { + const [isExpanded, setIsExpanded] = React.useState(false) -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) @@ -30,21 +39,31 @@ const XLogShorts: FC<{ slug: string; handle: string }> = ({ slug, handle }) => { .filter(Boolean) const isMobile = isMobileDevice() - const [isExpanded, setIsExpanded] = React.useState(false) + const isShort = !!page.data?.metadata?.content?.tags?.includes("short") - const toggleExpand = useCallback(() => { - setIsExpanded((prev) => !prev) - }, []) + const toggleExpand = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + setIsExpanded((prev) => !prev) + }, + [], + ) + + const handleRedirect = useCallback(() => { + window.open(url, "_blank") + }, [url]) if (page.isLoading) return
Loading...
+ if (!isShort) return {url} + return (
= ({ slug, handle }) => { 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", + "transition-all duration-500 ease-in-out", )} > = ({ slug, handle }) => {
)}
-

+

{page.data?.metadata?.content?.content}

@@ -111,11 +134,12 @@ const XLogShorts: FC<{ slug: string; handle: string }> = ({ slug, handle }) => {
diff --git a/src/markdown/embed-transformers/XLogShorts.ts b/src/markdown/embed-transformers/XLogShorts.ts index df4b08c9..02d9d674 100644 --- a/src/markdown/embed-transformers/XLogShorts.ts +++ b/src/markdown/embed-transformers/XLogShorts.ts @@ -1,54 +1,41 @@ import { match } from "path-to-regexp" import type { Transformer } from "../rehype-embed" -import { isHostIncludes } from "./utils" + +function getParamsFromShortsURL(url: URL) { + let slug = "" + let handle = "" + + // 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 undefined + + slug = matched?.params.slug || "" + // @ts-ignore + handle = matched?.params.handle || "" + } + + return { slug, handle } +} 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) + return !!getParamsFromShortsURL(url) }, getHTML(url) { - let slug = "" - let handle = "" + const { slug, handle } = getParamsFromShortsURL(url)! - // 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 `` + return `` }, }