feat(message): enhance message store management and UI stability
Some checks failed
Frontend CI / ota-android (push) Successful in 1m40s
Frontend CI / ota-ios (push) Successful in 1m38s
Frontend CI / build-and-push-web (push) Successful in 4m32s
Frontend CI / build-android-apk (push) Failing after 8m53s

Implement `removeMessage` in `useMessageStore` to allow for atomic message deletion from the state. Improve `useChatScreen` loading logic to prevent layout jumps and ensure consistent UI state during initial message loading. Additionally, add type safety for WebSocket sequence numbers and optimize message selector stability.

- Add `removeMessage` action to `useMessageStore` for state-consistent deletions
- Refine `useChatScreen` loading state logic to prevent FlashList layout issues
- Ensure `useMessages` hook returns a stable `EMPTY_MESSAGES` constant
- Cast WebSocket `seq` to integer to ensure consistent numeric comparison
- Update `useChatScreen` to use `useMessageStore` for immediate local deletion feedback
This commit is contained in:
2026-05-25 02:30:59 +08:00
parent f4db4eb1ed
commit 313820ed20
4 changed files with 29 additions and 15 deletions

View File

@@ -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

View File

@@ -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',

View File

@@ -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<string, Partial<MessageResponse>>) => void;
setUnreadCount: (total: number, system: number) => void;
setLastSystemMessageAt: (time: string | null) => void;
@@ -349,6 +350,19 @@ export const useMessageStore = create<MessageStore>((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
* 不影响其他消息,不丢失并发写入