diff --git a/apps/desktop/layer/renderer/src/modules/discover/recommendations.tsx b/apps/desktop/layer/renderer/src/modules/discover/recommendations.tsx
index 89a487c21..7f8718b3d 100644
--- a/apps/desktop/layer/renderer/src/modules/discover/recommendations.tsx
+++ b/apps/desktop/layer/renderer/src/modules/discover/recommendations.tsx
@@ -2,7 +2,6 @@ import { isMobile } from "@follow/components/hooks/useMobile.js"
import { EmptyIcon } from "@follow/components/icons/empty.js"
import { ActionButton } from "@follow/components/ui/button/action-button.js"
import { Card, CardContent } from "@follow/components/ui/card/index.jsx"
-import { LoadingCircle } from "@follow/components/ui/loading/index.js"
import { ScrollArea } from "@follow/components/ui/scroll-area/ScrollArea.js"
import { ResponsiveSelect } from "@follow/components/ui/select/responsive.js"
import { CategoryMap, RSSHubCategories } from "@follow/constants"
@@ -20,7 +19,6 @@ import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/ho
import { useAuthQuery } from "~/hooks/common"
import { Queries } from "~/queries"
-import { TrendingButton } from "../trending"
import { RecommendationCard } from "./recommendations-card"
const LanguageOptions = [
@@ -69,7 +67,7 @@ export function Recommendations() {
firstLoad = false
- const { data, isLoading, isFetching } = rsshubPopular
+ const { data, isLoading } = rsshubPopular
const keys = useMemo(() => {
if (!data) {
@@ -143,10 +141,14 @@ export function Recommendations() {
}
return (
-
-
-
-
Preferred language:
+
+
+
+ {t("words.categories")}
+
+
+
+ Language:
-
- {isFetching && (
-
-
- {t("discover.popular")}
-
-
-
-
- )}
-
+
{RSSHubCategories.map((cat) => (
{
- return await apiFetch<{
- data: {
- feed: FeedModel
- }[]
- }>("/trending/feeds", {
- method: "GET",
- params: {
- // language: "en",
- // view: 0,
- limit: 20,
- },
- })
- },
- })
-
- return (
-
-
-
- {t("words.trending")}
-
-
- {data?.data?.map((item, index) => (
-
-
- {index + 1}
-
-
- ))}
-
-
- )
-}
diff --git a/apps/desktop/layer/renderer/src/modules/trending/index.tsx b/apps/desktop/layer/renderer/src/modules/trending/index.tsx
index 1880aa561..60620208f 100644
--- a/apps/desktop/layer/renderer/src/modules/trending/index.tsx
+++ b/apps/desktop/layer/renderer/src/modules/trending/index.tsx
@@ -1,330 +1,170 @@
-import { isMobile, useMobile } from "@follow/components/hooks/useMobile.js"
-import { PhUsersBold } from "@follow/components/icons/users.jsx"
-import { Avatar, AvatarFallback, AvatarImage } from "@follow/components/ui/avatar/index.jsx"
-import { ActionButton, Button } from "@follow/components/ui/button/index.js"
-import { LoadingWithIcon } from "@follow/components/ui/loading/index.jsx"
-import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
-import { EllipsisHorizontalTextWithTooltip } from "@follow/components/ui/typography/index.js"
-import type { FeedModel, Models, UserModel } from "@follow/models"
-import { stopPropagation } from "@follow/utils/dom"
+import { ResponsiveSelect } from "@follow/components/ui/select/responsive.js"
+import { Skeleton } from "@follow/components/ui/skeleton/index.jsx"
+import { views } from "@follow/constants"
+import type { FeedModel } from "@follow/models"
import { cn } from "@follow/utils/utils"
import { useQuery } from "@tanstack/react-query"
-import type { FC } from "react"
+import { useState } from "react"
import { useTranslation } from "react-i18next"
-import { getTrendingAggregates } from "~/api/trending"
-import { DrawerModalLayout } from "~/components/ui/modal/stacked/custom-modal"
-import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/hooks"
-import { useFollow } from "~/hooks/biz/useFollow"
-import { UrlBuilder } from "~/lib/url-builder"
-import { FeedIcon } from "~/modules/feed/feed-icon"
+import { useGeneralSettingKey } from "~/atoms/settings/general"
+import { apiFetch } from "~/lib/api-fetch"
-import { usePresentUserProfileModal } from "../profile/hooks"
+import { FeedCard } from "../discover/feed-card"
-interface TrendingProps {
- language: string
-}
-export const TrendingButton = ({ language, className }: TrendingProps & { className?: string }) => {
- const { present } = useModalStack()
+const LanguageOptions = [
+ {
+ label: "All",
+ value: "all",
+ },
+ {
+ label: "English",
+ value: "eng",
+ },
+ {
+ label: "中文",
+ value: "cmn",
+ },
+]
+
+type Language = (typeof LanguageOptions)[number]["value"]
+
+const rangeOptions = [
+ {
+ label: "Today",
+ value: "1d",
+ },
+ {
+ label: "Three days",
+ value: "3d",
+ },
+ {
+ label: "This week",
+ value: "7d",
+ },
+ {
+ label: "This month",
+ value: "30d",
+ },
+]
+
+type Range = (typeof rangeOptions)[number]["value"]
+
+const viewOptions = [
+ {
+ label: "All",
+ value: "all",
+ },
+ ...views.map((view) => ({
+ label: view.name,
+ value: `${view.view}`,
+ })),
+]
+
+type View = (typeof viewOptions)[number]["value"]
+
+export function Trending() {
const { t } = useTranslation()
- return (
-
- )
-}
-const TrendContent: FC = ({ language }) => {
- const isMobile = useMobile()
+ const { t: tCommon } = useTranslation("common")
+ const lang = useGeneralSettingKey("language")
- const { data } = useQuery({
- queryKey: ["trending", language],
- queryFn: () => {
- return getTrendingAggregates({ language })
+ const [selectedLang, setSelectedLang] = useState(lang.startsWith("zh") ? "all" : "eng")
+ const [selectedRange, setSelectedRange] = useState("7d")
+ const [selectedView, setSelectedView] = useState("all")
+
+ const { data, isLoading } = useQuery({
+ queryKey: ["trending", selectedLang, selectedView, selectedRange],
+ queryFn: async () => {
+ return await apiFetch<{
+ data: {
+ feed: FeedModel
+ }[]
+ }>("/trending/feeds", {
+ method: "GET",
+ params: {
+ language: selectedLang === "all" ? undefined : selectedLang,
+ view: selectedView === "all" ? undefined : Number(selectedView),
+ limit: 20,
+ },
+ })
},
})
- const { t } = useTranslation()
- const { dismiss } = useCurrentModal()
- if (!data)
- return (
-
- }
- size="large"
- />
-
- )
return (
-
- {!isMobile && (
-
-
-
{t("words.trending")}
+
+
+
+ {t("words.trending")}
+
+
+
+ Language:
+ {
+ setSelectedLang(value as Language)
+ }}
+ triggerClassName="w-32 h-8 rounded"
+ size="sm"
+ items={LanguageOptions}
+ />
- )}
-
-
-
-
-
-
-
-
-
-
+
+ Range:
+ {
+ setSelectedRange(value as Range)
+ }}
+ triggerClassName="w-32 h-8 rounded"
+ size="sm"
+ items={rangeOptions}
+ />
+
+
+ View:
+ {
+ setSelectedView(value as View)
+ }}
+ triggerClassName="w-32 h-8 rounded"
+ size="sm"
+ items={viewOptions}
+ renderItem={(item) => <>{tCommon(item.label as any)}>}
+ />
+
+
+
+ {isLoading ? (
+ <>
+
+
+
+
+
+
+ >
+ ) : (
+ data?.data?.map((item, index) => (
+
+
+ {index + 1}
+
+
+ ))
+ )}
+
)
}
-const TrendingLists: FC<{
- data: Models.TrendingList[]
-}> = ({ data }) => {
- const follow = useFollow()
- const { t } = useTranslation()
- return (
-
- {t("trending.list")}
-
-
- {data.map((item) => (
- -
-
-
- ))}
-
-
- )
-}
-
-const UserCount: Component<{ count: number }> = ({ count, className }) => {
- return (
-
-
- {count}
-
- )
-}
-
-interface TopUserAvatarProps {
- user: UserModel
- position: string
-}
-
-const TopUserAvatar: React.FC
= ({ user, position }) => (
-
-
-
-
- {user.name?.slice(0, 2)}
-
-
- {user.name && (
-
- {user.name}
-
- )}
-
-)
-
-const TrendingUsers: FC<{ data: UserModel[] }> = ({ data }) => {
- const profile = usePresentUserProfileModal("dialog")
- const { t } = useTranslation()
- return (
-
- {t("trending.user")}
-
-
-
-
-
-
-
-
-
- {data.slice(0, 3).map((user, index: number) => (
-
- ))}
-
-
- {data.length > 3 && (
-
- {data.slice(3).map((user) => (
- -
-
-
- ))}
-
- )}
-
- )
-}
-
-const TrendingFeeds = ({ data }: { data: FeedModel[] }) => {
- const follow = useFollow()
- const { t } = useTranslation()
- return (
-
- {t("trending.feed")}
-
-
-
- )
-}
-
-const TrendingEntries = ({ data }: { data: Models.TrendingEntry[] }) => {
- const { t } = useTranslation()
- const filteredData = data.filter((entry) => !entry.url.startsWith("https://x.com"))
- return (
-
- {t("trending.entry")}
-
- {filteredData.length > 0 ? (
-
- {filteredData.map((entry) => {
- return (
- -
-
-
-
- {entry.readCount}
-
-
- )
- })}
-
- ) : (
- {t("trending.entry_no_results")}
- )}
-
- )
-}
diff --git a/apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/discover/index.tsx b/apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/discover/index.tsx
index f2fbf35b8..592159a5a 100644
--- a/apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/discover/index.tsx
+++ b/apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/discover/index.tsx
@@ -16,8 +16,8 @@ import { DiscoverImport } from "~/modules/discover/import"
import { DiscoverInboxList } from "~/modules/discover/inbox-list-form"
import { Recommendations } from "~/modules/discover/recommendations"
import { DiscoverTransform } from "~/modules/discover/transform-form"
-import { Trending } from "~/modules/discover/trending"
import { DiscoverUser } from "~/modules/discover/user-form"
+import { Trending } from "~/modules/trending"
const tabs: {
name: I18nKeys
diff --git a/icons/mgc/classify_2_cute_re.svg b/icons/mgc/classify_2_cute_re.svg
new file mode 100644
index 000000000..ec5b2c470
--- /dev/null
+++ b/icons/mgc/classify_2_cute_re.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/icons/mgc/polygon_cute_re.svg b/icons/mgc/polygon_cute_re.svg
new file mode 100644
index 000000000..de1b27f2c
--- /dev/null
+++ b/icons/mgc/polygon_cute_re.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/locales/app/en.json b/locales/app/en.json
index 3bf4d47cc..47ebdeba8 100644
--- a/locales/app/en.json
+++ b/locales/app/en.json
@@ -406,6 +406,7 @@
"words.add": "Add",
"words.boost": "Boost",
"words.browser": "Browser",
+ "words.categories": "Categories",
"words.confirm": "Confirm",
"words.discover": "Discover",
"words.email": "Email",