diff --git a/src/hooks/useCursorPagination.ts b/src/hooks/useCursorPagination.ts index b48f1b6..6b03a40 100644 --- a/src/hooks/useCursorPagination.ts +++ b/src/hooks/useCursorPagination.ts @@ -302,6 +302,20 @@ export function useCursorPagination( [] ); + // 就地更新单条数据 + const updateItem = useCallback( + (id: string, updates: Partial) => { + setState(prev => { + const idx = prev.list.findIndex(item => getItemId(item) === id); + if (idx === -1) return prev; + const newList = [...prev.list]; + newList[idx] = { ...newList[idx], ...updates }; + return { ...prev, list: newList }; + }); + }, + [] + ); + // 自动加载第一页 useEffect(() => { // 使用 ref 防止重复加载,不依赖 loadMore 函数引用 @@ -377,6 +391,7 @@ export function useCursorPagination( refresh, reset, setList, + updateItem, }; } diff --git a/src/infrastructure/pagination/types.ts b/src/infrastructure/pagination/types.ts index 6e54336..b2a200b 100644 --- a/src/infrastructure/pagination/types.ts +++ b/src/infrastructure/pagination/types.ts @@ -240,6 +240,8 @@ export interface CursorPaginationActions { reset: () => void; /** 设置数据(用于外部数据注入) */ setList: (list: T[], nextCursor: string | null, prevCursor: string | null, hasMore: boolean) => void; + /** 就地更新单条数据 */ + updateItem: (id: string, updates: Partial) => void; } /** diff --git a/src/screens/message/NotificationsScreen.tsx b/src/screens/message/NotificationsScreen.tsx index fefeb89..789b67f 100644 --- a/src/screens/message/NotificationsScreen.tsx +++ b/src/screens/message/NotificationsScreen.tsx @@ -110,6 +110,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack hasMore, loadMore, refresh, + updateItem, error: paginationError, } = useCursorPagination(fetchSystemMessages, { pageSize: 20 }); @@ -182,17 +183,16 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack } }, [refresh, setSystemUnreadCount]); - // 页面加载和获得焦点时刷新,并自动标记所有消息为已读 - // 使用 ref 防止重复执行,避免无限循环 - const hasInitializedRef = useRef(false); + // 页面获得焦点时刷新未读数并标记全部已读 + const markAllReadAndRefreshRef = useRef(handleMarkAllRead); + markAllReadAndRefreshRef.current = handleMarkAllRead; + useEffect(() => { - if (isFocused && !hasInitializedRef.current) { - hasInitializedRef.current = true; + if (isFocused) { fetchUnreadCount(); - // 进入界面自动标记所有消息为已读 - handleMarkAllRead(); + markAllReadAndRefreshRef.current(); } - }, [isFocused]); // 只依赖 isFocused,函数使用 ref 稳定引用 + }, [isFocused]); // 启动入场动画 useEffect(() => { @@ -308,13 +308,11 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack const messageId = String(message.id); const wasUnread = message.is_read !== true; await messageService.markSystemMessageRead(messageId); - // 【游标分页】不再直接修改 messages 状态,而是通过刷新获取最新数据 if (wasUnread) { + updateItem(messageId, { is_read: true }); setUnreadCount(prev => Math.max(0, prev - 1)); decrementSystemUnreadCount(1); } - // 更新本地未读数以及全局 TabBar 红点 - fetchUnreadCount(); messageManager.fetchUnreadCount(); // 根据消息类型处理导航 diff --git a/src/screens/profile/UserProfileScreen.tsx b/src/screens/profile/UserProfileScreen.tsx index cb68d28..b6edb69 100644 --- a/src/screens/profile/UserProfileScreen.tsx +++ b/src/screens/profile/UserProfileScreen.tsx @@ -47,6 +47,7 @@ export const UserProfileScreen: React.FC = ({ mode, user handleMessage, handleBlock, isBlocked, + isBlockedProfile, handleSettings, handleEditProfile, isCurrentUser, @@ -54,7 +55,18 @@ export const UserProfileScreen: React.FC = ({ mode, user } = useUserProfile({ mode, userId, isDesktop, isTablet }); // 渲染帖子列表 - Twitter 风格无边框 - const renderPostList = useCallback((postList: Post[], emptyTitle: string, emptyDesc: string) => { + const renderPostList = useCallback((postList: Post[], emptyTitle: string, emptyDesc: string, blockedTitle?: string, blockedDesc?: string) => { + if (isBlockedProfile && blockedTitle) { + return ( + + ); + } + if (postList.length === 0) { return ( = ({ mode, user })} ); - }, [currentUser?.id, handlePostAction, activeTab]); + }, [currentUser?.id, handlePostAction, activeTab, isBlockedProfile]); // 渲染内容 const renderContent = useCallback(() => { @@ -95,7 +107,9 @@ export const UserProfileScreen: React.FC = ({ mode, user return renderPostList( posts, mode === 'self' ? '还没有帖子' : '这个用户还没有发布任何帖子', - mode === 'self' ? '分享你的想法,发布第一条帖子吧' : '' + mode === 'self' ? '分享你的想法,发布第一条帖子吧' : '', + mode === 'other' ? '已将该用户拉黑' : undefined, + mode === 'other' ? '你已将此用户拉黑,不再显示其帖子' : undefined ); } diff --git a/src/screens/profile/useUserProfile.ts b/src/screens/profile/useUserProfile.ts index 109797d..ea29540 100644 --- a/src/screens/profile/useUserProfile.ts +++ b/src/screens/profile/useUserProfile.ts @@ -34,6 +34,7 @@ export interface UseUserProfileReturn { loading: boolean; refreshing: boolean; isBlocked: boolean; + isBlockedProfile: boolean; // Tab 相关 activeTab: number; @@ -82,6 +83,7 @@ export const useUserProfile = (options: UseUserProfileOptions): UseUserProfileRe const [refreshing, setRefreshing] = useState(false); const [activeTab, setActiveTab] = useState(0); const [isBlocked, setIsBlocked] = useState(false); + const [isBlockedProfile, setIsBlockedProfile] = useState(false); // 获取其他用户数据(other 模式) const loadOtherUserData = useCallback(async (forceRefresh = false) => { @@ -98,6 +100,7 @@ export const useUserProfile = (options: UseUserProfileOptions): UseUserProfileRe const response = await postService.getUserPosts(userId); setPosts(response.list); + setIsBlockedProfile(response.is_blocked || false); return userData; } catch (error) { @@ -407,6 +410,7 @@ case 'like': loading, refreshing, isBlocked, + isBlockedProfile, activeTab, setActiveTab, scrollBottomInset, diff --git a/src/services/post/postService.ts b/src/services/post/postService.ts index d2b4caf..6b1c9e4 100644 --- a/src/services/post/postService.ts +++ b/src/services/post/postService.ts @@ -14,6 +14,7 @@ interface PostListResponse { page: number; page_size: number; total_pages: number; + is_blocked?: boolean; } // 帖子响应 - API直接返回Post对象 @@ -131,7 +132,7 @@ class PostService { userId: string, page = 1, pageSize = 20 - ): Promise> { + ): Promise & { is_blocked?: boolean }> { try { const response = await api.get(`/users/${userId}/posts`, { page, @@ -368,13 +369,13 @@ class PostService { async getUserPostsCursor( userId: string, params: CursorPaginationRequest = {} - ): Promise> { + ): Promise & { is_blocked?: boolean }> { try { const response = await api.get( `/users/${userId}/posts`, params ); - + const data = response.data; if (data && typeof data === 'object' && Array.isArray(data.list)) { @@ -391,6 +392,7 @@ class PostService { next_cursor: currentPage < totalPages ? String(currentPage + 1) : null, prev_cursor: currentPage > 1 ? String(currentPage - 1) : null, has_more: currentPage < totalPages, + is_blocked: data.is_blocked, }; } return { @@ -398,6 +400,7 @@ class PostService { next_cursor: data.next_cursor ?? null, prev_cursor: data.prev_cursor ?? null, has_more: data.has_more ?? false, + is_blocked: data.is_blocked, }; }