feat: ssr for activities pages
This commit is contained in:
parent
ba751181e7
commit
cfa2c240f8
|
|
@ -1,12 +1,31 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `Hottest Activities - ${APP_NAME}`,
|
||||
}
|
||||
|
||||
export default async function HottestActivities() {
|
||||
return <HomeFeed type="hottest" />
|
||||
const queryClient = getQueryClient()
|
||||
await prefetchGetFeed(
|
||||
{
|
||||
type: "hottest",
|
||||
daysInterval: 7,
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
return (
|
||||
<Hydrate state={dehydratedState}>
|
||||
<HomeFeed type="hottest" />
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,30 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `Latest Activities - ${APP_NAME}`,
|
||||
}
|
||||
|
||||
export default async function LatestActivities() {
|
||||
return <HomeFeed type="latest" />
|
||||
const queryClient = getQueryClient()
|
||||
await prefetchGetFeed(
|
||||
{
|
||||
type: "latest",
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
return (
|
||||
<Hydrate state={dehydratedState}>
|
||||
<HomeFeed type="latest" />
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import { SearchInput } from "~/components/common/SearchInput"
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export function generateMetadata({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: {
|
||||
[key: string]: string | string[] | undefined
|
||||
}
|
||||
}): Metadata {
|
||||
return {
|
||||
title: `Search: ${searchParams.q} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
|
||||
async function Search({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
}) {
|
||||
const queryClient = getQueryClient()
|
||||
await prefetchGetFeed(
|
||||
{
|
||||
type: "search",
|
||||
searchKeyword: searchParams.q || undefined,
|
||||
searchType: "latest",
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
return (
|
||||
<Hydrate state={dehydratedState}>
|
||||
<SearchInput />
|
||||
<div className="mt-10">
|
||||
<HomeFeed type="search" />
|
||||
</div>
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
||||
export default Search
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
tag: string
|
||||
}
|
||||
}): Metadata {
|
||||
return {
|
||||
title: `Tag: ${params.tag} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Tag({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
tag: string
|
||||
}
|
||||
}) {
|
||||
params.tag = decodeURIComponent(params.tag)
|
||||
|
||||
const queryClient = getQueryClient()
|
||||
await prefetchGetFeed(
|
||||
{
|
||||
type: "tag",
|
||||
tag: params.tag,
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
return (
|
||||
<Hydrate state={dehydratedState}>
|
||||
<h2 className="text-3xl font-bold">Tag: {params.tag}</h2>
|
||||
<HomeFeed type="tag" />
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { getTranslation } from "~/lib/i18n"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
import topics from "../../../../../../data/topics.json"
|
||||
|
||||
|
|
@ -30,8 +34,21 @@ export default async function Topic({
|
|||
params.topic = decodeURIComponent(params.topic)
|
||||
const info = topics.find((t) => t.name === params.topic)
|
||||
|
||||
const queryClient = getQueryClient()
|
||||
await prefetchGetFeed(
|
||||
{
|
||||
type: "topic",
|
||||
topicIncludeKeywords: info?.includeKeywords,
|
||||
topicIncludeTags: info?.includeTags,
|
||||
noteIds: info?.notes,
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hydrate state={dehydratedState}>
|
||||
<div className="border rounded-xl px-5 py-6 mb-4 space-y-2 relative bg-zinc-50">
|
||||
<div className="text-2xl flex items-center font-bold">
|
||||
<i className="icon-[mingcute--hashtag-fill]" />
|
||||
|
|
@ -47,7 +64,7 @@ export default async function Topic({
|
|||
{t("Participate in Topic")}
|
||||
</Button> */}
|
||||
</div>
|
||||
<HomeFeed type="topic" noteIds={info?.notes} />
|
||||
</>
|
||||
<HomeFeed type="topic" />
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { SearchInput } from "~/components/common/SearchInput"
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { HomeSidebar } from "~/components/home/HomeSidebar"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
|
||||
export function generateMetadata({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: {
|
||||
[key: string]: string | string[] | undefined
|
||||
}
|
||||
}): Metadata {
|
||||
return {
|
||||
title: `Search: ${searchParams.q} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
|
||||
async function Search() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex-1 min-w-[300px]">
|
||||
<SearchInput />
|
||||
<div className="mt-10">
|
||||
<HomeFeed type="search" />
|
||||
</div>
|
||||
</div>
|
||||
<HomeSidebar hideSearch={true} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Search
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { HomeSidebar } from "~/components/home/HomeSidebar"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
|
||||
export function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
tag: string
|
||||
}
|
||||
}): Metadata {
|
||||
return {
|
||||
title: `Tag: ${params.tag} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
|
||||
export default function Tag({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
tag: string
|
||||
}
|
||||
}) {
|
||||
params.tag = decodeURIComponent(params.tag)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex-1 min-w-[300px]">
|
||||
<h2 className="text-3xl font-bold">Tag: {params.tag}</h2>
|
||||
<HomeFeed type="tag" />
|
||||
</div>
|
||||
<HomeSidebar />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import { cn } from "~/lib/utils"
|
|||
|
||||
import data from "../../../data/titles.json"
|
||||
import { Tooltip } from "../ui/Tooltip"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
|
||||
const icons: {
|
||||
[key: string]: {
|
||||
|
|
@ -38,11 +37,17 @@ export const Titles = ({ characterId }: { characterId?: number }) => {
|
|||
"inline-flex p-[1px] rounded-sm",
|
||||
)}
|
||||
>
|
||||
<UniLink href={title.link} className="inline-flex">
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
window.open(title.link)
|
||||
}}
|
||||
className="inline-flex"
|
||||
>
|
||||
<span className="text-white">
|
||||
<i className={cn(icons[title.name].icon, "text-[10px] block")} />
|
||||
</span>
|
||||
</UniLink>
|
||||
</span>
|
||||
</Tooltip>
|
||||
))}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -200,13 +200,7 @@ const Post = ({ post, keyword }: { post: ExpandedNote; keyword?: string }) => {
|
|||
|
||||
const MemoedPost = memo(Post)
|
||||
|
||||
export const HomeFeed = ({
|
||||
noteIds,
|
||||
type,
|
||||
}: {
|
||||
noteIds?: string[]
|
||||
type?: FeedType
|
||||
}) => {
|
||||
export const HomeFeed = ({ type }: { type?: FeedType }) => {
|
||||
const { t } = useTranslation("common")
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
|
|
@ -227,21 +221,47 @@ export const HomeFeed = ({
|
|||
params.topic = decodeURIComponent(params.topic)
|
||||
}
|
||||
|
||||
const feed = useGetFeed({
|
||||
let feedConfig: Parameters<typeof useGetFeed>[0] = {
|
||||
type,
|
||||
characterId: currentCharacterId,
|
||||
noteIds: noteIds,
|
||||
daysInterval: hotInterval,
|
||||
searchKeyword: searchParams?.get("q") || undefined,
|
||||
searchType,
|
||||
tag: decodeURIComponent(params?.tag),
|
||||
topicIncludeKeywords: params.topic
|
||||
? topics.find((t) => t.name === params.topic)?.includeKeywords
|
||||
: undefined,
|
||||
topicIncludeTags: params.topic
|
||||
? topics.find((t) => t.name === params.topic)?.includeTags
|
||||
: undefined,
|
||||
})
|
||||
}
|
||||
switch (type) {
|
||||
case "following":
|
||||
feedConfig = {
|
||||
type,
|
||||
characterId: currentCharacterId,
|
||||
}
|
||||
break
|
||||
case "topic":
|
||||
const info = topics.find((t) => t.name === params.topic)
|
||||
feedConfig = {
|
||||
type,
|
||||
topicIncludeKeywords: info?.includeKeywords,
|
||||
topicIncludeTags: info?.includeTags,
|
||||
noteIds: info?.notes,
|
||||
}
|
||||
break
|
||||
case "hottest":
|
||||
feedConfig = {
|
||||
type,
|
||||
daysInterval: hotInterval,
|
||||
}
|
||||
break
|
||||
case "search":
|
||||
feedConfig = {
|
||||
type,
|
||||
searchKeyword: searchParams?.get("q") || undefined,
|
||||
searchType,
|
||||
}
|
||||
break
|
||||
case "tag":
|
||||
feedConfig = {
|
||||
type,
|
||||
tag: decodeURIComponent(params?.tag),
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
const feed = useGetFeed(feedConfig)
|
||||
|
||||
const hasFiltering = type === "latest"
|
||||
|
||||
|
|
@ -287,7 +307,9 @@ export const HomeFeed = ({
|
|||
},
|
||||
]
|
||||
|
||||
const [feedInOne, setFeedInOne] = useState<ExpandedNote[]>([])
|
||||
const [feedInOne, setFeedInOne] = useState<ExpandedNote[]>(
|
||||
feed.data?.pages?.[0]?.list || [],
|
||||
)
|
||||
useEffect(() => {
|
||||
if (feed.data?.pages?.length) {
|
||||
setFeedInOne(
|
||||
|
|
@ -368,8 +390,9 @@ export const HomeFeed = ({
|
|||
) : !feed.data?.pages[0]?.count ? (
|
||||
<EmptyState />
|
||||
) : (
|
||||
<div className="xlog-posts my-8">
|
||||
<div className="xlog-posts my-8 min-h-[1177px]">
|
||||
<VirtuosoGrid
|
||||
initialItemCount={9}
|
||||
overscan={2604}
|
||||
endReached={() => feed.hasNextPage && feed.fetchNextPage()}
|
||||
useWindowScroll
|
||||
|
|
@ -413,7 +436,7 @@ const FeedSkeleton = () => {
|
|||
return (
|
||||
<Skeleton.Container
|
||||
count={9}
|
||||
className="grid gap-3 sm:gap-6 grid-cols-2 sm:grid-cols-3"
|
||||
className="grid gap-3 sm:gap-6 grid-cols-2 sm:grid-cols-3 my-8"
|
||||
>
|
||||
<div className="rounded-2xl border">
|
||||
<Skeleton.Rectangle className="h-auto rounded-t-2xl rounded-b-none w-full aspect-video border-b" />
|
||||
|
|
|
|||
|
|
@ -4,17 +4,12 @@ import { cacheGet } from "~/lib/redis.server"
|
|||
import * as homeModel from "~/models/home.model"
|
||||
|
||||
export const prefetchGetFeed = async (
|
||||
data: {
|
||||
type?: homeModel.FeedType
|
||||
characterId?: number
|
||||
limit?: number
|
||||
noteIds?: string[]
|
||||
},
|
||||
data: Parameters<typeof homeModel.getFeed>[0],
|
||||
queryClient: QueryClient,
|
||||
) => {
|
||||
const key = ["getFeed", data]
|
||||
await queryClient.prefetchInfiniteQuery({
|
||||
queryKey: ["getFeed", data],
|
||||
queryKey: key,
|
||||
queryFn: async ({ pageParam }) => {
|
||||
return cacheGet({
|
||||
key,
|
||||
|
|
|
|||
Loading…
Reference in New Issue