feat: ssr for activities pages

This commit is contained in:
DIYgod 2023-07-08 23:09:43 +01:00
parent ba751181e7
commit cfa2c240f8
No known key found for this signature in database
10 changed files with 216 additions and 110 deletions

View File

@ -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>
)
}

View File

@ -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>
)
}

View File

@ -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

View File

@ -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>
)
}

View File

@ -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>
)
}

View File

@ -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

View File

@ -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 />
</>
)
}

View File

@ -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>

View File

@ -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" />

View File

@ -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,