import OpenIMSDK, { LoginStatus, OpenIMEvent, type ConversationItem as OpenIMConversationItem, type MessageItem, } from "@openim/rn-client-sdk"; import { useCallback, useEffect, useMemo, useState } from "react"; import type { ConversationItem } from "@/features/im/types/conversation"; function sortConversations(items: OpenIMConversationItem[]) { return [...items].sort((a, b) => { if (a.isPinned !== b.isPinned) { return a.isPinned ? -1 : 1; } return (b.latestMsgSendTime ?? 0) - (a.latestMsgSendTime ?? 0); }); } function upsertConversations( current: OpenIMConversationItem[], changed: OpenIMConversationItem[] ) { if (!changed.length) { return current; } const map = new Map(current.map((item) => [item.conversationID, item])); changed.forEach((item) => { map.set(item.conversationID, item); }); return sortConversations([...map.values()]); } function formatRelativeTime(timestamp: number) { if (!timestamp) { return ""; } const now = Date.now(); const diff = Math.max(0, now - timestamp); const minute = 60 * 1000; const hour = 60 * minute; const day = 24 * hour; if (diff < minute) { return "刚刚"; } if (diff < hour) { return `${Math.floor(diff / minute)}分钟前`; } if (diff < day) { return `${Math.floor(diff / hour)}小时前`; } if (diff < day * 2) { return "昨天"; } const date = new Date(timestamp); const month = `${date.getMonth() + 1}`.padStart(2, "0"); const dayOfMonth = `${date.getDate()}`.padStart(2, "0"); return `${month}-${dayOfMonth}`; } function parseLatestMessagePreview(latestMsg: string, draftText: string) { if (draftText) { return `[草稿] ${draftText}`; } if (!latestMsg) { return ""; } try { const message = JSON.parse(latestMsg) as MessageItem; if (message.textElem?.content) { return message.textElem.content; } if (message.quoteElem?.text) { return `[引用] ${message.quoteElem.text}`; } if (message.pictureElem) { return "[图片]"; } if (message.videoElem) { return "[视频]"; } if (message.fileElem) { return "[文件]"; } if (message.soundElem) { return "[语音]"; } if (message.notificationElem) { return "[系统通知]"; } } catch { return latestMsg; } return "[暂不支持的消息]"; } function toViewModel(item: OpenIMConversationItem): ConversationItem { const title = item.showName || item.userID || item.groupID || "未命名会话"; return { id: item.conversationID, kind: "direct", title, preview: parseLatestMessagePreview(item.latestMsg, item.draftText), timeLabel: formatRelativeTime(item.latestMsgSendTime), avatarUrl: item.faceURL || undefined, unreadCount: item.unreadCount, }; } export function useConversationList() { const [conversations, setConversations] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isLoggedIn, setIsLoggedIn] = useState(false); const [error, setError] = useState(null); const reload = useCallback(async () => { try { setError(null); const status = await OpenIMSDK.getLoginStatus(); const logged = status === LoginStatus.Logged; setIsLoggedIn(logged); if (!logged) { setConversations([]); return; } const list = await OpenIMSDK.getAllConversationList(); setConversations(sortConversations(list)); } catch (err) { const message = err instanceof Error ? err.message : "拉取会话失败"; setError(message); } finally { setIsLoading(false); } }, []); useEffect(() => { reload(); }, [reload]); useEffect(() => { const onConversationChanged = (changed: OpenIMConversationItem[]) => { setConversations((prev) => upsertConversations(prev, changed)); }; const onNewConversation = (created: OpenIMConversationItem[]) => { setConversations((prev) => upsertConversations(prev, created)); }; const onRecvNewMessages = () => { reload(); }; OpenIMSDK.on(OpenIMEvent.OnConversationChanged, onConversationChanged); OpenIMSDK.on(OpenIMEvent.OnNewConversation, onNewConversation); OpenIMSDK.on(OpenIMEvent.OnRecvNewMessages, onRecvNewMessages); return () => { OpenIMSDK.off(OpenIMEvent.OnConversationChanged, onConversationChanged); OpenIMSDK.off(OpenIMEvent.OnNewConversation, onNewConversation); OpenIMSDK.off(OpenIMEvent.OnRecvNewMessages, onRecvNewMessages); }; }, [reload]); const items = useMemo(() => conversations.map(toViewModel), [conversations]); return { items, isLoading, isLoggedIn, error, reload, }; }