diff --git a/src/components/common/PostCard.tsx b/src/components/common/PostCard.tsx
new file mode 100644
index 00000000..5a12fba2
--- /dev/null
+++ b/src/components/common/PostCard.tsx
@@ -0,0 +1,190 @@
+"use client"
+
+import { CharacterEntity } from "crossbell"
+import Link from "next/link"
+import { useRouter } from "next/navigation"
+import { memo } from "react"
+import reactStringReplace from "react-string-replace"
+
+import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
+import { Titles } from "~/components/common/Titles"
+import PostCover from "~/components/home/PostCover"
+import { Avatar } from "~/components/ui/Avatar"
+import { useDate } from "~/hooks/useDate"
+import { useIsMobileLayout } from "~/hooks/useMobileLayout"
+import { getSiteLink } from "~/lib/helpers"
+import { useTranslation } from "~/lib/i18n/client"
+import { ExpandedNote } from "~/lib/types"
+import { cn } from "~/lib/utils"
+
+const Card = ({
+ character,
+ post,
+ keyword,
+ comment,
+ createdAt,
+}: {
+ character?: CharacterEntity
+ post: ExpandedNote
+ keyword?: string
+ comment?: string
+ createdAt?: string
+}) => {
+ const router = useRouter()
+ const { t } = useTranslation("common")
+ const date = useDate()
+ const isMobileLayout = useIsMobileLayout()
+
+ return (
+
+
+
+
+ {comment && (
+
+
+ {comment}
+
+ )}
+
+ {post.metadata?.content?.title}
+
+ {!comment && (
+
+ {keyword
+ ? reactStringReplace(
+ post.metadata?.content?.summary || "",
+ keyword,
+ (match, i) => (
+
+ {match}
+
+ ),
+ )
+ : post.metadata?.content?.summary}
+ {post.metadata?.content?.summary && "..."}
+
+ )}
+
+
+ {!!post.metadata?.content?.tags?.[1] && (
+ {
+ e.preventDefault()
+ router.push(`/tag/${post.metadata?.content?.tags?.[1]}`)
+ }}
+ >
+
+ {post.metadata?.content?.tags?.[1]}
+
+ )}
+
+
+
+ {post.metadata?.content?.readingTime} {t("min")}
+
+
+ {!!post.stat?.viewDetailCount && (
+
+
+ {post.stat?.viewDetailCount}
+
+ )}
+
+
+
+ {
+ e.preventDefault()
+ window.open(
+ `${getSiteLink({
+ subdomain: character?.handle || "",
+ })}`,
+ )
+ }}
+ >
+
+
+
+
+ {character?.metadata?.content?.name || character?.handle}
+
+
+
+
+
·
+
+
+
+
+ )
+}
+
+const Post = ({ post, keyword }: { post: ExpandedNote; keyword?: string }) => {
+ let isComment
+ if (post.metadata?.content?.tags?.includes("comment") && post.toNote) {
+ isComment = true
+ }
+
+ return (
+
+ )
+}
+
+const PostCard = memo(Post)
+
+export default PostCard
diff --git a/src/components/home/HomeFeed.tsx b/src/components/home/HomeFeed.tsx
index 82022c1f..69cc4a12 100644
--- a/src/components/home/HomeFeed.tsx
+++ b/src/components/home/HomeFeed.tsx
@@ -1,205 +1,26 @@
"use client"
-import { CharacterEntity } from "crossbell"
-import Link from "next/link"
-import { useParams, useRouter, useSearchParams } from "next/navigation"
-import { memo, useEffect, useState } from "react"
-import reactStringReplace from "react-string-replace"
+import { useParams, useSearchParams } from "next/navigation"
+import { useEffect, useState } from "react"
import { VirtuosoGrid } from "react-virtuoso"
import { useAccountState, useConnectModal } from "@crossbell/connect-kit"
import { Switch } from "@headlessui/react"
-import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
-import { Titles } from "~/components/common/Titles"
-import PostCover from "~/components/home/PostCover"
-import { Avatar } from "~/components/ui/Avatar"
+import PostCard from "~/components/common/PostCard"
import { EmptyState } from "~/components/ui/EmptyState"
import { Skeleton } from "~/components/ui/Skeleton"
import { Tabs } from "~/components/ui/Tabs"
import { Tooltip } from "~/components/ui/Tooltip"
-import { useDate } from "~/hooks/useDate"
-import { useIsMobileLayout } from "~/hooks/useMobileLayout"
-import { getSiteLink } from "~/lib/helpers"
import { useTranslation } from "~/lib/i18n/client"
import { getStorage, setStorage } from "~/lib/storage"
import { ExpandedNote } from "~/lib/types"
-import { cn, getStringLength } from "~/lib/utils"
+import { getStringLength } from "~/lib/utils"
import type { FeedType, SearchType } from "~/models/home.model"
import { useGetFeed } from "~/queries/home"
import topics from "../../../data/topics.json"
-const PostCard = ({
- character,
- post,
- keyword,
- comment,
- createdAt,
-}: {
- character?: CharacterEntity
- post: ExpandedNote
- keyword?: string
- comment?: string
- createdAt?: string
-}) => {
- const router = useRouter()
- const { t } = useTranslation("common")
- const date = useDate()
- const isMobileLayout = useIsMobileLayout()
-
- return (
-
-
-
-
- {comment && (
-
-
- {comment}
-
- )}
-
- {post.metadata?.content?.title}
-
- {!comment && (
-
- {keyword
- ? reactStringReplace(
- post.metadata?.content?.summary || "",
- keyword,
- (match, i) => (
-
- {match}
-
- ),
- )
- : post.metadata?.content?.summary}
- {post.metadata?.content?.summary && "..."}
-
- )}
-
-
- {!!post.metadata?.content?.tags?.[1] && (
- {
- e.preventDefault()
- router.push(`/tag/${post.metadata?.content?.tags?.[1]}`)
- }}
- >
-
- {post.metadata?.content?.tags?.[1]}
-
- )}
-
-
-
- {post.metadata?.content?.readingTime} {t("min")}
-
-
- {!!post.stat?.viewDetailCount && (
-
-
- {post.stat?.viewDetailCount}
-
- )}
-
-
-
- {
- e.preventDefault()
- window.open(
- `${getSiteLink({
- subdomain: character?.handle || "",
- })}`,
- )
- }}
- >
-
-
-
-
- {character?.metadata?.content?.name || character?.handle}
-
-
-
-
-
·
-
-
-
-
- )
-}
-
-const Post = ({ post, keyword }: { post: ExpandedNote; keyword?: string }) => {
- let isComment
- if (post.metadata?.content?.tags?.includes("comment") && post.toNote) {
- isComment = true
- }
-
- return (
-
- )
-}
-
-const MemoedPost = memo(Post)
-
export const HomeFeed = ({ type }: { type?: FeedType }) => {
const { t } = useTranslation("common")
const searchParams = useSearchParams()
@@ -411,7 +232,7 @@ export const HomeFeed = ({ type }: { type?: FeedType }) => {
const post = feedInOne[index]
if (!post) return null
return (
-