You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
15 KiB
JavaScript
364 lines
15 KiB
JavaScript
import React, { useEffect, useState, useRef } from 'react';
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
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, fetchCleanUnreadMsgCount } from '@/actions/ConversationActions';
|
|
import { ChatItem } from 'react-chat-elements';
|
|
// import ConversationsNewItem from './ConversationsNewItem';
|
|
import { flush, isEmpty, isNotEmpty, stringToColour, TagColorStyle } from '@/utils/commons';
|
|
import useConversationStore from '@/stores/ConversationStore';
|
|
import useAuthStore from '@/stores/AuthStore';
|
|
import ChannelLogo from './ChannelLogo';
|
|
import RegionCodeEmoji from '@/components/RegionCodeEmoji'
|
|
import { ReadIcon } from '@/components/Icons';
|
|
import useStyleStore from '@/stores/StyleStore';
|
|
import { OrderLabelDefaultOptionsMapped, OrderStatusDefaultOptionsMapped } from '@/stores/OrderStore';
|
|
import { whatsappMsgTypeMapped } from '@/channel/bubbleMsgUtils';
|
|
|
|
const OrderSignEmoji = ({ item }) => (
|
|
<>
|
|
<Tooltip color={'cyan'} title={OrderLabelDefaultOptionsMapped[String(item.order_label_id)]?.label}>{OrderLabelDefaultOptionsMapped[String(item.order_label_id)]?.emoji}</Tooltip>
|
|
<Tooltip color={'orange'} title={OrderStatusDefaultOptionsMapped[String(item.order_state_id)]?.label}>{OrderStatusDefaultOptionsMapped[String(item.order_state_id)]?.emoji}</Tooltip>
|
|
<Tooltip color={'volcano'} title={'走团中'}>{item.intour === 1 && item.order_state_id === 5 ? '👣' : ''}</Tooltip>
|
|
</>
|
|
)
|
|
|
|
const NewTagForm = ({onSubmit,...props}) => {
|
|
const [form] = Form.useForm();
|
|
const [subLoding, setSubLoding] = useState(false);
|
|
|
|
const inputRef = useRef(null);
|
|
useEffect(() => {
|
|
inputRef.current?.focus()
|
|
|
|
return () => {}
|
|
}, [])
|
|
|
|
const onFinish = async (values) => {
|
|
// console.log('Received values of form[new_tag]: ', values);
|
|
setSubLoding(true);
|
|
if (typeof onSubmit === 'function') {
|
|
await onSubmit(values.tag_label);
|
|
}
|
|
form.resetFields();
|
|
setSubLoding(false);
|
|
}
|
|
return (
|
|
<Form
|
|
form={form}
|
|
name='new_tag_form'
|
|
layout='inline' size='small'
|
|
initialValues={{}}
|
|
onFinish={onFinish}>
|
|
<Form.Item name={'tag_label'} rules={[{ required: true, message: '请输入标签名' }]}>
|
|
<Input placeholder='新建并设置' ref={inputRef} />
|
|
</Form.Item>
|
|
<Form.Item>
|
|
<Button type='primary' htmlType='submit' loading={subLoding} >
|
|
确定
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
);
|
|
};
|
|
|
|
const ChatListItem = (({item, refreshConversationList,setListUpdateFlag,onSwitchConversation, setNewChatModalVisible,setEditingChat,...props}) => {
|
|
const [mobile] = useStyleStore((state) => [state.mobile]);
|
|
|
|
const routerReplace = mobile === false ? true : false; // : true;
|
|
const routePrefix = mobile === false ? `/order/chat` : `/m/chat`;
|
|
const { order_sn } = useParams();
|
|
const navigate = useNavigate();
|
|
const userId = useAuthStore((state) => state.loginUser.userId);
|
|
const [currentConversation, setCurrentConversation] = useConversationStore((state) => [state.currentConversation, state.setCurrentConversation]);
|
|
const delConversationitem = useConversationStore((state) => state.delConversationitem);
|
|
|
|
const setClosedConversationList = useConversationStore((state) => state.setClosedConversationList);
|
|
|
|
const [currentHandleChat, setCurrentHandleChat] = useState({});
|
|
const [handleLoading, setHandleLoading] = useState(false);
|
|
|
|
const itemTagsKeys = (item.tags || []).map(t => t.key);
|
|
const [tags, addTag] = useConversationStore(state => [state.tags, state.addTag]);
|
|
const handleConversationItemClose = async (item) => {
|
|
setHandleLoading(true);
|
|
await fetchConversationItemClose({ conversationid: item.sn, opisn: item.opi_sn });
|
|
delConversationitem(item);
|
|
if (String(order_sn) === String(item.coli_sn)) {
|
|
navigate(routePrefix, { replace: routerReplace });
|
|
}
|
|
const _clist = await fetchConversationsSearch({ opisn: userId, session_enable: 0 });
|
|
setClosedConversationList(_clist);
|
|
setCurrentHandleChat({});
|
|
setHandleLoading(false);
|
|
};
|
|
|
|
const handleConversationItemUnread = async (item) => {
|
|
setHandleLoading(true);
|
|
if (item.unread_msg_count < 999) {
|
|
await fetchConversationItemUnread({ conversationid: item.sn });
|
|
} else {
|
|
await fetchCleanUnreadMsgCount({ opisn: item.opi_sn, conversationid: item.sn });
|
|
}
|
|
await refreshConversationList(item.lasttime);
|
|
setListUpdateFlag(Math.random());
|
|
setCurrentHandleChat({});
|
|
setHandleLoading(false);
|
|
}
|
|
|
|
const handleConversationItemTop = async (item) => {
|
|
setHandleLoading(true);
|
|
await fetchConversationItemTop({ conversationid: item.sn, top_state: item.top_state === 0 ? 1 : 0 });
|
|
await refreshConversationList(item.lasttime);
|
|
setListUpdateFlag(Math.random());
|
|
setCurrentHandleChat({});
|
|
setHandleLoading(false);
|
|
}
|
|
|
|
const handleConversationItemMuted = async (item) => {
|
|
setHandleLoading(true);
|
|
await fetchConversationItemTop({ conversationid: item.sn, top_state: item.top_state === -1 ? 0 : -1 });
|
|
await refreshConversationList(item.lasttime);
|
|
setListUpdateFlag(Math.random());
|
|
setCurrentHandleChat({});
|
|
setHandleLoading(false);
|
|
}
|
|
|
|
const handleConversationItemTags = async (item, tagKey, tagLabel) => {
|
|
const _tags = (item.tags || []).map(t => t.key);
|
|
setHandleLoading(true);
|
|
if (isNotEmpty(tagKey) && _tags.includes(Number(tagKey))) {
|
|
await deleteConversationTags({ conversationid: item.sn, tag_id: tagKey, opisn: userId })
|
|
} else {
|
|
const rtag = await postConversationTags({
|
|
conversationid: item.sn,
|
|
tag_id: tagKey || '',
|
|
tag_label: tagLabel || '',
|
|
opisn: userId,
|
|
})
|
|
if (isEmpty(tagKey)) {
|
|
addTag({ label: rtag.tag_label, key: rtag.tag_key, value: rtag.tag_key })
|
|
}
|
|
}
|
|
await refreshConversationList(item.lasttime);
|
|
setListUpdateFlag(Math.random());
|
|
setContextMenuOpen(false);
|
|
setCurrentHandleChat({});
|
|
setHandleLoading(false);
|
|
}
|
|
|
|
const [contextMenuOpen, setContextMenuOpen] = useState(false);
|
|
const handleContextMenuOpenChange = (nextOpen, info, item) => {
|
|
if (info.source === 'trigger' || nextOpen) {
|
|
setContextMenuOpen(nextOpen);
|
|
setCurrentHandleChat(nextOpen ? item : {})
|
|
} else {
|
|
// setCurrentHandleChat({});
|
|
}
|
|
};
|
|
|
|
const [openTags, setOpenTags] = useState([]);
|
|
useEffect(() => {
|
|
if (contextMenuOpen === false) {
|
|
setOpenTags([]);
|
|
}
|
|
|
|
return () => {};
|
|
}, [contextMenuOpen])
|
|
|
|
const RenderLastMsg = (msg) => {
|
|
const readFromMsg = msg?.originText || msg?.text || '';
|
|
// const _text = isEmpty(msg) ? '' : msg.type === 'text' ? msg.text.body : `[${(msg?.type || '').toUpperCase()}]`;
|
|
const _text = isEmpty(msg?.type) ? '' : ((whatsappMsgTypeMapped?.[msg.type]?.renderForReply(msg) || {})?.message || readFromMsg)
|
|
return (
|
|
<>{_text}</>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Dropdown
|
|
key={item.sn}
|
|
destroyPopupOnHide
|
|
trigger={['contextMenu']}
|
|
overlayClassName='z-[998]'
|
|
open={contextMenuOpen}
|
|
onOpenChange={(nextOpen, info) => handleContextMenuOpenChange(nextOpen, info, item)}
|
|
menu={{
|
|
items: [
|
|
{ label: item.top_state === 1 ? '取消置顶' : '置顶会话', key: 'top' },
|
|
{ label: item.unread_msg_count > 998 ? '标为已读' : '标记为未读', key: 'unread' },
|
|
// { label: item.top_state === -1 ? '取消静音' : '设为静音', key: 'mute' },
|
|
{
|
|
label: '设置标签',
|
|
key: 'tags',
|
|
children: [
|
|
...tags.map((t) => ({
|
|
...t,
|
|
key: `tag_${t.key}`,
|
|
style: { color: stringToColour(t.label) },
|
|
// icon: itemTagsKeys.includes(t.key) ? <CloseCircleOutlined className='text-red-500' /> : false,
|
|
extra: itemTagsKeys.includes(t.key) ? <MinusCircleOutlined className='text-red-500' /> : false,
|
|
})),
|
|
{
|
|
label: (
|
|
<>
|
|
<Popover
|
|
content={
|
|
<NewTagForm
|
|
onSubmit={(tagLabel) => {
|
|
handleConversationItemTags(item, '', tagLabel)
|
|
setContextMenuOpen(false)
|
|
}}
|
|
/>
|
|
}
|
|
placement='bottom'
|
|
trigger={['click']}>
|
|
<Button type='dashed' size='small' className='m-1'>
|
|
+新标签
|
|
</Button>
|
|
</Popover>
|
|
</>
|
|
),
|
|
key: 'new_tags',
|
|
},
|
|
],
|
|
onTitleClick: ({ key, domEvent }) => {
|
|
// console.log(']]]', key);
|
|
},
|
|
},
|
|
{ label: '编辑联系人', key: 'edit0' },
|
|
|
|
{ type: 'divider' },
|
|
{ label: '移到🗂隐藏列表', key: 'close', danger: true },
|
|
],
|
|
triggerSubMenuAction: 'click',
|
|
openKeys: openTags,
|
|
onOpenChange: (openKeys) => {
|
|
if (!isEmpty(openKeys) && contextMenuOpen) {
|
|
setOpenTags(openKeys)
|
|
}
|
|
},
|
|
onClick: ({ key, domEvent }) => {
|
|
domEvent.stopPropagation()
|
|
if (key.startsWith('tag_')) {
|
|
const tagKey = key.replace('tag_', '')
|
|
return handleConversationItemTags(item, tagKey)
|
|
}
|
|
switch (key) {
|
|
case 'top':
|
|
setContextMenuOpen(false);
|
|
return handleConversationItemTop(item);
|
|
case 'mute':
|
|
setContextMenuOpen(false);
|
|
return handleConversationItemMuted(item);
|
|
case 'unread':
|
|
setContextMenuOpen(false);
|
|
return handleConversationItemUnread(item);
|
|
case 'close':
|
|
setContextMenuOpen(false);
|
|
return handleConversationItemClose(item);
|
|
case 'edit0':
|
|
setOpenTags([])
|
|
setEditingChat({ ...item, is_new: false })
|
|
return setNewChatModalVisible(true)
|
|
case 'new_tags':
|
|
return
|
|
default:
|
|
// setContextMenuOpen(false);
|
|
console.log('unknown key', key)
|
|
|
|
return
|
|
}
|
|
},
|
|
}}>
|
|
<div
|
|
className={[
|
|
'border-0 border-solid border-neutral-200',
|
|
// item.top_state === 1 ? 'bg-stone-100' : '',
|
|
String(item.sn) === String(currentConversation.sn)
|
|
? '__active text-primary bg-whatsapp-bg'
|
|
: item.top_state === 1
|
|
? 'bg-stone-100'
|
|
: '',
|
|
'hover:bg-slate-50',
|
|
(item.sn) === (currentHandleChat?.sn) ? ' bg-slate-50 text-slate-500' : '',
|
|
// String(item.sn) === String(tabSelectedConversation?.sn) ? ' bg-neutral-200' : '',
|
|
].join(' ')}>
|
|
{/* <div className='pl-4 pt-1 text-xs text-right'>
|
|
{tags.map((tag) => <Tag color={tag.color} key={tag.value}>{tag.label}</Tag>)}
|
|
</div> */}
|
|
<Spin spinning={(item.sn) === (currentHandleChat?.sn) && (props.conversationsListLoading || handleLoading)} size='small'>
|
|
<ChatItem
|
|
{...item}
|
|
key={item.sn}
|
|
id={item.sn}
|
|
letterItem={ item.session_type === 1 ? void 0 : { id: item.show_default, letter: (item?.show_default || '').split("@")[0].slice(0, 5) }}
|
|
avatar={ item.session_type === 1 ? 'https://hiana-crm.oss-accelerate.aliyuncs.com/WAMedia/02ab0228-4c3c-4834-ac73-a6dfcdf81938.png' : void 0}
|
|
avatarSize={'small'}
|
|
alt={item.whatsapp_name}
|
|
title={
|
|
<span>
|
|
{/* 🔝 */}
|
|
{/* <RegionCodeEmoji regionCode={item?.last_message?.regionCode || ''} /> */}
|
|
{item.show_default}
|
|
</span>
|
|
// item.conversation_memo ||
|
|
}
|
|
// subtitle={item.coli_id}
|
|
subtitle={
|
|
<>
|
|
{/* <ReadIcon /> */}
|
|
{/* <DeliverIcon /> */}
|
|
{/* <SentIcon /> */}
|
|
{/* <span>{item.coli_id}</span> */}
|
|
{/* <span><ReadIcon />最后一条消息</span> */}
|
|
<span className='text-xs'>
|
|
<RenderLastMsg {...item?.last_message} />
|
|
</span>
|
|
<div className='text-sm'>
|
|
<OrderSignEmoji item={item} />
|
|
{(item?.tags || [])?.map((tag) => (
|
|
<Tag key={tag.label} style={{ ...TagColorStyle(tag.label, true) }} className='text-xs px-0.5 me-0.5'>
|
|
{tag.label}
|
|
</Tag>
|
|
))}
|
|
{/* <span title={'附加备注'}>附加备注</span> */}
|
|
</div>
|
|
</>
|
|
}
|
|
date={item.lasttime || item.last_received_time || item.last_send_time}
|
|
dateString='' // 为了覆盖: 其他客户端发送的失败消息, 推送到此处产生新会话, 但是dataString是长字符串
|
|
unread={item.unread_msg_count > 99 ? 0 : item.unread_msg_count}
|
|
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' : '',
|
|
// ].join(' ')}
|
|
// statusText={<WhatsAppOutlined key={'channel'} className='text-whatsapp' />}
|
|
statusText={
|
|
<ChannelLogo
|
|
channel={flush([
|
|
item?.channels?.phone_number ? 'waba' : null,
|
|
item?.channels?.email ? 'email' : null,
|
|
item?.channels?.whatsapp_phone_number ? 'waba' : null,
|
|
item?.last_message?.source || null, // wai, WABA, email
|
|
])}
|
|
/>
|
|
}
|
|
statusColor={'#fff'}
|
|
onClick={() => onSwitchConversation(item)}
|
|
customStatusComponents={[
|
|
...(item.unread_msg_count > 99 ? [() => <div className='w-4 h-4 bg-red-500 rounded-full' key={'unread'}></div>] : []),
|
|
// () => <span key={'tag'} className='self-end>💎💴❤👑💼🤝💤💔💨✅🕳❓❔❇✳❎🚫❌🎈🎊🎁📜</span>,
|
|
]}
|
|
/>
|
|
</Spin>
|
|
</div>
|
|
</Dropdown>
|
|
</>
|
|
)
|
|
});
|
|
export default ChatListItem;
|