* refactor: unify loading component (#1184)

* chore: move themeColor to viewport (#1185)

* fix: i18n key word (#1190)

* feat: make xlogshorts more general (#1192)

* fix: allow magnet protocol in href (#1193)

* fix: allow magnet protocol in href

* chore: update

* chore: add ed2k

* docs: update required node version (#1194)

* fix: replace div with a tag for better SEO.

* fix: can't scroll to top when browsing post (#1197)

---------

Co-authored-by: Stephen Zhou <hi@hyoban.cc>
Co-authored-by: XLor <yjl9903@onekuma.cn>
This commit is contained in:
Caspian Zhao 2023-12-25 11:29:24 +00:00 committed by GitHub
parent 758add612a
commit c0b89dedce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 252 additions and 268 deletions

View File

@ -10,7 +10,7 @@ https://docs.github.com/en/get-started/quickstart/fork-a-repo
## Requirements
- [Node.js](https://nodejs.org). Version needs to be >=15.
- [Node.js](https://nodejs.org) 18.17 or later.
- [pnpm](https://pnpm.io/) package manager.
- [Docker](https://www.docker.com/) for running the cache database.

View File

@ -75,6 +75,7 @@
"clsx": "2.0.0",
"crossbell": "1.10.1",
"dayjs": "1.11.10",
"deepmerge": "^4.3.1",
"dotenv": "16.3.1",
"emoji-mart": "5.5.2",
"fast-average-color": "9.4.0",
@ -210,7 +211,7 @@
"typescript": "5.3.3"
},
"engines": {
"node": ">=15"
"node": ">=18.17.0"
},
"nextBundleAnalysis": {
"budget": 358400,

View File

@ -140,6 +140,9 @@ dependencies:
dayjs:
specifier: 1.11.10
version: 1.11.10
deepmerge:
specifier: ^4.3.1
version: 4.3.1
dotenv:
specifier: 16.3.1
version: 16.3.1
@ -6430,6 +6433,11 @@ packages:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
/deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
dev: false
/define-data-property@1.1.0:
resolution: {integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==}
engines: {node: '>= 0.4'}

View File

@ -6,6 +6,7 @@ import { useEffect } from "react"
import { Virtuoso } from "react-virtuoso"
import { CommentItem } from "~/components/common/CommentItem"
import { Loading } from "~/components/common/Loading"
import { DashboardMain } from "~/components/dashboard/DashboardMain"
import { UniLink } from "~/components/ui/UniLink"
import { getSiteLink } from "~/lib/helpers"
@ -65,7 +66,7 @@ export default function CommentsPage() {
comments.hasNextPage && comments.fetchNextPage()
}
components={{
Footer: comments.isLoading ? Loader : undefined,
Footer: comments.isLoading ? Loading : undefined,
}}
itemContent={(_, p) =>
p?.list?.map((comment, idx) => {
@ -125,15 +126,3 @@ export default function CommentsPage() {
</DashboardMain>
)
}
const Loader = () => {
const t = useTranslations()
return (
<div
className="relative mt-4 w-full text-sm text-center py-4"
key={"loading"}
>
{t("Loading")}...
</div>
)
}

View File

@ -8,6 +8,7 @@ import toast from "react-hot-toast"
import { useQueryClient } from "@tanstack/react-query"
import { Loading } from "~/components/common/Loading"
import { DashboardMain } from "~/components/dashboard/DashboardMain"
import EditorAutofill from "~/components/dashboard/editor-properties/EditorAutofill"
import EditorCover from "~/components/dashboard/editor-properties/EditorCover"
@ -247,9 +248,7 @@ export default function PortfolioEditor() {
<>
<DashboardMain className="max-w-screen-lg" title="Edit portfolio">
{page.isLoading ? (
<div className="flex justify-center items-center min-h-[300px]">
{t("Loading")}...
</div>
<Loading className="min-h-[300px]" />
) : (
<>
<EditorExtraProperties updateValue={updateValue} />

View File

@ -9,6 +9,7 @@ import toast from "react-hot-toast"
import type { EditorView } from "@codemirror/view"
import { useQueryClient } from "@tanstack/react-query"
import { Loading } from "~/components/common/Loading"
import { DashboardMain } from "~/components/dashboard/DashboardMain"
import DualColumnEditor from "~/components/dashboard/DualColumnEditor"
import EditorCover from "~/components/dashboard/editor-properties/EditorCover"
@ -385,9 +386,7 @@ export default function PostEditor() {
<>
<DashboardMain fullWidth>
{page.isLoading ? (
<div className="flex justify-center items-center min-h-[300px]">
{t("Loading")}...
</div>
<Loading className="min-h-[300px]" />
) : (
<>
<header

View File

@ -8,6 +8,7 @@ import toast from "react-hot-toast"
import { useQueryClient } from "@tanstack/react-query"
import { Loading } from "~/components/common/Loading"
import { DashboardMain } from "~/components/dashboard/DashboardMain"
import EditorContent from "~/components/dashboard/editor-properties/EditorContent"
import EditorImages from "~/components/dashboard/editor-properties/EditorImages"
@ -249,9 +250,7 @@ export default function ShortEditor() {
<>
<DashboardMain className="max-w-screen-lg" title="Edit short">
{page.isLoading ? (
<div className="flex justify-center items-center min-h-[300px]">
{t("Loading")}...
</div>
<Loading className="min-h-[300px]" />
) : (
<>
<EditorExtraProperties updateValue={updateValue} />

View File

@ -16,6 +16,7 @@ import {
} from "@crossbell/notification"
import { ConnectButton } from "~/components/common/ConnectButton"
import { Loading } from "~/components/common/Loading"
import { Logo } from "~/components/common/Logo"
import { DashboardTopbar } from "~/components/dashboard/DashboardTopbar"
import { Avatar } from "~/components/ui/Avatar"
@ -444,8 +445,6 @@ export default function DashboardLayout({
</div>
)
) : (
<div className="w-screen h-screen flex justify-center items-center">
Loading...
</div>
<Loading className="w-screen h-screen" />
)
}

View File

@ -157,7 +157,7 @@ export default function SubdomainIndex() {
</UniLink>
</li>
</ul>
{t.rich("hello.tips", {
{t.rich("hello.community", {
p: (chunks) => <p>{chunks}</p>,
})}
<ul

View File

@ -1,12 +1,12 @@
import { Loading } from "~/components/common/Loading"
import PageComponent from "./page-component"
export default function Dashboard() {
return (
<>
<PageComponent />
<div className="flex items-center justify-center w-full h-60">
Loading...
</div>
<Loading className="h-60" />
</>
)
}

View File

@ -1,3 +1,11 @@
import "@crossbell/connect-kit/colors.css"
import "@mantine/core/styles.css"
import "remark-github-alerts/styles/github-colors-light.css"
import "remark-github-alerts/styles/github-colors-dark-class.css"
import "remark-github-alerts/styles/github-base.css"
import "~/css/main.css"
import { Viewport } from "next"
import { NextIntlClientProvider } from "next-intl"
import { getMessages, unstable_setRequestLocale } from "next-intl/server"
import { headers } from "next/headers"
@ -17,18 +25,14 @@ import {
UMAMI_SCRIPT,
} from "~/lib/env"
import { getColorScheme } from "~/lib/get-color-scheme"
import { withHrefLang } from "~/lib/with-hreflang"
import { ColorSchemeInjector } from "./ColorSchemeInjector"
import Providers, { mantineDefaultColorScheme, mantineTheme } from "./providers"
import "@crossbell/connect-kit/colors.css"
import "@mantine/core/styles.css"
import "remark-github-alerts/styles/github-colors-light.css"
import "remark-github-alerts/styles/github-colors-dark-class.css"
import "remark-github-alerts/styles/github-base.css"
import "~/css/main.css"
import { withHrefLang } from "~/lib/with-hreflang"
export const viewport: Viewport = {
themeColor: "#ffffff",
}
export const generateMetadata = withHrefLang(async () => ({
title: `${APP_NAME} - ${APP_SLOGAN}`,
@ -44,7 +48,6 @@ export const generateMetadata = withHrefLang(async () => ({
"dapp",
"crypto",
],
themeColor: "#ffffff",
alternates: {
types: {
"application/rss+xml": [

View File

@ -7,6 +7,7 @@ import { ExpandedCharacter } from "~/lib/types"
import { Button } from "../ui/Button"
import CharacterListItem from "./CharacterListItem"
import { Loading } from "./Loading"
import { PortalProvider } from "./Portal"
export const CharacterList = ({
@ -57,7 +58,7 @@ export const CharacterList = ({
className="max-h-screen"
endReached={() => hasMore && loadMore()}
components={{
Footer: hasMore ? Loader : undefined,
Footer: hasMore ? Loading : undefined,
}}
data={flattenList}
itemContent={(index, sub) => {
@ -89,12 +90,3 @@ export const CharacterList = ({
</Modal>
)
}
const Loader = () => {
const t = useTranslations()
return (
<div className="text-sm py-3 text-center" key={0}>
{t("Loading")} ...
</div>
)
}

View File

@ -11,6 +11,8 @@ import { ExpandedNote } from "~/lib/types"
import { cn } from "~/lib/utils"
import { useGetComments } from "~/queries/page"
import { Loading } from "./Loading"
const Comment = ({
page,
className,
@ -64,7 +66,7 @@ const Comment = ({
data={data}
endReached={() => comments.hasNextPage && comments.fetchNextPage()}
components={{
Footer: comments.isLoading ? Loader : undefined,
Footer: comments.isLoading ? Loading : undefined,
}}
totalListHeightChanged={(height) => {
if (fixHeight && height > 0) {
@ -89,23 +91,7 @@ const Comment = ({
))
}
></Virtuoso>
{comments.isLoading && (
<div className="relative mt-4 w-full text-sm text-center py-4">
{t("Loading")}...
</div>
)}
</div>
)
}
const Loader = () => {
const t = useTranslations()
return (
<div
className="relative mt-4 w-full text-sm text-center py-4"
key={"loading"}
>
{t("Loading")}...
{comments.isLoading && <Loading />}
</div>
)
}

View File

@ -0,0 +1,23 @@
import { useTranslations } from "next-intl"
import { cn } from "~/lib/utils"
export const Loading = ({
className,
}: {
className?: string
context?: unknown
}) => {
const t = useTranslations()
return (
<div
className={cn(
"flex gap-2 justify-center items-center w-full my-6",
className,
)}
>
<div className="i-mingcute-loading-3-fill animate-spin text-xl"></div>
{t("Loading")}...
</div>
)
}

View File

@ -17,6 +17,7 @@ import { ModalContentProps, useModalStack } from "../ui/ModalStack"
import { Tabs } from "../ui/Tabs"
import { UniLink } from "../ui/UniLink"
import { CharacterFloatCard } from "./CharacterFloatCard"
import { Loading } from "./Loading"
export const usePatronModal = () => {
const { present } = useModalStack()
@ -179,7 +180,7 @@ const PatronModal = ({
</div>
<div className="text-zinc-500 text-sm mt-2">
{tips.isLoading ? (
"Loading..."
<Loading />
) : tips.data?.pages?.[0]?.list?.length ? (
<>
<ul className="flex items-center justify-center">

View File

@ -64,7 +64,7 @@ const Card = ({
? externalLink || ""
: `${linkPrefix || ""}/${post.metadata?.content?.slug}${queryString}`
}
scroll={false}
scroll={!linkPrefix}
className={cn(
"xlog-post rounded-2xl flex flex-col items-center group relative",
isShort

View File

@ -13,15 +13,13 @@ import { useUploadFile } from "~/hooks/useUploadFile"
import { cn } from "~/lib/utils"
import { Rendered, renderPageContent } from "~/markdown"
import { Loading } from "../common/Loading"
const DynamicCodeMirrorEditor = dynamic(
() => import("~/components/ui/CodeMirror"),
{
ssr: false,
loading: () => (
<div className="flex-1 h-12 flex items-center justify-center">
Loading...
</div>
),
loading: () => <Loading className="flex-1 h-12" />,
},
)
@ -29,11 +27,7 @@ const DynamicPageContent = dynamic(
() => import("~/components/common/PageContent"),
{
ssr: false,
loading: () => (
<div className="flex-1 h-12 flex items-center justify-center">
Loading...
</div>
),
loading: () => <Loading className="flex-1 h-12" />,
},
)

View File

@ -25,6 +25,7 @@ import { cn } from "~/lib/utils"
import { useGetPagesBySite, usePinnedPage } from "~/queries/page"
import { useGetSite } from "~/queries/site"
import { Loading } from "../common/Loading"
import { PlatformsSyncMap } from "../site/Platform"
import { Button } from "../ui/Button"
import { EmptyState } from "../ui/EmptyState"
@ -317,7 +318,7 @@ export const PagesManager = ({ type }: { type: NoteType }) => {
)}
<div className="-mt-3">
{pages.isLoading && <p className="py-4 px-3">{t("Loading")}...</p>}
{pages.isLoading && <Loading />}
{!pages.isLoading && !pages.data?.pages?.[0].count && (
<EmptyState resource={type} />
)}

View File

@ -8,6 +8,7 @@ import { VirtuosoGrid } from "react-virtuoso"
import { useAccountState, useConnectModal } from "@crossbell/connect-kit"
import { Switch } from "@headlessui/react"
import { Loading } from "~/components/common/Loading"
import PostCard from "~/components/common/PostCard"
import { EmptyState } from "~/components/ui/EmptyState"
import { Skeleton } from "~/components/ui/Skeleton"
@ -245,7 +246,7 @@ export const HomeFeed = ({ type }: { type?: FeedType }) => {
}}
></VirtuosoGrid>
{feed.isFetching && feed.hasNextPage && isMounted && <Loader />}
{feed.isFetching && feed.hasNextPage && isMounted && <Loading />}
</div>
)}
</div>
@ -253,18 +254,6 @@ export const HomeFeed = ({ type }: { type?: FeedType }) => {
)
}
const Loader = () => {
const t = useTranslations()
return (
<div
className="relative w-full text-sm text-center py-4 mt-12"
key={"loading"}
>
{t("Loading")} ...
</div>
)
}
const FeedSkeleton = () => {
return (
<Skeleton.Container

View File

@ -12,6 +12,8 @@ import { getSiteLink } from "~/lib/helpers"
import { cn } from "~/lib/utils"
import { useGetShowcase } from "~/queries/home"
import { Loading } from "../common/Loading"
export function ShowCase() {
const showcaseSites = useGetShowcase()
const [showcaseMore, setShowcaseMore] = useState(false)
@ -28,7 +30,7 @@ export function ShowCase() {
.map(Number)}
siteIds={showcaseSites.data?.map((s: { handle: string }) => s.handle)}
/>
{showcaseSites.isLoading && <p className="mt-10">{t("Loading")}...</p>}
{showcaseSites.isLoading && <Loading className="mt-10" />}
<ul
className={`pt-10 grid grid-cols-2 md:grid-cols-3 gap-10 overflow-y-hidden relative text-left ${
showcaseMore ? "" : "max-h-[540px]"

View File

@ -8,6 +8,8 @@ import { NoteType, PagesSortTypes, PageVisibilityEnum } from "~/lib/types"
import { useGetPagesBySiteLite, usePinnedPage } from "~/queries/page"
import { useGetSite } from "~/queries/site"
import { Loading } from "../common/Loading"
export default function SiteHome({
handle,
type,
@ -70,7 +72,7 @@ export default function SiteHome({
return (
<>
<Tabs items={tabs} className="mb-6" type="rounded" />
{!posts.data?.pages?.length && posts.isLoading && <>Loading...</>}
{!posts.data?.pages?.length && posts.isLoading && <Loading />}
<PostList
posts={posts}
pinnedNoteId={pinnedPage.noteId}

View File

@ -7,6 +7,8 @@ import PostList from "~/components/site/PostList"
import { useGetSearchPagesBySite } from "~/queries/page"
import { useGetSite } from "~/queries/site"
import { Loading } from "../common/Loading"
export const SiteSearch = () => {
const t = useTranslations()
@ -24,7 +26,7 @@ export const SiteSearch = () => {
<h2 className="mb-8 mt-5 text-zinc-500">
{posts.data?.pages?.[0].count || "0"} {t("results")}
</h2>
{posts.isLoading && <>{t("Loading")}...</>}
{posts.isLoading && <Loading />}
<PostList posts={posts} keyword={keyword} />
</>
)

View File

@ -50,13 +50,7 @@ import { useIsDark } from "~/hooks/useDarkMode"
import { useGetState } from "~/hooks/useGetState"
import { useIsUnmounted } from "~/hooks/useLifecycle"
const LoadingHolder = () => {
return (
<div className="flex-1 h-12 flex items-center justify-center">
Loading...
</div>
)
}
import { Loading } from "../common/Loading"
interface XLogCodeMirrorEditorProps {
value?: string
@ -229,7 +223,7 @@ const CodeMirrorEditor = forwardRef<
(props.LoadingComponent ? (
createElement(props.LoadingComponent)
) : (
<LoadingHolder />
<Loading className="flex-1 h-12" />
))}
</>
)

View File

@ -0,0 +1,151 @@
"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 { Loading } from "../common/Loading"
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 XLogPost: FC<Props> = ({ slug, handle, url }) => {
const [isExpanded, setIsExpanded] = React.useState(false)
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 isMobile = isMobileDevice()
const images = page.data?.metadata.content.images || []
const isShort = !!page.data?.metadata?.content?.tags?.includes("short")
const preventNavigate = useCallback(
(e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.stopPropagation()
e.preventDefault()
},
[],
)
const toggleExpand = useCallback(
(e: React.MouseEvent<HTMLElement, MouseEvent>) => {
preventNavigate(e)
setIsExpanded((prev) => !prev)
},
[preventNavigate],
)
if (page.isLoading) return <Loading />
return (
<UniLink href={url} className="!no-underline !text-inherit">
<div
className={cn(
"flex flex-col md:flex-row w-full bg-zinc-50 rounded-xl p-4 gap-x-4 hover:cursor-pointer my-2 transition-all duration-500 ease-in-out",
isExpanded ? "sm:h-[350px]" : "sm:h-[150px]",
)}
>
<div
onClick={preventNavigate}
className={cn(
"w-full h-full mb-2 sm:mb-0 align-middle flex items-center justify-center transition-all duration-500 ease-in-out",
isExpanded ? "sm:w-[350px]" : "sm:w-[150px]",
)}
>
<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={
isExpanded || isMobile ? "line-clamp-6" : "line-clamp-1"
}
>
{isShort
? page.data?.metadata?.content?.content
: page.data?.metadata?.content?.summary}
</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 hover:cursor-pointer",
isExpanded ? "rotate-180" : "rotate-0",
"transition-transform duration-500 ease-in-out",
"hidden sm:inline-block",
)}
onClick={toggleExpand}
/>
</span>
</div>
</div>
</div>
</UniLink>
)
}
export default XLogPost

View File

@ -1,151 +0,0 @@
"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"
import { UniLink } from "./UniLink"
interface Props {
slug: string
handle: string
url: string
}
const XLogShorts: FC<Props> = ({ slug, handle, url }) => {
const [isExpanded, setIsExpanded] = React.useState(false)
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 isShort = !!page.data?.metadata?.content?.tags?.includes("short")
const toggleExpand = useCallback(
(e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.stopPropagation()
setIsExpanded((prev) => !prev)
},
[],
)
const handleRedirect = useCallback(() => {
window.open(url, "_blank")
}, [url])
if (page.isLoading) return <div>Loading...</div>
if (!isShort) return <UniLink href={url}>{url}</UniLink>
return (
<div
onClick={handleRedirect}
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-all 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-all 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={
isExpanded || isMobile ? "line-clamp-6" : "line-clamp-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 hover:cursor-pointer",
isExpanded ? "rotate-180" : "rotate-0",
"transition-transform duration-500 ease-in-out",
"hidden sm:inline-block",
)}
onClick={toggleExpand}
/>
</span>
</div>
</div>
</div>
)
}
export default XLogShorts

View File

@ -28,7 +28,7 @@ function getParamsFromShortsURL(url: URL) {
return { slug, handle }
}
export const XLogShortsTransformer: Transformer = {
export const XLogPostTransformer: Transformer = {
name: "XLogShorts",
shouldTransform(url) {
return !!getParamsFromShortsURL(url)
@ -36,6 +36,6 @@ export const XLogShortsTransformer: Transformer = {
getHTML(url) {
const { slug, handle } = getParamsFromShortsURL(url)!
return `<xlog-shorts slug="${slug}" handle="${handle}" url="${url.toString()}"/>`
return `<xlog-post slug="${slug}" handle="${handle}" url="${url.toString()}"/>`
},
}

View File

@ -4,14 +4,14 @@ import { CodeSandboxTransformer } from "./CodeSandBox"
import { GitHubRepoTransformer } from "./GitHub"
import { NetEaseMusicTransformer } from "./NetEaseMusic"
import { SpotifyTransformer } from "./Spotify"
import { XLogShortsTransformer } from "./XLogShorts"
import { XLogPostTransformer } from "./XLogPost"
import { YouTubeTransformer } from "./YouTube"
export const transformers: Transformer[] = [
CodeSandboxTransformer,
GitHubRepoTransformer,
YouTubeTransformer,
XLogShortsTransformer,
XLogPostTransformer,
NetEaseMusicTransformer,
BilibiliTransformer,
SpotifyTransformer,

View File

@ -57,7 +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 XLogPost = dynamic(() => import("~/components/ui/XLogPost"))
const APlayer = dynamic(() => import("~/components/ui/APlayer"))
const DPlayer = dynamic(() => import("~/components/ui/DPlayer"))
@ -234,7 +234,7 @@ export const renderPageContent = (
audio: APlayer,
video: DPlayer,
"github-repo": GithubRepo,
"xlog-shorts": XLogShorts,
"xlog-post": XLogPost,
style: Style,
} as any,
})

View File

@ -1,9 +1,8 @@
import deepmerge from "deepmerge"
import { defaultSchema } from "rehype-sanitize"
const scheme = {
...defaultSchema,
const scheme = deepmerge(defaultSchema, {
tagNames: [
...(defaultSchema.tagNames || []),
"video",
"iframe",
"style",
@ -18,8 +17,7 @@ const scheme = {
...["svg", "path", "circle"],
],
attributes: {
...defaultSchema.attributes,
"*": [...(defaultSchema.attributes?.["*"] || []), "className", "style"],
"*": ["className", "style"],
video: ["src", "controls", "loop", "muted", "autoPlay", "playsInline"],
audio: [
"src",
@ -48,6 +46,9 @@ const scheme = {
path: ["d", "fill"],
circle: ["cx", "cy", "r", "fill"],
},
}
protocols: {
href: ["magnet", "ed2k"],
},
})
export default scheme