557 lines
16 KiB
TypeScript
557 lines
16 KiB
TypeScript
|
|
/**
|
|||
|
|
* 消息模块 React Hooks
|
|||
|
|
*
|
|||
|
|
* 提供React组件与消息状态管理的集成
|
|||
|
|
* 所有hooks都基于zustand store和订阅机制
|
|||
|
|
* 确保组件能实时获取状态更新
|
|||
|
|
*
|
|||
|
|
* 重构说明:直接使用 zustand store,移除了对 messageManager 的直接依赖
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|||
|
|
import { ConversationResponse, MessageResponse, MessageSegment } from '../../types/dto';
|
|||
|
|
import { useMessageStore, subscriptionManager, normalizeConversationId } from './store';
|
|||
|
|
import type { MessageEvent, MessageSubscriber } from './types';
|
|||
|
|
|
|||
|
|
// ==================== useConversations - 获取会话列表 ====================
|
|||
|
|
|
|||
|
|
interface UseConversationsReturn {
|
|||
|
|
conversations: ConversationResponse[];
|
|||
|
|
isLoading: boolean;
|
|||
|
|
refresh: () => Promise<void>;
|
|||
|
|
loadMore: () => Promise<void>;
|
|||
|
|
hasMore: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取会话列表
|
|||
|
|
* 用于 MessageListScreen 等需要显示会话列表的组件
|
|||
|
|
*/
|
|||
|
|
export function useConversations(
|
|||
|
|
fetchConversations: (forceRefresh: boolean, source: string) => Promise<void>,
|
|||
|
|
loadMoreConversations: () => Promise<void>,
|
|||
|
|
canLoadMore: () => boolean,
|
|||
|
|
initialize: () => Promise<void>
|
|||
|
|
): UseConversationsReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [conversations, setConversations] = useState<ConversationResponse[]>(() => store.getConversations());
|
|||
|
|
const [isLoading, setIsLoading] = useState(() => store.isLoading());
|
|||
|
|
const [hasMore, setHasMore] = useState(() => canLoadMore());
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
// 订阅事件
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
switch (event.type) {
|
|||
|
|
case 'conversations_updated':
|
|||
|
|
setConversations(store.getConversations());
|
|||
|
|
setHasMore(canLoadMore());
|
|||
|
|
break;
|
|||
|
|
case 'conversations_loading':
|
|||
|
|
setIsLoading(!!event.payload?.loading);
|
|||
|
|
break;
|
|||
|
|
case 'connection_changed':
|
|||
|
|
// 连接状态变化时可能需要刷新
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 初始化
|
|||
|
|
initialize().catch(error => {
|
|||
|
|
console.error('[useConversations] 初始化失败:', error);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 冷启动兜底:延迟做一次强制刷新
|
|||
|
|
const coldStartSyncTimer = setTimeout(() => {
|
|||
|
|
fetchConversations(true, 'hooks-initial-refresh').catch(error => {
|
|||
|
|
console.error('[useConversations] 冷启动强制刷新失败:', error);
|
|||
|
|
});
|
|||
|
|
}, 1200);
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
clearTimeout(coldStartSyncTimer);
|
|||
|
|
unsubscribe();
|
|||
|
|
};
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const refresh = useCallback(async () => {
|
|||
|
|
await fetchConversations(true, 'hooks-manual-refresh');
|
|||
|
|
}, [fetchConversations]);
|
|||
|
|
|
|||
|
|
const loadMore = useCallback(async () => {
|
|||
|
|
await loadMoreConversations();
|
|||
|
|
}, [loadMoreConversations]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
conversations,
|
|||
|
|
isLoading,
|
|||
|
|
refresh,
|
|||
|
|
loadMore,
|
|||
|
|
hasMore,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useMessages - 获取指定会话的消息 ====================
|
|||
|
|
|
|||
|
|
interface UseMessagesReturn {
|
|||
|
|
messages: MessageResponse[];
|
|||
|
|
isLoading: boolean;
|
|||
|
|
hasMore: boolean;
|
|||
|
|
loadMore: () => Promise<void>;
|
|||
|
|
refresh: () => Promise<void>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取指定会话的消息
|
|||
|
|
*/
|
|||
|
|
export function useMessages(
|
|||
|
|
conversationId: string,
|
|||
|
|
fetchMessages: (conversationId: string) => Promise<void>,
|
|||
|
|
loadMoreMessages: (conversationId: string, beforeSeq: number, limit?: number) => Promise<MessageResponse[]>
|
|||
|
|
): UseMessagesReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const normalizedConversationId = normalizeConversationId(conversationId);
|
|||
|
|
|
|||
|
|
const [messages, setMessages] = useState<MessageResponse[]>(() =>
|
|||
|
|
store.getMessages(normalizedConversationId)
|
|||
|
|
);
|
|||
|
|
const [isLoading, setIsLoading] = useState(() =>
|
|||
|
|
store.isLoadingMessages(normalizedConversationId)
|
|||
|
|
);
|
|||
|
|
const [hasMore, setHasMore] = useState(true);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'messages_updated' && event.payload.conversationId === normalizedConversationId) {
|
|||
|
|
setMessages(event.payload.messages);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 激活会话
|
|||
|
|
fetchMessages(normalizedConversationId).catch(error => {
|
|||
|
|
console.error('[useMessages] 激活会话失败:', error);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
// 清理活动会话
|
|||
|
|
if (store.getActiveConversation() === normalizedConversationId) {
|
|||
|
|
store.setCurrentConversation(null);
|
|||
|
|
}
|
|||
|
|
unsubscribe();
|
|||
|
|
};
|
|||
|
|
}, [normalizedConversationId]);
|
|||
|
|
|
|||
|
|
const loadMore = useCallback(async () => {
|
|||
|
|
const currentMessages = store.getMessages(conversationId);
|
|||
|
|
if (currentMessages.length === 0) return;
|
|||
|
|
|
|||
|
|
// 消息数组在 store 内保持 seq 升序,首项即最早消息
|
|||
|
|
const minSeq = currentMessages[0]?.seq;
|
|||
|
|
if (minSeq === undefined) return;
|
|||
|
|
|
|||
|
|
const loadedMessages = await loadMoreMessages(conversationId, minSeq, 20);
|
|||
|
|
setHasMore(loadedMessages.length > 0);
|
|||
|
|
}, [conversationId, loadMoreMessages]);
|
|||
|
|
|
|||
|
|
const refresh = useCallback(async () => {
|
|||
|
|
await fetchMessages(conversationId);
|
|||
|
|
setMessages(store.getMessages(conversationId));
|
|||
|
|
}, [conversationId, fetchMessages]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
messages,
|
|||
|
|
isLoading,
|
|||
|
|
hasMore,
|
|||
|
|
loadMore,
|
|||
|
|
refresh,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useSendMessage - 发送消息 ====================
|
|||
|
|
|
|||
|
|
interface UseSendMessageReturn {
|
|||
|
|
sendMessage: (segments: MessageSegment[], options?: { replyToId?: string }) => Promise<MessageResponse | null>;
|
|||
|
|
isSending: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 发送消息
|
|||
|
|
*/
|
|||
|
|
export function useSendMessage(
|
|||
|
|
conversationId: string,
|
|||
|
|
sendMessageService: (conversationId: string, segments: MessageSegment[], options?: { replyToId?: string }) => Promise<MessageResponse | null>
|
|||
|
|
): UseSendMessageReturn {
|
|||
|
|
const [isSending, setIsSending] = useState(false);
|
|||
|
|
|
|||
|
|
const sendMessage = useCallback(async (segments: MessageSegment[], options?: { replyToId?: string }) => {
|
|||
|
|
setIsSending(true);
|
|||
|
|
try {
|
|||
|
|
const message = await sendMessageService(conversationId, segments, options);
|
|||
|
|
return message;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[useSendMessage] 发送消息失败:', error);
|
|||
|
|
return null;
|
|||
|
|
} finally {
|
|||
|
|
setIsSending(false);
|
|||
|
|
}
|
|||
|
|
}, [conversationId, sendMessageService]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
sendMessage,
|
|||
|
|
isSending,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useMarkAsRead - 标记已读 ====================
|
|||
|
|
|
|||
|
|
interface UseMarkAsReadReturn {
|
|||
|
|
markAsRead: (seq: number) => Promise<void>;
|
|||
|
|
markAllAsRead: () => Promise<void>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 标记已读
|
|||
|
|
*/
|
|||
|
|
export function useMarkAsRead(
|
|||
|
|
conversationId: string,
|
|||
|
|
markAsReadService: (conversationId: string, seq: number) => Promise<void>,
|
|||
|
|
markAllAsReadService: () => Promise<void>
|
|||
|
|
): UseMarkAsReadReturn {
|
|||
|
|
const markAsRead = useCallback(async (seq: number) => {
|
|||
|
|
await markAsReadService(conversationId, seq);
|
|||
|
|
}, [conversationId, markAsReadService]);
|
|||
|
|
|
|||
|
|
const markAllAsRead = useCallback(async () => {
|
|||
|
|
await markAllAsReadService();
|
|||
|
|
}, [markAllAsReadService]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
markAsRead,
|
|||
|
|
markAllAsRead,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useUnreadCount - 获取未读数 ====================
|
|||
|
|
|
|||
|
|
interface UseUnreadCountReturn {
|
|||
|
|
totalUnreadCount: number;
|
|||
|
|
systemUnreadCount: number;
|
|||
|
|
refresh: () => Promise<void>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取未读数
|
|||
|
|
*/
|
|||
|
|
export function useUnreadCount(fetchUnreadCount: () => Promise<void>): UseUnreadCountReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [counts, setCounts] = useState(() => store.getUnreadCount());
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'unread_count_updated') {
|
|||
|
|
setCounts(store.getUnreadCount());
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 初始获取
|
|||
|
|
fetchUnreadCount();
|
|||
|
|
|
|||
|
|
return unsubscribe;
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const refresh = useCallback(async () => {
|
|||
|
|
await fetchUnreadCount();
|
|||
|
|
}, [fetchUnreadCount]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
totalUnreadCount: counts.total,
|
|||
|
|
systemUnreadCount: counts.system,
|
|||
|
|
refresh,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useSystemUnreadCount - 获取系统消息未读数 ====================
|
|||
|
|
|
|||
|
|
interface UseSystemUnreadCountReturn {
|
|||
|
|
systemUnreadCount: number;
|
|||
|
|
setSystemUnreadCount: (count: number) => void;
|
|||
|
|
incrementSystemUnreadCount: () => void;
|
|||
|
|
decrementSystemUnreadCount: (count?: number) => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取系统消息未读数
|
|||
|
|
*/
|
|||
|
|
export function useSystemUnreadCount(
|
|||
|
|
setSystemUnreadCountService: (count: number) => void,
|
|||
|
|
incrementSystemUnreadCountService: () => void,
|
|||
|
|
decrementSystemUnreadCountService: (count?: number) => void
|
|||
|
|
): UseSystemUnreadCountReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [systemUnreadCount, setSystemUnreadCountState] = useState(() =>
|
|||
|
|
store.getUnreadCount().system
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'unread_count_updated') {
|
|||
|
|
setSystemUnreadCountState(store.getUnreadCount().system);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return unsubscribe;
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const setSystemUnreadCount = useCallback((count: number) => {
|
|||
|
|
setSystemUnreadCountService(count);
|
|||
|
|
}, [setSystemUnreadCountService]);
|
|||
|
|
|
|||
|
|
const incrementSystemUnreadCount = useCallback(() => {
|
|||
|
|
incrementSystemUnreadCountService();
|
|||
|
|
}, [incrementSystemUnreadCountService]);
|
|||
|
|
|
|||
|
|
const decrementSystemUnreadCount = useCallback((count = 1) => {
|
|||
|
|
decrementSystemUnreadCountService(count);
|
|||
|
|
}, [decrementSystemUnreadCountService]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
systemUnreadCount,
|
|||
|
|
setSystemUnreadCount,
|
|||
|
|
incrementSystemUnreadCount,
|
|||
|
|
decrementSystemUnreadCount,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useConversation - 获取单个会话 ====================
|
|||
|
|
|
|||
|
|
interface UseConversationReturn {
|
|||
|
|
conversation: ConversationResponse | null;
|
|||
|
|
refresh: () => Promise<void>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取单个会话
|
|||
|
|
*/
|
|||
|
|
export function useConversation(
|
|||
|
|
conversationId: string,
|
|||
|
|
fetchConversationDetail: (conversationId: string) => Promise<ConversationResponse | null>
|
|||
|
|
): UseConversationReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [conversation, setConversation] = useState<ConversationResponse | null>(() =>
|
|||
|
|
store.getConversation(conversationId)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
setConversation(store.getConversation(conversationId));
|
|||
|
|
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'conversations_updated') {
|
|||
|
|
const updated = store.getConversation(conversationId);
|
|||
|
|
setConversation(updated);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return unsubscribe;
|
|||
|
|
}, [conversationId]);
|
|||
|
|
|
|||
|
|
const refresh = useCallback(async () => {
|
|||
|
|
await fetchConversationDetail(conversationId);
|
|||
|
|
setConversation(store.getConversation(conversationId));
|
|||
|
|
}, [conversationId, fetchConversationDetail]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
conversation,
|
|||
|
|
refresh,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useMessageManager - 通用订阅 ====================
|
|||
|
|
|
|||
|
|
interface UseMessageManagerReturn {
|
|||
|
|
isConnected: boolean;
|
|||
|
|
subscribe: (subscriber: MessageSubscriber) => () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 通用消息订阅
|
|||
|
|
*/
|
|||
|
|
export function useMessageManager(initialize: () => Promise<void>): UseMessageManagerReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [isConnected, setIsConnected] = useState(() => store.isConnected());
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'connection_changed') {
|
|||
|
|
setIsConnected(event.payload.connected);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
initialize().then(() => {
|
|||
|
|
setIsConnected(store.isConnected());
|
|||
|
|
}).catch(error => {
|
|||
|
|
console.error('[useMessageManager] 初始化失败:', error);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return unsubscribe;
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const subscribe = useCallback((subscriber: MessageSubscriber) => {
|
|||
|
|
return subscriptionManager.subscribe(subscriber);
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
isConnected,
|
|||
|
|
subscribe,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useCreateConversation - 创建会话 ====================
|
|||
|
|
|
|||
|
|
interface UseCreateConversationReturn {
|
|||
|
|
createConversation: (userId: string) => Promise<ConversationResponse | null>;
|
|||
|
|
isCreating: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建会话
|
|||
|
|
*/
|
|||
|
|
export function useCreateConversation(
|
|||
|
|
createConversationService: (userId: string) => Promise<ConversationResponse | null>
|
|||
|
|
): UseCreateConversationReturn {
|
|||
|
|
const [isCreating, setIsCreating] = useState(false);
|
|||
|
|
|
|||
|
|
const createConversation = useCallback(async (userId: string) => {
|
|||
|
|
setIsCreating(true);
|
|||
|
|
try {
|
|||
|
|
const conversation = await createConversationService(userId);
|
|||
|
|
return conversation;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[useCreateConversation] 创建会话失败:', error);
|
|||
|
|
return null;
|
|||
|
|
} finally {
|
|||
|
|
setIsCreating(false);
|
|||
|
|
}
|
|||
|
|
}, [createConversationService]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
createConversation,
|
|||
|
|
isCreating,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useUpdateConversation - 更新会话 ====================
|
|||
|
|
|
|||
|
|
interface UseUpdateConversationReturn {
|
|||
|
|
updateConversation: (conversationId: string, updates: Partial<ConversationResponse>) => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新会话
|
|||
|
|
*/
|
|||
|
|
export function useUpdateConversation(
|
|||
|
|
updateConversationService: (conversationId: string, updates: Partial<ConversationResponse>) => void
|
|||
|
|
): UseUpdateConversationReturn {
|
|||
|
|
const updateConversation = useCallback((conversationId: string, updates: Partial<ConversationResponse>) => {
|
|||
|
|
updateConversationService(conversationId, updates);
|
|||
|
|
}, [updateConversationService]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
updateConversation,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useGroupTyping - 群聊输入状态 ====================
|
|||
|
|
|
|||
|
|
interface UseGroupTypingReturn {
|
|||
|
|
typingUsers: string[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取群聊输入状态
|
|||
|
|
*/
|
|||
|
|
export function useGroupTyping(groupId: string): UseGroupTypingReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [typingUsers, setTypingUsers] = useState<string[]>(() =>
|
|||
|
|
store.getTypingUsers(groupId)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
setTypingUsers(store.getTypingUsers(groupId));
|
|||
|
|
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'typing_status' && event.payload.groupId === groupId) {
|
|||
|
|
setTypingUsers(event.payload.typingUsers);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return unsubscribe;
|
|||
|
|
}, [groupId]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
typingUsers,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useGroupMuted - 群聊禁言状态 ====================
|
|||
|
|
|
|||
|
|
interface UseGroupMutedReturn {
|
|||
|
|
isMuted: boolean;
|
|||
|
|
setMutedStatus: (muted: boolean) => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取群聊禁言状态
|
|||
|
|
*/
|
|||
|
|
export function useGroupMuted(groupId: string): UseGroupMutedReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [isMuted, setIsMuted] = useState(() => store.isMuted(groupId));
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
setIsMuted(store.isMuted(groupId));
|
|||
|
|
|
|||
|
|
const unsubscribe = subscriptionManager.subscribe((event: MessageEvent) => {
|
|||
|
|
if (event.type === 'group_notice' && event.payload.groupId === groupId) {
|
|||
|
|
if (event.payload.noticeType === 'muted' || event.payload.noticeType === 'unmuted') {
|
|||
|
|
setIsMuted(event.payload.noticeType === 'muted');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return unsubscribe;
|
|||
|
|
}, [groupId]);
|
|||
|
|
|
|||
|
|
const setMutedStatus = useCallback((muted: boolean) => {
|
|||
|
|
store.setMutedStatus(groupId, muted);
|
|||
|
|
}, [groupId]);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
isMuted,
|
|||
|
|
setMutedStatus,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== useActiveConversation - 活动会话 ====================
|
|||
|
|
|
|||
|
|
interface UseActiveConversationReturn {
|
|||
|
|
activeConversationId: string | null;
|
|||
|
|
setActiveConversation: (conversationId: string | null) => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取/设置活动会话
|
|||
|
|
*/
|
|||
|
|
export function useActiveConversation(): UseActiveConversationReturn {
|
|||
|
|
const store = useMessageStore();
|
|||
|
|
const [activeConversationId, setActiveConversationId] = useState<string | null>(() =>
|
|||
|
|
store.getActiveConversation()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const setActiveConversation = useCallback((conversationId: string | null) => {
|
|||
|
|
store.setCurrentConversation(conversationId);
|
|||
|
|
setActiveConversationId(conversationId);
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
activeConversationId,
|
|||
|
|
setActiveConversation,
|
|||
|
|
};
|
|||
|
|
}
|