From 48339384d2285b2a6702a0f305bcb4a1f9e5df28 Mon Sep 17 00:00:00 2001 From: lafay <2021211506@stu.hit.edu.cn> Date: Tue, 24 Mar 2026 05:18:22 +0800 Subject: [PATCH] refactor(ImageGallery, HomeScreen, PostService, UserStore): streamline post type handling and improve tab navigation - Updated ImageGallery to manage loading states more effectively and prevent unnecessary updates. - Refactored HomeScreen to remove the 'recommend' post type, simplifying the post type options. - Adjusted PostService and UserStore to eliminate references to 'recommend', ensuring consistency across the application. - Enhanced tab navigation in SimpleMobileTabNavigator to optimize rendering and manage mounted tabs more efficiently. - Improved error handling and loading states in various components for better user experience. --- src/components/common/ImageGallery.tsx | 16 +++++-- .../interfaces/IPostRepository.ts | 4 +- src/hooks/usePrefetch.ts | 6 +-- src/navigation/SimpleMobileTabNavigator.tsx | 47 +++++++++++++++++-- src/screens/home/HomeScreen.tsx | 11 ++--- src/services/postService.ts | 7 +-- src/stores/postManager.ts | 2 +- src/stores/userStore.ts | 9 ++-- src/types/dto.ts | 4 +- 9 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/components/common/ImageGallery.tsx b/src/components/common/ImageGallery.tsx index 8e4aaf3..ad71b5c 100644 --- a/src/components/common/ImageGallery.tsx +++ b/src/components/common/ImageGallery.tsx @@ -86,7 +86,9 @@ export const ImageGallery: React.FC = ({ }) => { const closeTimerRef = useRef | null>(null); const isClosingRef = useRef(false); + const currentIndexRef = useRef(initialIndex); const [currentIndex, setCurrentIndex] = useState(initialIndex); + currentIndexRef.current = currentIndex; const [showControls, setShowControls] = useState(true); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); @@ -152,16 +154,19 @@ export const ImageGallery: React.FC = ({ }; }, []); - // 图片变化时重置加载状态和缩放 + // 图片变化时重置缩放(加载态在 updateIndex / 打开弹窗时同步设置,避免晚一帧仍显示上一张) useEffect(() => { - setLoading(true); - setError(false); resetZoom(); }, [currentImage?.id, resetZoom]); const updateIndex = useCallback( (newIndex: number) => { const clampedIndex = Math.max(0, Math.min(validImages.length - 1, newIndex)); + if (clampedIndex === currentIndexRef.current) { + return; + } + setLoading(true); + setError(false); setCurrentIndex(clampedIndex); onIndexChange?.(clampedIndex); }, @@ -412,7 +417,8 @@ export const ImageGallery: React.FC = ({ contentFit="contain" cachePolicy="disk" priority="high" - recyclingKey={currentImage.id} + recyclingKey={currentImage.url} + transition={null} allowDownscaling onLoadStart={() => setLoading(true)} onLoad={() => { @@ -556,11 +562,13 @@ const styles = StyleSheet.create({ justifyContent: 'center', alignItems: 'center', zIndex: 5, + backgroundColor: '#000', }, errorContainer: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', alignItems: 'center', + backgroundColor: '#000', }, errorText: { color: '#999', diff --git a/src/data/repositories/interfaces/IPostRepository.ts b/src/data/repositories/interfaces/IPostRepository.ts index c1dc72f..eb81a14 100644 --- a/src/data/repositories/interfaces/IPostRepository.ts +++ b/src/data/repositories/interfaces/IPostRepository.ts @@ -17,8 +17,8 @@ export interface GetPostsParams { pageSize?: number; /** 游标 - 用于无限滚动加载 */ cursor?: string; - /** 帖子类型筛选(可选):recommend, follow, hot, latest */ - post_type?: 'recommend' | 'follow' | 'hot' | 'latest'; + /** 帖子类型筛选(可选):follow, hot, latest */ + post_type?: 'follow' | 'hot' | 'latest'; /** 社区ID过滤 */ communityId?: string; /** 作者ID过滤 */ diff --git a/src/hooks/usePrefetch.ts b/src/hooks/usePrefetch.ts index ffe0556..539d65b 100644 --- a/src/hooks/usePrefetch.ts +++ b/src/hooks/usePrefetch.ts @@ -134,7 +134,7 @@ const prefetchService = new PrefetchService(); * 预取帖子数据 * @param types 帖子类型数组 */ -function prefetchPosts(types: string[] = ['recommend', 'hot']): void { +function prefetchPosts(types: string[] = ['latest', 'hot']): void { types.forEach((type, index) => { prefetchService.schedule({ key: `posts:${type}:1`, @@ -230,7 +230,7 @@ function prefetchOnAppLaunch(): void { prefetchConversations(); // 中优先级:帖子列表 - prefetchPosts(['recommend']); + prefetchPosts(['latest']); } /** @@ -257,7 +257,7 @@ function prefetchMessageScreen(): void { * 进入首页时预取 */ function prefetchHomeScreen(): void { - prefetchPosts(['recommend', 'hot', 'latest']); + prefetchPosts(['latest', 'hot', 'follow']); } // ==================== React Hook ==================== diff --git a/src/navigation/SimpleMobileTabNavigator.tsx b/src/navigation/SimpleMobileTabNavigator.tsx index 6d81c2f..da32915 100644 --- a/src/navigation/SimpleMobileTabNavigator.tsx +++ b/src/navigation/SimpleMobileTabNavigator.tsx @@ -178,6 +178,12 @@ export function SimpleMobileTabNavigator() { const insets = useSafeAreaInsets(); const messageUnreadCount = useTotalUnreadCount(); const [activeTab, setActiveTab] = useState('HomeTab'); + const [mountedTabs, setMountedTabs] = useState>({ + HomeTab: true, + MessageTab: false, + ScheduleTab: false, + ProfileTab: false, + }); // 初始化 MessageManager useEffect(() => { @@ -186,12 +192,21 @@ export function SimpleMobileTabNavigator() { // 处理 Tab 切换 const handleTabPress = useCallback((tabName: TabName) => { + setMountedTabs((prev) => { + if (prev[tabName]) { + return prev; + } + return { + ...prev, + [tabName]: true, + }; + }); setActiveTab(tabName); }, []); - // 渲染当前 Tab 的内容 - const renderTabContent = () => { - switch (activeTab) { + // 根据 Tab 名称渲染对应内容 + const renderTabView = (tabName: TabName) => { + switch (tabName) { case 'HomeTab': return ; case 'MessageTab': @@ -276,7 +291,24 @@ export function SimpleMobileTabNavigator() { {/* 内容区域 */} - {renderTabContent()} + {(Object.keys(mountedTabs) as TabName[]).map((tabName) => { + if (!mountedTabs[tabName]) { + return null; + } + const isActive = activeTab === tabName; + return ( + + {renderTabView(tabName)} + + ); + })} {/* Tab Bar */} @@ -334,6 +366,13 @@ const styles = StyleSheet.create({ }, content: { flex: 1, + position: 'relative', + }, + tabScreen: { + ...StyleSheet.absoluteFillObject, + }, + tabScreenHidden: { + display: 'none', }, bottomSpacer: { backgroundColor: 'transparent', diff --git a/src/screens/home/HomeScreen.tsx b/src/screens/home/HomeScreen.tsx index 7d7d067..7410251 100644 --- a/src/screens/home/HomeScreen.tsx +++ b/src/screens/home/HomeScreen.tsx @@ -41,8 +41,8 @@ import { navigationService } from '../../infrastructure/navigation/navigationSer type NavigationProp = NativeStackNavigationProp & NativeStackNavigationProp; -const TABS = ['推荐', '关注', '热门', '最新']; -const TAB_ICONS = ['compass-outline', 'account-heart-outline', 'fire', 'clock-outline']; +const TABS = ['最新', '关注', '热门']; +const TAB_ICONS = ['clock-outline', 'account-heart-outline', 'fire']; const DEFAULT_PAGE_SIZE = 20; const SWIPE_TRANSLATION_THRESHOLD = 40; const SWIPE_COOLDOWN_MS = 300; @@ -51,7 +51,7 @@ const MOBILE_TAB_FLOATING_MARGIN = 12; const MOBILE_FAB_GAP = 12; type ViewMode = 'list' | 'grid'; -type PostType = 'recommend' | 'follow' | 'hot' | 'latest'; +type PostType = 'follow' | 'hot' | 'latest'; export const HomeScreen: React.FC = () => { const navigation = useNavigation(); @@ -106,11 +106,10 @@ export const HomeScreen: React.FC = () => { // 获取当前 tab 对应的帖子类型 const getPostType = useCallback((): PostType => { switch (activeIndex) { - case 0: return 'recommend'; + case 0: return 'latest'; case 1: return 'follow'; case 2: return 'hot'; - case 3: return 'latest'; - default: return 'recommend'; + default: return 'latest'; } }, [activeIndex]); diff --git a/src/services/postService.ts b/src/services/postService.ts index 9445c60..71a3430 100644 --- a/src/services/postService.ts +++ b/src/services/postService.ts @@ -63,11 +63,6 @@ class PostService { return response.data; } - // 获取推荐帖子 - async getRecommendedPosts(page = 1, pageSize = 20): Promise> { - return this.getPosts(page, pageSize, 'recommend'); - } - // 获取关注帖子 async getFollowingPosts(page = 1, pageSize = 20): Promise> { return this.getPosts(page, pageSize, 'follow'); @@ -282,7 +277,7 @@ class PostService { /** * 获取帖子列表(游标分页) * GET /api/v1/posts - * @param params 游标分页请求参数(包含 post_type 可选:recommend, follow, hot, latest) + * @param params 游标分页请求参数(包含 post_type 可选:follow, hot, latest) */ async getPostsCursor( params: CursorPaginationRequest = {} diff --git a/src/stores/postManager.ts b/src/stores/postManager.ts index 9dc51e5..35338dd 100644 --- a/src/stores/postManager.ts +++ b/src/stores/postManager.ts @@ -63,7 +63,7 @@ class PostManager extends CacheBus { } async getPosts( - type = 'recommend', + type = 'hot', page = 1, pageSize = 20, forceRefresh = false diff --git a/src/stores/userStore.ts b/src/stores/userStore.ts index d5e9131..17f1985 100644 --- a/src/stores/userStore.ts +++ b/src/stores/userStore.ts @@ -34,7 +34,7 @@ interface UserState { // 操作 fetchUser: (userId: string) => Promise; fetchUserPosts: (userId: string, page?: number) => Promise; - fetchPosts: (type: 'recommend' | 'follow' | 'hot' | 'latest', page?: number) => Promise>; + fetchPosts: (type: 'follow' | 'hot' | 'latest', page?: number) => Promise>; fetchNotifications: (type?: string, page?: number) => Promise; markNotificationAsRead: (notificationId: string) => Promise; markAllNotificationsAsRead: () => Promise; @@ -108,16 +108,13 @@ export const useUserStore = create((set, get) => { }, // 获取帖子列表(首页) - fetchPosts: async (type: 'recommend' | 'follow' | 'hot' | 'latest', page = 1) => { + fetchPosts: async (type: 'follow' | 'hot' | 'latest', page = 1) => { set({ isLoadingPosts: true }); try { let response; switch (type) { - case 'recommend': - response = await postService.getRecommendedPosts(page); - break; case 'follow': response = await postService.getFollowingPosts(page); break; @@ -332,7 +329,7 @@ export const useUserStore = create((set, get) => { // 刷新所有数据 refreshAll: async () => { await Promise.all([ - get().fetchPosts('recommend', 1), + get().fetchPosts('latest', 1), get().fetchNotificationBadge(), ]); }, diff --git a/src/types/dto.ts b/src/types/dto.ts index bf610e5..713f45c 100644 --- a/src/types/dto.ts +++ b/src/types/dto.ts @@ -482,8 +482,8 @@ export interface CursorPaginationRequest { direction?: 'forward' | 'backward'; /** 每页数量(默认 20,最大 100) */ page_size?: number; - /** 帖子类型筛选(可选):recommend, follow, hot, latest */ - post_type?: 'recommend' | 'follow' | 'hot' | 'latest'; + /** 帖子类型筛选(可选):follow, hot, latest */ + post_type?: 'follow' | 'hot' | 'latest'; } /**