UNPKG

@droppii-org/chat-sdk

Version:

Droppii React Chat SDK

128 lines (127 loc) 8.68 kB
"use client"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useEffect, useCallback } from "react"; import { useRouter, useSearchParams, usePathname } from "next/navigation"; import { Input, Avatar, Badge, Empty } from "antd"; import { SessionType } from "@openim/wasm-client-sdk"; import { useConversationList } from "../../hooks/conversation/useConversation"; import { Icon } from "../icon"; import { useChatContext } from "../../context/ChatContext"; import useMessageStore from "../../hooks/zustand/useMessageStore"; import { useMessage } from "../../hooks/message/useMessage"; const parseLatestMessage = (latestMsg, currentUserId) => { var _a; if (!latestMsg) return ""; try { const msgData = JSON.parse(latestMsg); // Check for text message (textElem) if ((_a = msgData.textElem) === null || _a === void 0 ? void 0 : _a.content) { const isMe = currentUserId && msgData.sendID === currentUserId; const sender = isMe ? "Me" : (msgData === null || msgData === void 0 ? void 0 : msgData.senderNickname) || msgData.sendID; return `${sender}: ${msgData.textElem.content}`; } // TODO: Handle other message types (fileElem, videoElem, etc.) // For now, return empty string for non-text messages // This can be enhanced later to show appropriate previews return "Tin nhắn không khả dụng"; } catch (error) { console.error("Error parsing latest message:", error); return ""; } }; // Utility function to format timestamp const formatTimestamp = (timestamp) => { if (!timestamp) return ""; const date = new Date(timestamp); const now = new Date(); const diffInMs = now.getTime() - date.getTime(); const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24)); if (diffInDays === 0) { // Today - show time return date.toLocaleTimeString("vi-VN", { hour: "2-digit", minute: "2-digit", hour12: false, }); } else if (diffInDays === 1) { // Yesterday return "Hôm qua"; } else if (diffInDays < 7) { // This week - show day name return date.toLocaleDateString("vi-VN", { weekday: "long" }); } else { // Older - show date return date.toLocaleDateString("vi-VN", { day: "2-digit", month: "2-digit", }); } }; // Transform API data to UI-friendly format const transformConversationData = (apiData, currentUserId) => { return apiData.map((conv) => (Object.assign(Object.assign({}, conv), { id: conv.conversationID, threadId: conv.conversationID, name: conv.showName || "Unknown User", username: conv.userID || conv.groupID || "", avatar: conv.faceURL || "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg", lastMessage: parseLatestMessage(conv.latestMsg, currentUserId), timestamp: formatTimestamp(conv.latestMsgSendTime), unreadCount: conv.unreadCount, isOnline: true, source: conv.conversationType === 3 ? "group" : "direct" }))); }; const DeskConversationList = ({ onConversationSelect, className = "", }) => { const [searchQuery, setSearchQuery] = useState(""); const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const { user } = useChatContext(); const selectedThreadId = useMessageStore((state) => state.selectedThreadId); const setSelectedThreadId = useMessageStore((state) => state.setSelectedThreadId); const setSelectedSourceId = useMessageStore((state) => state.setSelectedSourceId); const { markConversationMessageAsRead } = useMessage(selectedThreadId); const { conversationList } = useConversationList(selectedThreadId); // Transform real conversation data from the API const conversations = transformConversationData(conversationList || [], user === null || user === void 0 ? void 0 : user.userID); const filteredConversations = conversations.filter((conv) => conv.name.toLowerCase().includes(searchQuery.toLowerCase()) || conv.lastMessage.toLowerCase().includes(searchQuery.toLowerCase())); const handleConversationClick = (conversation) => { const newSearchParams = new URLSearchParams(searchParams); newSearchParams.set("threadId", conversation.id); router.push(`${pathname}?${newSearchParams.toString()}`); setSelectedThreadId(conversation.id); onConversationSelect === null || onConversationSelect === void 0 ? void 0 : onConversationSelect(conversation.id, conversation.id); }; const onSetSelectedSourceId = useCallback(() => { const selectedConversation = conversations.findIndex((conv) => conv.id === selectedThreadId); if (selectedConversation !== -1) { const conversation = conversations[selectedConversation]; const sourceId = conversation.conversationType === SessionType.Group ? conversation.groupID : conversation.userID; setSelectedSourceId(sourceId); } }, [conversationList, selectedThreadId]); useEffect(() => { const threadId = searchParams.get("threadId"); if (threadId) { setSelectedThreadId(threadId); } else if (conversations.length > 0) { setSelectedThreadId(conversations[0].id); const newSearchParams = new URLSearchParams(searchParams); newSearchParams.set("threadId", conversations[0].id); router.replace(`${pathname}?${newSearchParams.toString()}`); } }, [searchParams, conversations.length]); useEffect(() => { if (!!selectedThreadId) { markConversationMessageAsRead(); onSetSelectedSourceId(); } }, [selectedThreadId, onSetSelectedSourceId, markConversationMessageAsRead]); return (_jsxs("div", { className: `flex flex-col h-full bg-white border-r border-gray-200 ${className}`, children: [_jsx("div", { className: "p-3 border-b border-gray-200", children: _jsx(Input, { placeholder: "T\u00ECm ki\u1EBFm", prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "rounded-lg" }) }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [filteredConversations.map((conversation) => (_jsxs("div", { onClick: () => handleConversationClick(conversation), className: `relative p-3 border-b border-gray-100 hover:bg-gray-50 cursor-pointer transition-colors ${selectedThreadId === conversation.threadId ? "bg-blue-50" : "bg-white"}`, children: [selectedThreadId === conversation.threadId && (_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-1 bg-blue-500" })), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "relative flex-shrink-0", children: _jsx(Badge, { dot: conversation.isOnline, status: conversation.isOnline ? "success" : "default", offset: [-2, 36], children: _jsx(Avatar, { size: 48, src: conversation.avatar, alt: conversation.name }) }) }), _jsx("div", { className: "flex-1 min-w-0", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-semibold text-gray-900 text-sm truncate", children: conversation.name }), _jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: conversation.lastMessage })] }), _jsxs("div", { className: "flex flex-col items-end gap-1 ml-2", children: [_jsx("span", { className: "text-xs text-gray-400", children: conversation.timestamp }), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.id))), filteredConversations.length === 0 && (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 48, className: "text-gray-300" }), description: _jsxs("div", { children: [_jsx("p", { className: "text-lg font-medium mb-2 text-gray-500", children: "Kh\u00F4ng t\u00ECm th\u1EA5y cu\u1ED9c tr\u00F2 chuy\u1EC7n" }), _jsx("p", { className: "text-sm text-gray-400", children: searchQuery ? "Thử tìm kiếm với từ khóa khác" : "Chưa có cuộc trò chuyện nào" })] }) }) }))] })] })); }; export default DeskConversationList;