diff --git a/src/screens/message/components/ChatScreen/useChatScreen.ts b/src/screens/message/components/ChatScreen/useChatScreen.ts index c70e518..5e5a231 100644 --- a/src/screens/message/components/ChatScreen/useChatScreen.ts +++ b/src/screens/message/components/ChatScreen/useChatScreen.ts @@ -26,6 +26,7 @@ import { uploadService } from '@/services/upload'; import { ApiError } from '@/services/core'; // 【新架构】使用 MessageManager import { useChat, useGroupTyping, useGroupMuted, messageManager, callStore } from '../../../../stores'; +import { useMessageStore } from '../../../../stores/message'; import { groupService } from '@/services/message'; import { userManager } from '../../../../stores/user'; import { groupManager } from '../../../../stores/group'; @@ -295,17 +296,14 @@ export const useChatScreen = (props?: ChatScreenProps) => { }, [followRestricted, myPrivateSentCount]); // 加载态语义修正: - // isLoadingMessages 在分页加载历史时也会短暂为 true。 - // 若直接驱动 ChatScreen 的 loading,会导致消息列表被卸载重挂载,触发“回到底部”。 - // 这里只在“首屏且尚未展示过消息列表”时展示 loading 占位。 + // 首屏加载:尚未展示过消息列表 且 无消息 → 显示 loading(避免 FlashList 以空 data 挂载后 inverted 布局异常) + // 一旦有消息或加载完成(isLoadingMessages 归 false),即解除 loading useEffect(() => { if (messageManagerMessages.length > 0) { hasShownMessageListRef.current = true; } const shouldShowInitialLoading = - !hasShownMessageListRef.current && - isLoadingMessages && - messageManagerMessages.length === 0; + !hasShownMessageListRef.current && messageManagerMessages.length === 0 && isLoadingMessages; setLoading(shouldShowInitialLoading); }, [isLoadingMessages, messageManagerMessages.length]); @@ -1400,16 +1398,15 @@ export const useChatScreen = (props?: ChatScreenProps) => { const handleDeleteMessage = useCallback(async (messageId: string) => { try { await messageService.deleteMessage(messageId); - // 从本地状态移除(MessageManager 不会自动处理删除) - const updatedMessages = messages.filter(m => m.id !== messageId); - // 注意:这里我们依赖 MessageManager 的数据,所以需要通过其他方式刷新 - // 暂时只删除本地数据库 await messageRepository.delete(messageId); + if (conversationId) { + useMessageStore.getState().removeMessage(conversationId, messageId); + } } catch (error) { console.error('删除消息失败:', error); Alert.alert('删除失败', '无法删除消息'); } - }, [messages]); + }, [conversationId]); // 清空会话的所有聊天记录 const handleClearConversation = useCallback(async () => { diff --git a/src/stores/message/hooks.ts b/src/stores/message/hooks.ts index fe65eb7..034ee8d 100644 --- a/src/stores/message/hooks.ts +++ b/src/stores/message/hooks.ts @@ -18,6 +18,8 @@ import { messageManager } from './MessageManager'; import { useMessageStore } from './store'; import { useUnreadCountQuery } from '../../hooks/useUnreadCount'; +const EMPTY_MESSAGES: MessageResponse[] = []; + // ==================== useConversations - 获取会话列表 ==================== interface UseConversationsReturn { @@ -100,9 +102,10 @@ export function useMessages(conversationId: string | null): UseMessagesReturn { // 使用 useShallow 避免每次返回新数组引用导致的无限循环 const messages = useMessageStore( - useShallow(state => - normalizedConversationId ? (state.messagesMap.get(normalizedConversationId) || []) : [] - ) + useShallow(state => { + if (!normalizedConversationId) return EMPTY_MESSAGES; + return state.messagesMap.get(normalizedConversationId) ?? EMPTY_MESSAGES; + }) ); const isLoadingMessages = useMessageStore(state => normalizedConversationId ? state.loadingMessagesSet.has(normalizedConversationId) : false diff --git a/src/stores/message/services/WSMessageHandler.ts b/src/stores/message/services/WSMessageHandler.ts index c840e0a..ef59e56 100644 --- a/src/stores/message/services/WSMessageHandler.ts +++ b/src/stores/message/services/WSMessageHandler.ts @@ -340,7 +340,7 @@ export class WSMessageHandler implements IWSMessageHandler { id, conversation_id: normalizedConversationId, sender_id, - seq, + seq: typeof seq === 'string' ? parseInt(seq, 10) : (seq ?? 0), segments: segments || [], created_at, status: 'normal', diff --git a/src/stores/message/store.ts b/src/stores/message/store.ts index fcef89b..00a1252 100644 --- a/src/stores/message/store.ts +++ b/src/stores/message/store.ts @@ -85,6 +85,7 @@ export interface MessageActions { removeConversation: (conversationId: string) => void; setMessages: (conversationId: string, messages: MessageResponse[]) => void; mergeMessages: (conversationId: string, incoming: MessageResponse[]) => void; + removeMessage: (conversationId: string, messageId: string) => void; patchMessages: (conversationId: string, patches: Map>) => void; setUnreadCount: (total: number, system: number) => void; setLastSystemMessageAt: (time: string | null) => void; @@ -349,6 +350,19 @@ export const useMessageStore = create((set, get) => ({ }); }, + removeMessage: (conversationId: string, messageId: string) => { + const normalizedId = normalizeConversationId(conversationId); + set(state => { + const existing = state.messagesMap.get(normalizedId); + if (!existing) return state; + const filtered = existing.filter(m => String(m.id) !== String(messageId)); + if (filtered.length === existing.length) return state; + const newMessagesMap = new Map(state.messagesMap); + newMessagesMap.set(normalizedId, filtered); + return { messagesMap: newMessagesMap }; + }); + }, + /** * 原子性 patch:按消息 ID 更新指定字段(如 sender、status) * 不影响其他消息,不丢失并发写入