上传文件的类型限制;高德地图; ChatHistory;会话列表: 显示订单号; 未读消息设置已读接口

dev/mobile
Lei OT 2 years ago
parent 4c7aeefefe
commit 0839335c2d

@ -37,7 +37,11 @@ export const fetchOrderConversationsList = async (params) => {
}; };
export const fetchConversationItemClose = async (body) => { export const fetchConversationItemClose = async (body) => {
const getParams = body ? new URLSearchParams(body).toString() : ''; const { result } = await fetchJSON(`${API_HOST}/closeconversation`, body);
const { result } = await fetchJSON(`${API_HOST}/closeconversation?${getParams}`);
return result; return result;
}; };
export const fetchCleanUnreadMsgCount = async (params) => {
const { errcode, result } = await fetchJSON(`${API_HOST}/clean_unread_msg_count`, params);
return errcode !== 0 ? {} : result;
};

@ -266,9 +266,6 @@ export const whatsappMsgTypeMapped = {
}, },
}, },
originText: msg.image?.caption || '', originText: msg.image?.caption || '',
onOpen: () => {
console.log('Open image', msg.image.link);
},
}), }),
renderForReply: (msg) => ({ renderForReply: (msg) => ({
id: msg.wamid, id: msg.wamid,
@ -377,7 +374,7 @@ export const whatsappMsgTypeMapped = {
id: msg.wamid, id: msg.wamid,
title: `位置信息 ${msg.location.name || ''} ↓打开高德地图`, title: `位置信息 ${msg.location.name || ''} ↓打开高德地图`,
text: msg.location.address, // 地址 text: msg.location.address, // 地址
src: `https://uri.amap.com/marker?position=${msg.location.longitude},${msg.location.latitude}`, src: `https://uri.amap.com/marker?position=${msg.location.longitude},${msg.location.latitude}&callnative=1`,
data: { data: {
longitude: msg.location?.longitude, longitude: msg.location?.longitude,
latitude: msg.location?.latitude, latitude: msg.location?.latitude,

@ -70,7 +70,7 @@ function AuthApp() {
let interval; let interval;
if (totalNotify > 0) { if (totalNotify > 0) {
interval = setInterval(() => { interval = setInterval(() => {
document.title = isTitleVisible ? `🔔🔥💬【${totalNotify}条新消息】` : '聊天式销售平台'; document.title = isTitleVisible ? `🔔🔥💬【${totalNotify}条新消息】` : '______________';
setIsTitleVisible(!isTitleVisible); setIsTitleVisible(!isTitleVisible);
}, 500); }, 500);
} else { } else {

@ -1,170 +1,167 @@
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom';
import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { Row, Col, Divider, Table , Card, Button, Input, import { Row, Col, Divider, Table, Card, Button, Input, Flex, Layout, Space, Empty, Radio, Select, DatePicker, Form, List, Avatar } from 'antd';
Space, Empty, Radio, Select, DatePicker, Form, List, Avatar import { StarFilled, ZoomInOutlined, StarOutlined, SearchOutlined } from '@ant-design/icons';
} from 'antd' import { ChatList, ChatItem, MessageBox } from 'react-chat-elements';
import {
StarFilled, ZoomInOutlined, StarOutlined, SearchOutlined
} from '@ant-design/icons'
const { Search } = Input const { Sider, Content, Header, Footer } = Layout;
const { RangePicker } = DatePicker const { Search } = Input;
const { RangePicker } = DatePicker;
const data = [ const data = [
{ {
title: 'Ann', title: 'Ann',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0',
msgTime: '03-04 13:45:29', msgTime: '03-04 13:45:29',
content: 'Hi, this is Ann from China Highlights travel and it is my pleasure to help your Beijing trip. I have sent you an email with the general idea about you trip. Please check it and if any question or new idea just let me know. We are specailized in customizing tour and I would like to work with you to create a tour itinerary that you like. Look forward to your reply.' content:
'Hi, this is Ann from China Highlights travel and it is my pleasure to help your Beijing trip. I have sent you an email with the general idea about you trip. Please check it and if any question or new idea just let me know. We are specailized in customizing tour and I would like to work with you to create a tour itinerary that you like. Look forward to your reply.',
}, },
{ {
title: 'David Azhari', title: 'David Azhari',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2',
msgTime: '03-04 16:33:08', msgTime: '03-04 16:33:08',
content: 'Hi! I just replied to your email saying that I have a few questions and notes' content: 'Hi! I just replied to your email saying that I have a few questions and notes',
}, },
{ {
title: 'David Azhari', title: 'David Azhari',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2',
msgTime: '03-04 16:33:34', msgTime: '03-04 16:33:34',
content: 'So first, is it possible for you guys to write a PU Invitation letter for visas or no?' content: 'So first, is it possible for you guys to write a PU Invitation letter for visas or no?',
}, },
{ {
title: 'Ann', title: 'Ann',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0',
msgTime: '03-04 16:33:41', msgTime: '03-04 16:33:41',
content: 'you prefer we discuss here or go on by mail ?' content: 'you prefer we discuss here or go on by mail ?',
}, },
{ {
title: 'David Azhari', title: 'David Azhari',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2',
msgTime: '03-04 16:34:03', msgTime: '03-04 16:34:03',
content: 'I prefer by mail if its ok with you' content: 'I prefer by mail if its ok with you',
}, },
{ {
title: 'Ann', title: 'Ann',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0',
msgTime: '03-04 16:34:28', msgTime: '03-04 16:34:28',
content: 'both is okay ,I am typing mail to you now lol and receive your message here.' content: 'both is okay ,I am typing mail to you now lol and receive your message here.',
}, },
{ {
title: 'David Azhari', title: 'David Azhari',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2',
msgTime: '03-05 02:56:37', msgTime: '03-05 02:56:37',
content: 'Ok thank you so much' content: 'Ok thank you so much',
}, },
{ {
title: 'Ann', title: 'Ann',
avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0',
msgTime: '03-04 16:44:03', msgTime: '03-04 16:44:03',
content: 'you are welcome I have sent the mail to you ,please check.it is my pleasure to assist you with your tour.' content: 'you are welcome I have sent the mail to you ,please check.it is my pleasure to assist you with your tour.',
}, },
] ];
// eslint-disable-next-line react/display-name
const SearchForm = memo(function ({ onSubmit }) { const SearchForm = memo(function ({ onSubmit }) {
const [form] = Form.useForm() const [form] = Form.useForm();
function handleSubmit(values) { function handleSubmit(values) {
onSubmit?.(values) onSubmit?.(values);
} }
return ( return (
<Form <Form
layout={'inline'} layout={'inline'}
form={form} form={form}
initialValues={{ }} initialValues={{}}
onFinish={handleSubmit} onFinish={handleSubmit}
style={{ style={{
maxWidth: 'none', maxWidth: 'none',
}} }}>
> <Form.Item label='顾问' name='travel' style={{ width: '200px' }}>
<Form.Item label='顾问' name='travel' style={{width: '200px'}}> <Select
<Select showSearch
showSearch placeholder='请选择'
placeholder='请选择' optionFilterProp='children'
optionFilterProp='children' filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
filterOption={(input, option) => options={[
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())} {
options={[ value: '杨新玲',
{ label: '杨新玲',
value: '杨新玲', },
label: '杨新玲', {
}, value: '黄雪荣',
{ label: '黄雪荣',
value: '黄雪荣', },
label: '黄雪荣', {
}, value: '骆梅玉',
{ label: '骆梅玉',
value: '骆梅玉', },
label: '骆梅玉', {
}, value: '秦宇尘',
{ label: '秦宇尘',
value: '秦宇尘', },
label: '秦宇尘', {
}, value: '莫梦瑶',
{ label: '莫梦瑶',
value: '莫梦瑶', },
label: '莫梦瑶', {
}, value: '秦雯萱',
{ label: '秦雯萱',
value: '秦雯萱', },
label: '秦雯萱', {
}, value: '王露加',
{ label: '王露加',
value: '王露加', },
label: '王露加', ]}
}, />
]}
/>
</Form.Item> </Form.Item>
<Form.Item label='客人' name='orderLabel' style={{width: '200px'}}> <Form.Item label='客人' name='orderLabel' style={{ width: '200px' }}>
<Select <Select
showSearch showSearch
placeholder='请选择' placeholder='请选择'
optionFilterProp='children' optionFilterProp='children'
filterOption={(input, option) => filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())} options={[
options={[ {
{ value: 'Denise',
value: 'Denise', label: 'Denise',
label: 'Denise', },
}, {
{ value: 'Kennedy',
value: 'Kennedy', label: 'Kennedy',
label: 'Kennedy', },
}, {
{ value: 'Harsh',
value: 'Harsh', label: 'Harsh',
label: 'Harsh', },
}, {
{ value: 'SMLiew',
value: 'SMLiew', label: 'SMLiew',
label: 'SMLiew', },
}, {
{ value: 'See Kok Ching',
value: 'See Kok Ching', label: 'See Kok Ching',
label: 'See Kok Ching', },
}, {
{ value: 'Jonathan Michael Lilley',
value: 'Jonathan Michael Lilley', label: 'Jonathan Michael Lilley',
label: 'Jonathan Michael Lilley', },
}, {
{ value: 'Loan',
value: 'Loan', label: 'Loan',
label: 'Loan', },
}, {
{ value: 'Leah Belle',
value: 'Leah Belle', label: 'Leah Belle',
label: 'Leah Belle', },
}, {
{ value: 'Christelle Narvasa',
value: 'Christelle Narvasa', label: 'Christelle Narvasa',
label: 'Christelle Narvasa', },
}, {
{ value: 'Mai Schaefer',
value: 'Mai Schaefer', label: 'Mai Schaefer',
label: 'Mai Schaefer', },
}, ]}
]} />
/>
</Form.Item> </Form.Item>
<Form.Item label='关键词' name='orderNumber'> <Form.Item label='关键词' name='orderNumber'>
<Input placeholder='关键词' allowClear /> <Input placeholder='关键词' allowClear />
@ -172,45 +169,62 @@ const SearchForm = memo(function ({ onSubmit }) {
<Form.Item label='日期' name='startDate'> <Form.Item label='日期' name='startDate'>
<RangePicker /> <RangePicker />
</Form.Item> </Form.Item>
<Form.Item > <Form.Item>
<Button type='primary' htmlType='submit'>搜索</Button> <Button type='primary' htmlType='submit'>
搜索
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
) );
}) });
function ChatHistory() { function ChatHistory() {
const [formValues, setFormValues] = useState({});
const [formValues, setFormValues] = useState({})
const handleSubmit = useCallback((values) => { const handleSubmit = useCallback((values) => {
setFormValues({...values}) setFormValues({ ...values });
}, []) }, []);
return ( return (
<> <>
<SearchForm onSubmit={handleSubmit} /> <SearchForm onSubmit={handleSubmit} />
<Divider plain orientation='left'></Divider> <Divider plain orientation='left'></Divider>
<div style={{maxWidth: '800px'}}> <Layout hasSider className='h-screen chathistory-wrapper' style={{ maxHeight: 'calc(100% - 279px)', height: 'calc(100% - 279px)' }}>
<List <Sider width={240} theme={'light'} className='h-full overflow-y-auto' style={{ maxHeight: 'calc(100vh - 279px)', height: 'calc(100vh - 279px)' }}>
itemLayout='vertical' <ChatList
dataSource={data} className={'__active text-primary '}
renderItem={(item, index) => ( dataSource={[
<List.Item {
> alt: 'Jane',
<List.Item.Meta title: 'Jane',
avatar={<Avatar src={item.avatarUrl} />} date: new Date(),
title={<a href='https://ant.design'>{item.title}</a>} letterItem: { id: 'x1', letter: 'Jane' },
description={item.msgTime} },
]}
/>
</Sider>
<Content style={{ maxHeight: 'calc(100vh - 279px)', height: 'calc(100vh - 279px)', minWidth: '360px' }}>
<div className='h-full relative'>
<List
loading={false}
loadMore={() => {
console.log('load more');
}}
className='h-full overflow-y-auto px-2'
itemLayout='vertical'
dataSource={data}
renderItem={(item, index) => (
<List.Item>
<List.Item.Meta avatar={<Avatar src={item.avatarUrl} />} title={item.title} description={item.msgTime} />
<div>{item.content}</div>
</List.Item>
)}
/> />
<div>{item.content}</div> </div>
</List.Item> </Content>
</Layout>
)}
/>
</div>
</> </>
) );
} }
export default ChatHistory export default ChatHistory;

@ -115,11 +115,10 @@ const Conversations = () => {
{...item} {...item}
key={item.sn} key={item.sn}
id={item.sn} id={item.sn}
// avatar={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.whatsapp_name.trim() || item.whatsapp_phone_number}`}
letterItem={{id: item.whatsapp_name.trim() || item.whatsapp_phone_number, letter: (item.whatsapp_name.trim() || item.whatsapp_phone_number).slice(0, 5)}} letterItem={{id: item.whatsapp_name.trim() || item.whatsapp_phone_number, letter: (item.whatsapp_name.trim() || item.whatsapp_phone_number).slice(0, 5)}}
alt={`${item.whatsapp_name.trim()}`} alt={`${item.whatsapp_name.trim()}`}
title={item.whatsapp_name.trim() || item.whatsapp_phone_number} title={item.whatsapp_name.trim() || item.whatsapp_phone_number}
subtitle={item.whatsapp_phone_number} subtitle={item.coli_id}
date={item.last_received_time} date={item.last_received_time}
unread={item.unread_msg_count} unread={item.unread_msg_count}
className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-4 border-solid border-whatsapp-bg bg-whatsapp-bg' : ''} className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-4 border-solid border-whatsapp-bg bg-whatsapp-bg' : ''}

@ -64,6 +64,7 @@ const ImageUpload = ({ disabled, invokeUploadFileMessage, invokeSendUploadMessag
showUploadList: false, showUploadList: false,
beforeUpload, beforeUpload,
maxCount: 1, maxCount: 1,
accept: 'audio/aac, audio/mp4, audio/mpeg, audio/amr, audio/ogg,image/jpeg, image/png,video/mp4, video/3gp,image/webp,text/plain, application/pdf, application/vnd.ms-powerpoint, application/msword, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}; };
return ( return (
<Upload <Upload

@ -1,60 +1,61 @@
import { useEffect, useState, useRef, memo, createRef, forwardRef } from 'react'; import { useEffect, useRef, useState, forwardRef, memo } from 'react';
import { Image, Button } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import { MessageBox } from 'react-chat-elements'; import { MessageBox } from 'react-chat-elements';
import useConversationStore from '@/stores/ConversationStore'; import { Button } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import useConversationStore from '@/stores/ConversationStore';
import { isEmpty, olog } from '@/utils/utils'; import { isEmpty, olog } from '@/utils/utils';
const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...props }) => { const MessagesList = ({ messages, handlePreview, reference }) => {
const setReferenceMsg = useConversationStore(useShallow((state) => state.setReferenceMsg)); const setReferenceMsg = useConversationStore(useShallow((state) => state.setReferenceMsg));
olog('render message list');
const scrollToMessage = (id, index) => {
const _i = index || props.dataSource.findIndex((msg) => msg.id === id);
if (_i >= 0) {
messageRefs.current[_i].current.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
};
// const messagesEndRef = useRef(null);
const messageRefs = useRef([]); const messageRefs = useRef([]);
messageRefs.current = props.dataSource.map((_, i) => messageRefs.current[i] ?? createRef()); const [page, setPage] = useState(1);
let timeout = null;
const toBottom = (e) => { const fetchNextPage = async () => {
if (!reference) return; olog('fetchNextPage')
reference.current.scrollTop = reference.current.scrollHeight - reference.current.offsetHeight; setPage(page + 1);
// Fetch next page of messages here
}; };
const prevProps = useRef(props); const handleScroll = (e) => {
useEffect(() => { const { scrollTop } = e.target;
if (prevProps.current.dataSource.length !== props.dataSource.length) { const delay = 1000; // 1 second
toBottom();
if (scrollTop === 0) {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(fetchNextPage, delay);
} }
};
prevProps.current = props; const scrollToBottom = () => {
}, [prevProps, props]); if (reference.current) {
reference.current.scrollTop = reference.current.scrollHeight;
}
};
const [previewVisible, setPreviewVisible] = useState(false); const scrollToMessage = (id, index) => {
const [previewSrc, setPreviewSrc] = useState(); const _i = index || messages.findIndex((msg) => msg.id === id);
const onPreviewClose = () => { if (reference.current && messageRefs.current[_i]) {
setPreviewSrc(''); reference.current.scrollTop = messageRefs.current[_i].offsetTop;
setPreviewVisible(false); }
}; };
const handlePreview = (msg) => {
switch (msg.type) {
case 'photo':
setPreviewVisible(true);
setPreviewSrc(msg.data.uri);
return false;
case 'file': useEffect(scrollToBottom, [messages]);
window.open(msg.data.link || msg.data.uri, '_blank', 'noopener,noreferrer');
return false;
default: useEffect(() => {
return false; const messageList = reference.current;
if (messageList) {
messageList.addEventListener('scroll', handleScroll);
} }
}; return () => {
if (messageList) {
messageList.removeEventListener('scroll', handleScroll);
}
};
}, []);
const RenderText = memo(function renderText({ str }) { const RenderText = memo(function renderText({ str }) {
const parts = str.split(/(https?:\/\/[^\s]+|\p{Emoji_Presentation})/gmu).filter((s) => s !== ''); const parts = str.split(/(https?:\/\/[^\s]+|\p{Emoji_Presentation})/gmu).filter((s) => s !== '');
@ -88,6 +89,7 @@ const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...p
</span> </span>
); );
}); });
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
const MessageBoxWithRef = forwardRef((props, ref) => ( const MessageBoxWithRef = forwardRef((props, ref) => (
<div ref={ref}> <div ref={ref}>
@ -96,27 +98,11 @@ const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...p
)); ));
return ( return (
<div className='relative h-full overflow-y-auto overflow-x-hidden flex'> <div className='relative h-full overflow-y-auto overflow-x-hidden flex flex-1'>
<div className='relative overflow-y-auto block flex-1' ref={reference}> <div ref={reference} className='relative overflow-y-auto overflow-x-hidden block flex-1'>
{props.dataSource.map((message, index) => ( {messages.map((message, index) => (
// <Dropdown
// key={message.id}
// menu={{
// items: [{ label: '', key: 'reply', disabled: !['text'].includes(message.whatsapp_msg_type) }],
// onClick: ({ key, domEvent }) => {
// domEvent.stopPropagation();
// switch (key) {
// case 'reply':
// return setReferenceMsg(message);
// default:
// return;
// }
// },
// }}
// trigger={['contextMenu']}>
<MessageBoxWithRef <MessageBoxWithRef
ref={messageRefs.current[index]} ref={(el) => (messageRefs.current[index] = el)}
key={message.id} key={message.id}
{...message} {...message}
position={message.sender === 'me' ? 'right' : 'left'} position={message.sender === 'me' ? 'right' : 'left'}
@ -149,11 +135,9 @@ const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...p
} }
: {})} : {})}
/> />
// </Dropdown>
))} ))}
</div> </div>
<Button onClick={toBottom} ghost type={'dashed'} shape={'circle'} className=' absolute bottom-1 right-4' icon={<DownOutlined />} /> <Button onClick={scrollToBottom} ghost type={'dashed'} shape={'circle'} className=' absolute bottom-1 right-4' icon={<DownOutlined />} />
<Image src={null} preview={{ visible: previewVisible, src: previewSrc, onClose: onPreviewClose }} />
</div> </div>
); );
}; };

@ -1,15 +1,39 @@
import { useRef } from 'react'; import { useRef, useState } from 'react';
import useConversationStore from '@/stores/ConversationStore'; import useConversationStore from '@/stores/ConversationStore';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { Image, } from 'antd';
import MessagesList from './MessagesList'; import MessagesList from './MessagesList';
const MessagesWrapper = () => { const MessagesWrapper = () => {
const activeMessages = useConversationStore(useShallow((state) => (state.currentConversation.sn && state.activeConversations[state.currentConversation.sn] ? state.activeConversations[state.currentConversation.sn]: []))); const activeMessages = useConversationStore(useShallow((state) => (state.currentConversation.sn && state.activeConversations[state.currentConversation.sn] ? state.activeConversations[state.currentConversation.sn]: [])));
const reference = useRef(null); const reference = useRef(null);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewSrc, setPreviewSrc] = useState();
const onPreviewClose = () => {
setPreviewSrc('');
setPreviewVisible(false);
};
const handlePreview = (msg) => {
switch (msg.whatsapp_msg_type) {
case 'image':
setPreviewVisible(true);
setPreviewSrc(msg.data.uri);
return false;
case 'document':
window.open(msg.data.link || msg.data.uri, '_blank', 'noopener,noreferrer');
return false;
default:
return false;
}
};
return ( return (
<> <>
<MessagesList dataSource={activeMessages} reference={reference} /> <MessagesList messages={activeMessages} {...{ reference, handlePreview }} />
<Image width={0} height={0} src={null} preview={{ visible: previewVisible, src: previewSrc, onClose: onPreviewClose }} />
</> </>
); );
}; };

@ -67,7 +67,7 @@
.chatwindow-wrapper .rce-mbox-reply-message, .chatwindow-wrapper .rce-mbox-reply-message,
.chatwindow-wrapper .emoji .chatwindow-wrapper .emoji
{ {
font-family: "Noto Color Emoji", 'Apple Color Emoji', 'Twemoji Mozilla', 'Segoe UI Emoji', 'Segoe UI Symbol', 'EmojiOne Color', 'Android Emoji', sans-serif; font-family: "Noto Color Emoji", 'Apple Color Emoji', 'Twemoji Mozilla', 'Segoe UI Emoji', 'Segoe UI Symbol', 'EmojiOne Color', 'Android Emoji', Arial, sans-serif;
font-weight: 500; font-weight: 500;
} }
.chatwindow-wrapper .rce-mbox-text a{ .chatwindow-wrapper .rce-mbox-text a{

Loading…
Cancel
Save