diff --git a/src/app/(home)/(activities)/latest/page.tsx b/src/app/(home)/(activities)/latest/page.tsx
new file mode 100644
index 00000000..b03a4036
--- /dev/null
+++ b/src/app/(home)/(activities)/latest/page.tsx
@@ -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: `Latest Activities - ${APP_NAME}`,
+}
+
+export default async function LatestActivities() {
+ const queryClient = getQueryClient()
+ await prefetchGetFeed(
+ {
+ type: "latest",
+ },
+ queryClient,
+ )
+
+ const dehydratedState = dehydrate(queryClient)
+
+ return (
+
+
+
+ )
+}
diff --git a/src/app/(home)/(activities)/page.tsx b/src/app/(home)/(activities)/page.tsx
index b03a4036..cb870107 100644
--- a/src/app/(home)/(activities)/page.tsx
+++ b/src/app/(home)/(activities)/page.tsx
@@ -3,19 +3,19 @@ import { Metadata } from "next"
import { Hydrate, dehydrate } from "@tanstack/react-query"
import { HomeFeed } from "~/components/home/HomeFeed"
-import { APP_NAME } from "~/lib/env"
+import { APP_NAME, APP_SLOGAN } from "~/lib/env"
import getQueryClient from "~/lib/query-client"
import { prefetchGetFeed } from "~/queries/home.server"
export const metadata: Metadata = {
- title: `Latest Activities - ${APP_NAME}`,
+ title: `${APP_NAME} - ${APP_SLOGAN}`,
}
-export default async function LatestActivities() {
+export default async function HomeActivities() {
const queryClient = getQueryClient()
await prefetchGetFeed(
{
- type: "latest",
+ type: "featured",
},
queryClient,
)
@@ -24,7 +24,7 @@ export default async function LatestActivities() {
return (
-
+
)
}
diff --git a/src/components/home/HomeActivitiesTabs.tsx b/src/components/home/HomeActivitiesTabs.tsx
index 980c68d4..2ccb01ca 100644
--- a/src/components/home/HomeActivitiesTabs.tsx
+++ b/src/components/home/HomeActivitiesTabs.tsx
@@ -16,10 +16,15 @@ export const HomeActivitiesTabs = () => {
const tabs = [
{
- text: "Latest",
+ text: "Featured",
href: "/",
active: pathname === "/",
},
+ {
+ text: "Latest",
+ href: "/latest",
+ active: pathname === "/latest",
+ },
{
text: "Hottest",
href: "/hottest",
diff --git a/src/lib/i18n/locales/zh/dashboard.json b/src/lib/i18n/locales/zh/dashboard.json
index 0e902a7c..3552d51f 100644
--- a/src/lib/i18n/locales/zh/dashboard.json
+++ b/src/lib/i18n/locales/zh/dashboard.json
@@ -181,6 +181,7 @@
"Subdomain Available.": "子域名可用。",
"Subdomain Unavailable.": "子域名不可用。",
"Recheck": "重新检查",
+ "Featured": "精选",
"Hottest": "最热",
"Latest": "最新",
"Following": "关注",
diff --git a/src/models/home.model.ts b/src/models/home.model.ts
index ba8a0fcb..d2b88323 100644
--- a/src/models/home.model.ts
+++ b/src/models/home.model.ts
@@ -8,6 +8,7 @@ import { ExpandedNote } from "~/lib/types"
import { client } from "~/queries/graphql"
import filter from "../../data/filter.json"
+import titles from "../../data/titles.json"
import topics from "../../data/topics.json"
export type FeedType =
@@ -18,12 +19,14 @@ export type FeedType =
| "search"
| "tag"
| "comments"
+ | "featured"
+
export type SearchType = "latest" | "hottest"
export async function getFeed({
type,
cursor,
- limit = 30,
+ limit = 12,
characterId,
daysInterval,
searchKeyword,
@@ -480,9 +483,9 @@ export async function getFeed({
},
},
take: 40,
- orderBy: {
- stat: {
- viewDetailCount: desc
+ orderBy: {
+ stat: {
+ viewDetailCount: desc
}
},
) {
@@ -534,6 +537,102 @@ export async function getFeed({
count: list?.length || 0,
}
}
+ case "featured": {
+ const result = await client
+ .query(
+ gql`
+ query getNotes($filter: [Int!], $limit: Int, $whitelist: [Int!]) {
+ notes(
+ where: {
+ characterId: {
+ notIn: $filter
+ },
+ deleted: {
+ equals: false,
+ },
+ metadata: {
+ content: {
+ path: "sources",
+ array_contains: "xlog"
+ },
+ NOT: [{
+ content: {
+ path: "tags",
+ array_starts_with: "comment"
+ }
+ }]
+ },
+ OR: [
+ {
+ stat: {
+ viewDetailCount: {
+ gt: 30
+ },
+ },
+ },
+ {
+ characterId: {
+ in: $whitelist
+ },
+ }
+ ]
+ },
+ orderBy: [{ createdAt: desc }],
+ take: $limit,
+ ${cursorQuery}
+ ) {
+ stat {
+ viewDetailCount
+ }
+ ${resultFields}
+ }
+ }
+ `,
+ {
+ filter: filter.latest,
+ limit,
+ whitelist: titles.reduce((acc, cur) => {
+ acc = acc.concat(cur.list)
+ return acc
+ }, [] as number[]),
+ },
+ )
+ .toPromise()
+
+ let list = await Promise.all(
+ result?.data?.notes.map(async (page: any) => {
+ const secondAgo = dayjs().diff(dayjs(page.createdAt), "second")
+ page.stat.hotScore =
+ page.stat.viewDetailCount / Math.max(Math.log10(secondAgo), 1)
+
+ const expand = await expandCrossbellNote({
+ note: page,
+ useHTML,
+ })
+ delete expand.metadata?.content.content
+ return expand
+ }),
+ )
+
+ const cursor = list?.length
+ ? `${list[list.length - 1]?.characterId}_${list[list.length - 1]
+ ?.noteId}`
+ : undefined
+
+ list = list.sort((a, b) => {
+ if (a.stat?.hotScore && b.stat?.hotScore) {
+ return b.stat.hotScore - a.stat.hotScore
+ } else {
+ return 0
+ }
+ })
+
+ return {
+ list,
+ cursor,
+ count: list?.length || 0,
+ }
+ }
case "search": {
const result = await indexer.search.notes(searchKeyword!, {
sources: ["xlog"],