From 64d44ec61bc62feddd506b003c7d617cfa0767ca Mon Sep 17 00:00:00 2001 From: DIYgod Date: Thu, 15 May 2025 17:42:07 +0800 Subject: [PATCH] feat(mobile): feed summary component --- .../src/modules/discover/FeedSummary.tsx | 137 ++++++++++++++ .../discover/search-tabs/SearchFeed.tsx | 177 +----------------- .../discover/search-tabs/SearchFeedCard.tsx | 68 +++++++ apps/mobile/src/modules/feed/FollowFeed.tsx | 22 +-- 4 files changed, 218 insertions(+), 186 deletions(-) create mode 100644 apps/mobile/src/modules/discover/FeedSummary.tsx create mode 100644 apps/mobile/src/modules/discover/search-tabs/SearchFeedCard.tsx diff --git a/apps/mobile/src/modules/discover/FeedSummary.tsx b/apps/mobile/src/modules/discover/FeedSummary.tsx new file mode 100644 index 000000000..049f7a4ad --- /dev/null +++ b/apps/mobile/src/modules/discover/FeedSummary.tsx @@ -0,0 +1,137 @@ +import { FeedViewType } from "@follow/constants" +import { getBackgroundGradient } from "@follow/utils" +import { LinearGradient } from "expo-linear-gradient" +import { Text, View } from "react-native" +import { ScrollView } from "react-native-gesture-handler" + +import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime" +import { FeedIcon } from "@/src/components/ui/icon/feed-icon" +import { Image } from "@/src/components/ui/image/Image" +import { ItemPressableStyle } from "@/src/components/ui/pressable/enum" +import { ItemPressable } from "@/src/components/ui/pressable/ItemPressable" +import type { apiClient } from "@/src/lib/api-fetch" +import { useNavigation } from "@/src/lib/navigation/hooks" +import { FollowScreen } from "@/src/screens/(modal)/FollowScreen" + +type SearchResultItem = Awaited>["data"][number] + +export const FeedSummary = ({ + item, + children, + className, +}: { + item: SearchResultItem + children?: React.ReactNode + className?: string +}) => { + const navigation = useNavigation() + + return ( + { + if (item.feed?.id) { + navigation.presentControllerView(FollowScreen, { + id: item.feed.id, + type: "feed", + }) + } else if (item.feed?.url) { + navigation.presentControllerView(FollowScreen, { + url: item.feed.url, + type: "url", + }) + } + }} + className={className} + > + {/* Headline */} + + + + + + + {item.feed?.title} + + {item.feed?.url} + + + {!!item.feed?.description && ( + + {item.feed?.description} + + )} + + {/* Preview */} + + + {item.entries?.map((entry) => )} + + + {children} + + ) +} + +const PreviewItem = ({ entry }: { entry: NonNullable[number] }) => { + const firstMedia = entry.media?.[0] + const [, , , bgAccent, bgAccentLight] = getBackgroundGradient( + entry.title || entry.url || "Untitled", + ) + + return ( + + {firstMedia ? ( + + ) : ( + + {entry.title?.[0]} + + )} + + + {entry.title} + + + + + ) +} diff --git a/apps/mobile/src/modules/discover/search-tabs/SearchFeed.tsx b/apps/mobile/src/modules/discover/search-tabs/SearchFeed.tsx index c3b9dfbe7..103f81b23 100644 --- a/apps/mobile/src/modules/discover/search-tabs/SearchFeed.tsx +++ b/apps/mobile/src/modules/discover/search-tabs/SearchFeed.tsx @@ -1,31 +1,13 @@ -import { FeedViewType } from "@follow/constants" -import { getBackgroundGradient } from "@follow/utils" import { useQuery } from "@tanstack/react-query" -import { LinearGradient } from "expo-linear-gradient" import { useAtomValue } from "jotai" import { Text, useWindowDimensions, View } from "react-native" -import { ScrollView } from "react-native-gesture-handler" -import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime" -import { FeedIcon } from "@/src/components/ui/icon/feed-icon" -import { Image } from "@/src/components/ui/image/Image" -import { ItemPressableStyle } from "@/src/components/ui/pressable/enum" -import { ItemPressable } from "@/src/components/ui/pressable/ItemPressable" -import { RightCuteReIcon } from "@/src/icons/right_cute_re" -import { SafeAlertCuteReIcon } from "@/src/icons/safe_alert_cute_re" -import { SafetyCertificateCuteReIcon } from "@/src/icons/safety_certificate_cute_re" -import { User3CuteReIcon } from "@/src/icons/user_3_cute_re" import { apiClient } from "@/src/lib/api-fetch" -import { useNavigation } from "@/src/lib/navigation/hooks" -import { FollowScreen } from "@/src/screens/(modal)/FollowScreen" -import { useSubscriptionByFeedId } from "@/src/store/subscription/hooks" -import { useColor } from "@/src/theme/colors" import { useSearchPageContext } from "../ctx" import { ItemSeparator } from "./__base" import { useDataSkeleton } from "./hooks" - -type SearchResultItem = Awaited>["data"][number] +import { SearchFeedCard } from "./SearchFeedCard" export const SearchFeed = () => { const { searchValueAtom } = useSearchPageContext() @@ -56,7 +38,7 @@ export const SearchFeed = () => { {data.data?.map((item) => ( - + ))} @@ -64,158 +46,3 @@ export const SearchFeed = () => { ) } - -const SearchFeedItem = ({ item }: { item: SearchResultItem }) => { - const isSubscribed = useSubscriptionByFeedId(item.feed?.id ?? "") - const navigation = useNavigation() - const iconColor = useColor("text") - - return ( - { - if (item.feed?.id) { - navigation.presentControllerView(FollowScreen, { - id: item.feed.id, - type: "feed", - }) - } else if (item.feed?.url) { - navigation.presentControllerView(FollowScreen, { - url: item.feed.url, - type: "url", - }) - } - }} - > - {/* Headline */} - - - - - - - {item.feed?.title} - - {!!item.feed?.description && ( - - {item.feed?.description} - - )} - - {/* Subscribe */} - {isSubscribed ? ( - - - Followed - - - ) : ( - - - Follow - - - )} - - - - - {item.feed?.url} - - - {/* Preview */} - - - {item.entries?.map((entry) => )} - - - - - - {item.subscriptionCount} followers - - - {item.updatesPerWeek ? ( - <> - - {item.updatesPerWeek} entries/week - - ) : item.entries?.[0]?.publishedAt ? ( - <> - - Updated - - - ) : null} - - - - ) -} - -const PreviewItem = ({ entry }: { entry: NonNullable[number] }) => { - const firstMedia = entry.media?.[0] - const [, , , bgAccent, bgAccentLight] = getBackgroundGradient( - entry.title || entry.url || "Untitled", - ) - - return ( - - {firstMedia ? ( - - ) : ( - - {entry.title?.[0]} - - )} - - - {entry.title} - - - - - ) -} diff --git a/apps/mobile/src/modules/discover/search-tabs/SearchFeedCard.tsx b/apps/mobile/src/modules/discover/search-tabs/SearchFeedCard.tsx new file mode 100644 index 000000000..b1d4929f8 --- /dev/null +++ b/apps/mobile/src/modules/discover/search-tabs/SearchFeedCard.tsx @@ -0,0 +1,68 @@ +import { Text, View } from "react-native" + +import { RelativeDateTime } from "@/src/components/ui/datetime/RelativeDateTime" +import { SafeAlertCuteReIcon } from "@/src/icons/safe_alert_cute_re" +import { SafetyCertificateCuteReIcon } from "@/src/icons/safety_certificate_cute_re" +import { User3CuteReIcon } from "@/src/icons/user_3_cute_re" +import type { apiClient } from "@/src/lib/api-fetch" +import { useSubscriptionByFeedId } from "@/src/store/subscription/hooks" +import { useColor } from "@/src/theme/colors" + +import { FeedSummary } from "../FeedSummary" + +type SearchResultItem = Awaited>["data"][number] + +export const SearchFeedCard = ({ item }: { item: SearchResultItem }) => { + const isSubscribed = useSubscriptionByFeedId(item.feed?.id ?? "") + const iconColor = useColor("text") + + return ( + + + <> + + + + + {item.analytics?.subscriptionCount} followers + + + + {item.analytics?.updatesPerWeek ? ( + <> + + + {item.analytics.updatesPerWeek} entries/week + + + ) : item.analytics?.latestEntryPublishedAt ? ( + <> + + Updated + + + ) : null} + + + {/* Subscribe */} + {isSubscribed ? ( + + + Followed + + + ) : ( + + + Follow + + + )} + + + + ) +} diff --git a/apps/mobile/src/modules/feed/FollowFeed.tsx b/apps/mobile/src/modules/feed/FollowFeed.tsx index 6ae42762f..601ed301b 100644 --- a/apps/mobile/src/modules/feed/FollowFeed.tsx +++ b/apps/mobile/src/modules/feed/FollowFeed.tsx @@ -17,10 +17,10 @@ import { FormLabel } from "@/src/components/ui/form/Label" import { FormSwitch } from "@/src/components/ui/form/Switch" import { TextField } from "@/src/components/ui/form/TextField" import { GroupedInsetListCard } from "@/src/components/ui/grouped/GroupedList" -import { FeedIcon } from "@/src/components/ui/icon/feed-icon" import { PlatformActivityIndicator } from "@/src/components/ui/loading/PlatformActivityIndicator" import { useCanDismiss, useNavigation } from "@/src/lib/navigation/hooks" import { useSetModalScreenOptions } from "@/src/lib/navigation/ScreenOptionsContext" +import { FeedSummary } from "@/src/modules/discover/FeedSummary" import { FeedViewSelector } from "@/src/modules/feed/view-selector" import { useFeed, usePrefetchFeed, usePrefetchFeedByUrl } from "@/src/store/feed/hooks" import { useSubscriptionByFeedId } from "@/src/store/subscription/hooks" @@ -163,16 +163,16 @@ function FollowImpl(props: { feedId: string }) { } > {/* Group 1 */} - - - - - - - {feed?.title} - {feed?.description} - - + + {/* Group 2 */}