feat: show shorts in home activities
This commit is contained in:
parent
5ca4d3fb7f
commit
a459ada1d3
|
|
@ -0,0 +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: `Shorts - ${APP_NAME}`,
|
||||
}
|
||||
|
||||
export default async function LatestActivities() {
|
||||
const queryClient = getQueryClient()
|
||||
await prefetchGetFeed(
|
||||
{
|
||||
type: "shorts",
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
return (
|
||||
<Hydrate state={dehydratedState}>
|
||||
<HomeFeed type="shorts" />
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import { notFound } from "next/navigation"
|
|||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import PageContent from "~/components/common/PageContent"
|
||||
import PostCover from "~/components/home/PostCover"
|
||||
import PostModal from "~/components/home/PostModal"
|
||||
import { PostFooter } from "~/components/site/PostFooter"
|
||||
import PostMeta from "~/components/site/PostMeta"
|
||||
|
|
@ -56,6 +57,12 @@ export default async function SiteModal({
|
|||
}
|
||||
const onlyContent = isOnlyContent()
|
||||
|
||||
const type = page?.metadata?.content?.tags?.[0]
|
||||
const images = page.metadata?.content?.attachments
|
||||
?.filter((attachment) => attachment.name === "image")
|
||||
.map((img) => img.address || "")
|
||||
.filter(Boolean)
|
||||
|
||||
return (
|
||||
<PostModal handle={site?.handle}>
|
||||
<div className="pb-16">
|
||||
|
|
@ -71,36 +78,57 @@ export default async function SiteModal({
|
|||
)}
|
||||
>
|
||||
<article>
|
||||
<div>
|
||||
{page?.metadata?.content?.tags?.includes("post") ? (
|
||||
<h2 className="xlog-post-title text-4xl font-bold leading-tight text-center">
|
||||
{page.metadata?.content?.title}
|
||||
</h2>
|
||||
) : (
|
||||
<h2 className="xlog-post-title text-xl font-bold page-title text-center">
|
||||
{page?.metadata?.content?.title}
|
||||
</h2>
|
||||
)}
|
||||
{page?.metadata?.content?.tags?.includes("post") && (
|
||||
<PostMeta
|
||||
{type === "short" ? (
|
||||
<div className="space-y-3">
|
||||
<PostCover
|
||||
uniqueKey={`short-${page.characterId}-${page.noteId}`}
|
||||
images={images}
|
||||
title={page.metadata?.content?.title}
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
{page?.metadata?.content?.title && (
|
||||
<h2 className="xlog-post-title text-xl font-bold page-title text-center">
|
||||
{page?.metadata?.content?.title}
|
||||
</h2>
|
||||
)}
|
||||
<div className="xlog-post-content prose">
|
||||
{page?.metadata?.content?.content}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
{page?.metadata?.content?.tags?.includes("post") ? (
|
||||
<h2 className="xlog-post-title text-4xl font-bold leading-tight text-center">
|
||||
{page.metadata?.content?.title}
|
||||
</h2>
|
||||
) : (
|
||||
<h2 className="xlog-post-title text-xl font-bold page-title text-center">
|
||||
{page?.metadata?.content?.title}
|
||||
</h2>
|
||||
)}
|
||||
{page?.metadata?.content?.tags?.includes("post") && (
|
||||
<PostMeta
|
||||
page={page}
|
||||
site={site}
|
||||
summary={summary}
|
||||
translated={{
|
||||
"AI-generated summary": t("AI-generated summary"),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<PageContent
|
||||
className="mt-10"
|
||||
content={page?.metadata?.content?.content}
|
||||
toc={false}
|
||||
page={page}
|
||||
site={site}
|
||||
summary={summary}
|
||||
translated={{
|
||||
"AI-generated summary": t("AI-generated summary"),
|
||||
}}
|
||||
withActions={false}
|
||||
onlyContent={onlyContent}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<PageContent
|
||||
className="mt-10"
|
||||
content={page?.metadata?.content?.content}
|
||||
toc={false}
|
||||
page={page}
|
||||
site={site}
|
||||
withActions={false}
|
||||
onlyContent={onlyContent}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</article>
|
||||
<Hydrate state={dehydratedState}>
|
||||
{page?.metadata && (
|
||||
|
|
|
|||
|
|
@ -82,13 +82,15 @@ const Card = ({
|
|||
className={cn(
|
||||
"px-3 py-2 w-full min-w-0 flex flex-col text-sm",
|
||||
isShort
|
||||
? "h-auto sm:h-[82px]"
|
||||
: "space-y-2 sm:px-5 sm:py-4 h-auto sm:h-[166px]",
|
||||
? "space-y-2 h-auto sm:h-[84px]" // 8 * 2 + 8 + 40 + 20
|
||||
: "space-y-2 sm:px-5 sm:py-4 h-auto sm:h-[163px]", // 16 * 2 + 8 * 2 + 75 + 20 + 20
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
isShort ? "line-clamp-2" : "space-y-2 line-clamp-3 h-[76px]",
|
||||
isShort
|
||||
? "line-clamp-2 max-h-10"
|
||||
: "space-y-2 line-clamp-3 h-[75px]",
|
||||
)}
|
||||
>
|
||||
{comment && (
|
||||
|
|
@ -133,95 +135,93 @@ const Card = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="xlog-post-meta text-zinc-400 flex items-center text-[13px] h-[26px] truncate space-x-2">
|
||||
{isPortfolio ? (
|
||||
<>
|
||||
<Tooltip
|
||||
label={`${platform?.name || platform}`}
|
||||
className="text-sm"
|
||||
>
|
||||
<span className="inline-flex items-center space-x-[6px]">
|
||||
{platform?.icon && (
|
||||
<Image
|
||||
src={platform?.icon}
|
||||
alt={platform?.name}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
)}
|
||||
<span>{t("Portfolio")}</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{!!post.stat?.portfolio?.videoViewsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--youtube-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.videoViewsCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.audoListensCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--headphone-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.audoListensCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.projectStarsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--star-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.projectStarsCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.textViewsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--eye-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat.portfolio.textViewsCount} />
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.commentsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--comment-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat.portfolio.commentsCount} />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!!post.metadata?.content?.tags?.[1] && (
|
||||
<span
|
||||
className="xlog-post-tags hover:text-zinc-600 hover:bg-zinc-200 border transition-colors text-zinc-500 inline-flex items-center bg-zinc-100 rounded-full px-2 py-[1.5px] truncate text-xs sm:text-[13px]"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
router.push(`/tag/${post.metadata?.content?.tags?.[1]}`)
|
||||
}}
|
||||
{!isShort && (
|
||||
<div className="xlog-post-meta text-zinc-400 flex items-center text-[13px] truncate space-x-2">
|
||||
{isPortfolio ? (
|
||||
<>
|
||||
<Tooltip
|
||||
label={`${platform?.name || platform}`}
|
||||
className="text-sm"
|
||||
>
|
||||
<i className="icon-[mingcute--tag-line] mr-[2px]" />
|
||||
{post.metadata?.content?.tags?.[1]}
|
||||
</span>
|
||||
)}
|
||||
{isShort && !!post.stat?.viewCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--eye-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat?.viewCount} />
|
||||
</span>
|
||||
)}
|
||||
{!isShort && !!post.stat?.viewDetailCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--eye-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat?.viewDetailCount} />
|
||||
</span>
|
||||
)}
|
||||
{post.stat?.commentsCount ? (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--comment-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat.commentsCount} />
|
||||
</span>
|
||||
) : (
|
||||
!isShort && (
|
||||
<span className="inline-flex items-center space-x-[6px]">
|
||||
{platform?.icon && (
|
||||
<Image
|
||||
src={platform?.icon}
|
||||
alt={platform?.name}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
)}
|
||||
<span>{t("Portfolio")}</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{!!post.stat?.portfolio?.videoViewsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--youtube-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.videoViewsCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.audoListensCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--headphone-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.audoListensCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.projectStarsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--star-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.projectStarsCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.textViewsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--eye-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.textViewsCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{!!post.stat?.portfolio?.commentsCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--comment-line] mr-[2px] text-base" />
|
||||
<FormattedNumber
|
||||
value={post.stat.portfolio.commentsCount}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!!post.metadata?.content?.tags?.[1] && (
|
||||
<span
|
||||
className="xlog-post-tags hover:text-zinc-600 hover:bg-zinc-200 border transition-colors text-zinc-500 inline-flex items-center bg-zinc-100 rounded-full px-2 py-[1.5px] truncate text-xs sm:text-[13px] h-5"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
router.push(`/tag/${post.metadata?.content?.tags?.[1]}`)
|
||||
}}
|
||||
>
|
||||
<i className="icon-[mingcute--tag-line] mr-[2px]" />
|
||||
{post.metadata?.content?.tags?.[1]}
|
||||
</span>
|
||||
)}
|
||||
{!isShort && !!post.stat?.viewDetailCount && (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--eye-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat?.viewDetailCount} />
|
||||
</span>
|
||||
)}
|
||||
{post.stat?.commentsCount ? (
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--comment-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat.commentsCount} />
|
||||
</span>
|
||||
) : (
|
||||
<span className="xlog-post-word-count sm:inline-flex items-center hidden">
|
||||
<i className="icon-[mingcute--sandglass-line] mr-[2px] text-sm" />
|
||||
<span
|
||||
|
|
@ -232,12 +232,17 @@ const Card = ({
|
|||
{post.metadata?.content?.readingTime} {t("min")}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center space-x-1 text-xs sm:text-sm overflow-hidden",
|
||||
isShort && !character && "!mt-1",
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center space-x-1 text-xs sm:text-sm overflow-hidden">
|
||||
>
|
||||
{character && (
|
||||
<>
|
||||
<CharacterFloatCard siteId={character?.handle}>
|
||||
|
|
@ -269,6 +274,14 @@ const Card = ({
|
|||
<span className="text-zinc-400 hidden sm:inline-block">·</span>
|
||||
</>
|
||||
)}
|
||||
{!!post.stat?.viewCount && (
|
||||
<span className="xlog-post-meta text-zinc-400 flex items-center text-[13px] truncate space-x-2">
|
||||
<span className="xlog-post-views inline-flex items-center">
|
||||
<i className="icon-[mingcute--eye-line] mr-[2px] text-base" />
|
||||
<FormattedNumber value={post.stat?.viewCount} />
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{!isShort && (
|
||||
<time
|
||||
dateTime={date.formatToISO(
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ export const HomeActivitiesTabs = () => {
|
|||
href: "/",
|
||||
active: pathname === "/",
|
||||
},
|
||||
{
|
||||
text: "Shorts",
|
||||
href: "/shorts",
|
||||
active: pathname === "/shorts",
|
||||
},
|
||||
{
|
||||
text: "Latest",
|
||||
href: "/latest",
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ export const HomeFeed = ({ type }: { type?: FeedType }) => {
|
|||
}`
|
||||
}
|
||||
isBlank={isMobileLayout}
|
||||
isShort={type === "shorts"}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export type FeedType =
|
|||
| "tag"
|
||||
| "comments"
|
||||
| "featured"
|
||||
| "shorts"
|
||||
|
||||
export type SearchType = "latest" | "hottest"
|
||||
|
||||
|
|
@ -267,6 +268,71 @@ export async function getFeed({
|
|||
}
|
||||
break
|
||||
}
|
||||
case "shorts": {
|
||||
const result = await client
|
||||
.query(
|
||||
gql`
|
||||
query getNotes($filter: [Int!], $limit: Int) {
|
||||
notes(
|
||||
where: {
|
||||
characterId: {
|
||||
notIn: $filter
|
||||
},
|
||||
deleted: {
|
||||
equals: false,
|
||||
},
|
||||
metadata: {
|
||||
AND: [{
|
||||
content: {
|
||||
path: "sources",
|
||||
array_contains: "xlog"
|
||||
}
|
||||
}, {
|
||||
content: {
|
||||
path: "tags",
|
||||
array_starts_with: "short"
|
||||
}
|
||||
}]
|
||||
},
|
||||
},
|
||||
orderBy: [{ createdAt: desc }],
|
||||
take: $limit,
|
||||
${cursorQuery}
|
||||
) {
|
||||
${resultFields}
|
||||
stat {
|
||||
viewCount
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
filter: filter.latest,
|
||||
limit,
|
||||
},
|
||||
)
|
||||
.toPromise()
|
||||
|
||||
const list = await Promise.all(
|
||||
result?.data?.notes.map((page: NoteEntity) =>
|
||||
expandCrossbellNote({
|
||||
note: page,
|
||||
useScore: true,
|
||||
useHTML,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
resultAll = {
|
||||
list,
|
||||
cursor: list?.length
|
||||
? `${list[list.length - 1]?.characterId}_${list[list.length - 1]
|
||||
?.noteId}`
|
||||
: undefined,
|
||||
count: list?.length || 0,
|
||||
}
|
||||
break
|
||||
}
|
||||
case "following": {
|
||||
if (!characterId) {
|
||||
return {
|
||||
|
|
@ -806,6 +872,9 @@ export async function getFeed({
|
|||
case "portfolio":
|
||||
limit = -1
|
||||
break
|
||||
case "short":
|
||||
limit = -1
|
||||
break
|
||||
}
|
||||
const pass =
|
||||
countCharacters(post?.metadata?.content?.content || "") > limit &&
|
||||
|
|
@ -835,7 +904,7 @@ export async function getFeed({
|
|||
})
|
||||
resultAll.list = resultAll.list.concat(next.list)
|
||||
resultAll.cursor = next.cursor
|
||||
resultAll.count = next.count
|
||||
resultAll.count = resultAll.list.length
|
||||
}
|
||||
|
||||
return resultAll
|
||||
|
|
|
|||
Loading…
Reference in New Issue