From d39d5f4da0bc5279fc50ee32611113267f3ca2d2 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 7 Jan 2025 18:50:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=89=8D=E7=AB=AF):=20=E8=AE=BE=E4=B8=BA?= =?UTF-8?q?=E9=9D=99=E9=9F=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/ConversationStore.js | 49 ++++++++++++------- src/utils/commons.js | 20 ++++++++ src/views/ChatWindow.jsx | 4 +- .../Online/Components/ChatListItem.jsx | 46 ++++++++++++----- .../Online/ConversationsList.jsx | 1 + 5 files changed, 88 insertions(+), 32 deletions(-) diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index 18661d5..6ed302a 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -1,6 +1,6 @@ import { create } from 'zustand'; import { RealTimeAPI } from '@/channel/realTimeAPI'; -import { olog, isEmpty, groupBy, sortArrayByOrder, logWebsocket, pick } from '@/utils/commons'; +import { olog, isEmpty, groupBy, sortArrayByOrder, logWebsocket, pick, sortKeys, omit, sortObjectsByKeysMap } from '@/utils/commons'; import { receivedMsgTypeMapped, handleNotification } from '@/channel/bubbleMsgUtils'; import { fetchConversationsList, fetchTemplates, fetchConversationsSearch, UNREAD_MARK, fetchTags } from '@/actions/ConversationActions'; import { devtools } from 'zustand/middleware'; @@ -12,6 +12,16 @@ const replaceObjectsByKey = (arr1, arr2, key) => { return arr1.map(item => map.has(item[key]) ? map.get(item[key]) : item); } +const sortConversationList = (list) => { + const mergedListMapped = groupBy(list, 'top_state'); + const topValOrder = Object.keys(mergedListMapped).filter(ss => ss !== '1').sort((a, b) => b - a); + const pagelist = topValOrder.reduce((r, topVal) => r.concat(mergedListMapped[String(topVal)]), []); + return { + topList: mergedListMapped['1'] || [], + pageList: pagelist, + } +}; + // const WS_URL = 'ws://202.103.68.144:8888/whatever/'; // const WS_URL = 'ws://120.79.9.217:10022/whatever/'; const conversationRow = { @@ -259,12 +269,12 @@ const conversationSlice = (set, get) => ({ const _len = hasCurrent ? 0 : conversationsList.unshift(currentConversation); } - const conversationsTopStateMapped = groupBy(conversationsList, 'top_state'); + const { topList, pageList } = sortConversationList(conversationsList); return set({ - topList: conversationsTopStateMapped[1] || [], + topList, // conversationsList: conversationsTopStateMapped[0], - pageList: conversationsTopStateMapped[0] || [], + pageList, conversationsList, activeConversations: { ...conversationsMapped, ...activeConversations } }) @@ -296,11 +306,11 @@ const conversationSlice = (set, get) => ({ } const refreshTotalNotify = mergedList.reduce((r, c) => r+(c.unread_msg_count === UNREAD_MARK ? 0 : c.unread_msg_count), 0); - const mergedListMapped = groupBy(mergedList, 'top_state'); + const { topList, pageList } = sortConversationList(mergedList) return set((state) => ({ - topList: mergedListMapped[1] || [], - pageList: mergedListMapped[0] || [], + topList, + pageList, conversationsList: mergedList, activeConversations: mergedListMsgs, totalNotify: refreshTotalNotify, @@ -312,11 +322,11 @@ const conversationSlice = (set, get) => ({ const targetId = conversation.sn; const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId)); conversationsList.splice(targetIndex, 1); - const mergedListMapped = groupBy(conversationsList, 'top_state'); + const { topList, pageList } = sortConversationList(conversationsList) return set({ - topList: mergedListMapped['1'] || [], - pageList: mergedListMapped['0'] || [], + topList, + pageList, conversationsList: [...conversationsList], activeConversations: { ...activeConversations, [`${targetId}`]: [] }, currentConversation: {}, @@ -361,11 +371,11 @@ const conversationSlice = (set, get) => ({ ...conversation, }) : null; - const mergedListMapped = groupBy(conversationsList, 'top_state'); + const { topList, pageList } = sortConversationList(conversationsList) return set({ - topList: mergedListMapped['1'] || [], - pageList: mergedListMapped['0'] || [], + topList, + pageList, conversationsList: [...conversationsList] }); }, @@ -447,12 +457,12 @@ const messageSlice = (set, get) => ({ }]; } const mergedList = [...newConversations, ...conversationsList] - const mergedListMapped = groupBy(mergedList, 'top_state'); setFilter({ loadNextPage: true }); + const { topList, pageList } = sortConversationList(mergedList); return set({ - topList: mergedListMapped['1'] || [], - pageList: mergedListMapped['0'] || [], + topList, + pageList, conversationsList: mergedList, activeConversations: { ...activeConversations, [String(targetId)]: targetMsgs }, }); @@ -503,7 +513,6 @@ const messageSlice = (set, get) => ({ // console.log('find in list, i:', targetIndex); // console.log('find in list, chat updated and Top: \n', JSON.stringify(newConversation, null, 2)); // console.log('list updated : \n', JSON.stringify(conversationsList, null, 2)); - const mergedListMapped = groupBy(conversationsList, 'top_state'); setFilter({ loadNextPage: true }); const isCurrent = Number(targetId) === Number(currentConversation.sn); @@ -515,10 +524,12 @@ const messageSlice = (set, get) => ({ last_message: message, } : {...currentConversation, last_message: message,}; + + const { topList, pageList } = sortConversationList(conversationsList); return set({ currentConversation: updatedCurrent, - topList: mergedListMapped['1'] || [], - pageList: mergedListMapped['0'] || [], + topList, + pageList, conversationsList: [...conversationsList], totalNotify: totalNotify + (message.sender === 'me' ? 0 : 1), activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] }, diff --git a/src/utils/commons.js b/src/utils/commons.js index 90217b2..a14b690 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -119,6 +119,26 @@ export const sortKeys = (obj) => .sort() .reduce((a, k2) => ({ ...a, [k2]: obj[k2] }), {}); +export function sortObjectsByKeysMap(objects, keyOrder) { + if (!objects) return {} // Handle null/undefined input + if (!keyOrder || keyOrder.length === 0) return objects + + const objectMap = new Map(Object.entries(objects)) + const sortedMap = new Map() + + for (const key of keyOrder) { + if (objectMap.has(key)) { + sortedMap.set(key, objectMap.get(key)) + objectMap.delete(key) // Optimization: Remove from original map after adding + } + } + // Add remaining keys + for (const [key, value] of objectMap) { + sortedMap.set(key, value) + } + return Object.fromEntries(sortedMap) +} + /** * 数组排序, 给定排序数组 * @param {array} items 需要排序的数组 diff --git a/src/views/ChatWindow.jsx b/src/views/ChatWindow.jsx index f3c1d37..e19b4c4 100644 --- a/src/views/ChatWindow.jsx +++ b/src/views/ChatWindow.jsx @@ -34,9 +34,9 @@ const ChatWindow = () => { collapsible={true} breakpoint='xl' collapsedWidth={73} - collapsed={collapsedLeft} + collapsed={false} onBreakpoint={(broken) => { - setCollapsedLeft(broken) + // setCollapsedLeft(broken) setCollapsedRight(broken) }} trigger={null}> diff --git a/src/views/Conversations/Online/Components/ChatListItem.jsx b/src/views/Conversations/Online/Components/ChatListItem.jsx index 91aea2b..de77ca6 100644 --- a/src/views/Conversations/Online/Components/ChatListItem.jsx +++ b/src/views/Conversations/Online/Components/ChatListItem.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Dropdown, Input, Button, Tag, Popover, Form, Tooltip } from 'antd'; +import { Dropdown, Input, Button, Tag, Popover, Form, Tooltip, Spin } from 'antd'; import { CloseCircleOutlined, MinusCircleOutlined } from '@ant-design/icons'; import { fetchConversationItemClose, fetchConversationsSearch, fetchConversationItemUnread, fetchConversationItemTop, postConversationTags, deleteConversationTags } from '@/actions/ConversationActions'; import { ChatItem } from 'react-chat-elements'; @@ -75,6 +75,8 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch const setClosedConversationList = useConversationStore((state) => state.setClosedConversationList); + const [currentHandleChat, setCurrentHandleChat] = useState({}); + const itemTagsKeys = (item.tags || []).map(t => t.key); const [tags, addTag] = useConversationStore(state => [state.tags, state.addTag]); const handleConversationItemClose = async (item) => { @@ -85,18 +87,28 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch } const _clist = await fetchConversationsSearch({ opisn: userId, session_enable: 0 }); setClosedConversationList(_clist); + setCurrentHandleChat({}); }; const handleConversationItemUnread = async (item) => { await fetchConversationItemUnread({ conversationid: item.sn }); await refreshConversationList(item.lasttime); setListUpdateFlag(Math.random()); + setCurrentHandleChat({}); } const handleConversationItemTop = async (item) => { await fetchConversationItemTop({ conversationid: item.sn, top_state: item.top_state === 0 ? 1 : 0 }); await refreshConversationList(item.lasttime); setListUpdateFlag(Math.random()); + setCurrentHandleChat({}); + } + + const handleConversationItemMuted = async (item) => { + await fetchConversationItemTop({ conversationid: item.sn, top_state: item.top_state === -1 ? 0 : -1 }); + await refreshConversationList(item.lasttime); + setListUpdateFlag(Math.random()); + setCurrentHandleChat({}); } const handleConversationItemTags = async (item, tagKey, tagLabel) => { @@ -117,12 +129,16 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch await refreshConversationList(item.lasttime); setListUpdateFlag(Math.random()); setContextMenuOpen(false); + setCurrentHandleChat({}); } const [contextMenuOpen, setContextMenuOpen] = useState(false); - const handleContextMenuOpenChange = (nextOpen, info) => { + const handleContextMenuOpenChange = (nextOpen, info, item) => { if (info.source === 'trigger' || nextOpen) { setContextMenuOpen(nextOpen); + setCurrentHandleChat(nextOpen ? item : {}) + } else { + // setCurrentHandleChat({}); } }; @@ -152,11 +168,12 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch trigger={['contextMenu']} overlayClassName='z-[998]' open={contextMenuOpen} - onOpenChange={handleContextMenuOpenChange} + onOpenChange={(nextOpen, info) => handleContextMenuOpenChange(nextOpen, info, item)} menu={{ items: [ item.top_state === 1 ? { label: '取消置顶', key: 'top' } : { label: '置顶会话', key: 'top' }, { label: '标记为未读', key: 'unread' }, + item.top_state === -1 ? { label: '取消静音', key: 'mute' } : { label: '设为静音', key: 'mute' }, { label: '设置标签', key: 'tags', @@ -214,14 +231,17 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch } switch (key) { case 'top': - setContextMenuOpen(false) - return handleConversationItemTop(item) + setContextMenuOpen(false); + return handleConversationItemTop(item); + case 'mute': + setContextMenuOpen(false); + return handleConversationItemMuted(item); case 'unread': - setContextMenuOpen(false) - return handleConversationItemUnread(item) + setContextMenuOpen(false); + return handleConversationItemUnread(item); case 'close': - setContextMenuOpen(false) - return handleConversationItemClose(item) + setContextMenuOpen(false); + return handleConversationItemClose(item); case 'edit0': setOpenTags([]) setEditingChat({ ...item, is_new: false }) @@ -245,11 +265,14 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch : item.top_state === 1 ? 'bg-stone-100' : '', + 'hover:bg-slate-100', + (item.sn) === (currentHandleChat?.sn) ? ' bg-slate-100 text-slate-500' : '', // String(item.sn) === String(tabSelectedConversation?.sn) ? ' bg-neutral-200' : '', ].join(' ')}> {/*
{tags.map((tag) => {tag.label})}
*/} + 99 ? 0 : item.unread_msg_count} - // muted={item.unread_msg_count>0} - // showMute={item.unread_msg_count>0} + muted={item.top_state === -1} + showMute={item.top_state === -1} // className={[ // String(item.sn) === String(currentConversation.sn) ? '__active text-primary bg-whatsapp-bg' : '', // String(item.sn) === String(tabSelectedConversation?.sn) ? ' bg-neutral-200' : '', @@ -313,6 +336,7 @@ const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitch // () =>