import React, { useState, useCallback } from 'react' import { View, Text, StyleSheet, FlatList, StatusBar as RNStatusBar, Pressable, RefreshControl, } from 'react-native' import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' import { useTranslation } from 'react-i18next' import { useRouter, useFocusEffect } from 'expo-router' import { PanGestureHandler } from 'react-native-gesture-handler' import { useMessages, type Message } from '@/hooks/use-messages' import { useMessageActions } from '@/hooks/use-message-actions' import { useAnnouncements } from '@/hooks/use-announcements' import { useSwipeNavigation } from '@/hooks/use-swipe-navigation' import LoadingState from '@/components/LoadingState' import ErrorState from '@/components/ErrorState' import PaginationLoader from '@/components/PaginationLoader' import { MessageCard, type Message as MessageCardMessage } from '@/components/message/MessageCard' import { MessageTabBar, type MessageType, TAB_ITEMS } from '@/components/message/MessageTabBar' import { AnnouncementBanner } from '@/components/message/AnnouncementBanner' import { SwipeToDelete } from '@/components/message/SwipeToDelete' import { MessageEmptyState } from '@/components/message/MessageEmptyState' // Navigation map for message click routing const NAVIGATION_MAP: Record = { TEMPLATE_GENERATION_SUCCESS: '/generationRecord', TEMPLATE_GENERATION_FAILED: '/generationRecord', TEMPLATE_LIKED: '/templateDetail', TEMPLATE_FAVORITED: '/templateDetail', TEMPLATE_COMMENTED: '/templateDetail', COMMENT_REPLIED: '/templateDetail', CREDITS_DEDUCTED: '/membership', CREDITS_REFUNDED: '/membership', CREDITS_RECHARGED: '/membership', } // Filter messages by tab type const filterMessagesByTab = (messages: Message[], tabType: MessageType): Message[] => { if (tabType === undefined) return messages return messages.filter(m => m.type === tabType) } export default function MessageScreen() { const { t } = useTranslation() const router = useRouter() const [activeTab, setActiveTab] = useState(undefined) // 获取当前 Tab 索引 const activeTabIndex = TAB_ITEMS.findIndex(tab => tab.type === activeTab) // Hooks const { messages: allMessages, loading, loadingMore, refreshing, error, refetch, loadMore, hasMore, } = useMessages({ limit: 20 }) const { markRead, batchMarkRead, deleteMessage, } = useMessageActions() const { announcements, execute: executeAnnouncements, } = useAnnouncements() // Filter messages based on active tab const filteredMessages = filterMessagesByTab(allMessages, activeTab) // Reload data when tab gains focus useFocusEffect( useCallback(() => { refetch() executeAnnouncements() }, [refetch, executeAnnouncements]) ) // Handle pull-to-refresh const handleRefresh = useCallback(() => { refetch() executeAnnouncements() }, [refetch, executeAnnouncements]) // Handle tab change const handleTabChange = useCallback((type: MessageType) => { setActiveTab(type) }, []) // 左右滑动切换消息类型 Tab const handleSwipeLeft = useCallback(() => { if (activeTabIndex < TAB_ITEMS.length - 1) { setActiveTab(TAB_ITEMS[activeTabIndex + 1].type) } }, [activeTabIndex]) const handleSwipeRight = useCallback(() => { if (activeTabIndex > 0) { setActiveTab(TAB_ITEMS[activeTabIndex - 1].type) } }, [activeTabIndex]) const { handleGestureEvent, handleGestureStateChange } = useSwipeNavigation({ onSwipeLeft: handleSwipeLeft, onSwipeRight: handleSwipeRight, canSwipeLeft: activeTabIndex < TAB_ITEMS.length - 1, canSwipeRight: activeTabIndex > 0, }) // Handle mark all read const handleMarkAllRead = useCallback(async () => { const unreadMessages = filteredMessages.filter(m => !m.isRead) if (unreadMessages.length === 0) return const unreadIds = unreadMessages.map(m => m.id) await batchMarkRead(unreadIds) refetch() }, [filteredMessages, batchMarkRead, refetch]) // Handle message press - navigate and mark as read const handleMessagePress = useCallback(async (message: Message) => { // Mark as read if unread if (!message.isRead) { await markRead(message.id) } // Navigate based on message subType const subType = message.data?.subType if (subType && NAVIGATION_MAP[subType]) { const route = NAVIGATION_MAP[subType] // Add relevant params based on message data const params: Record = {} if (message.data?.templateId) { params.id = message.data.templateId } if (message.data?.generationId) { params.id = message.data.generationId } const queryString = Object.keys(params).length > 0 ? `?${new URLSearchParams(params).toString()}` : '' router.push(`${route}${queryString}` as any) } }, [markRead, router]) // Handle delete message const handleDeleteMessage = useCallback(async (id: string) => { await deleteMessage(id) refetch() }, [deleteMessage, refetch]) // Handle announcement press const handleAnnouncementPress = useCallback((announcement: { id: string; title: string; link?: string }) => { if (announcement.link) { // Navigate to external link or internal route router.push(announcement.link as any) } }, [router]) // Handle load more const handleEndReached = useCallback(() => { if (hasMore && !loadingMore && !loading) { loadMore() } }, [hasMore, loadingMore, loading, loadMore]) // Render message item const renderMessageItem = useCallback(({ item }: { item: Message }) => ( ), [handleDeleteMessage, handleMessagePress]) // Render list footer const renderFooter = useCallback(() => { if (loadingMore) { return } return null }, [loadingMore]) // Render empty state const renderEmptyComponent = useCallback(() => ( ), [t]) // Loading state if (loading && allMessages.length === 0) { return ( {t('message.title')} {t('message.markAllRead')} ) } // Error state if (error && allMessages.length === 0) { return ( {t('message.title')} {t('message.markAllRead')} ) } return ( {/* Header */} {t('message.title')} {t('message.markAllRead')} {/* Announcement Banner */} {announcements.length > 0 && ( )} {/* Tab Bar */} {/* Message List */} item.id} contentContainerStyle={[ styles.listContent, filteredMessages.length === 0 && styles.emptyListContent, ]} showsVerticalScrollIndicator={false} onEndReached={handleEndReached} onEndReachedThreshold={0.5} ListFooterComponent={renderFooter} ListEmptyComponent={renderEmptyComponent} refreshControl={ } /> ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#090A0B', }, gestureContainer: { flex: 1, }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, }, headerTitle: { fontSize: 20, fontWeight: '600', color: '#FFFFFF', }, markAllReadText: { fontSize: 14, color: '#8A8A8A', }, listContent: { paddingHorizontal: 4, paddingTop: 12, paddingBottom: 100, }, emptyListContent: { flex: 1, }, })