重连; 获取用户

dev/mobile
Lei OT 2 years ago
parent 1d0a09cd4b
commit e3aa4c25dd

@ -67,7 +67,7 @@ export const sentMsgTypeMapped = {
to: msg.to,
msgtype: 'image',
msgcontent: {
image: { link: msg.data.uri },
image: { link: msg.data.uri, caption: msg.text },
...(msg.context ? { context: msg.context, message_origin: msg.message_origin } : {}),
},
}),
@ -96,7 +96,7 @@ export const sentMsgTypeMapped = {
conversationid: msg.id.split('.')[0],
type: 'text',
title: msg.template_origin.components.header?.[0]?.text || '',
text: templateDataMapped?.body?.text || '', // msg.template_origin.components.body?.[0]?.text || '',
text: autoLinkText( templateDataMapped?.body?.text || ''), // msg.template_origin.components.body?.[0]?.text || '',
};
},
},
@ -247,7 +247,7 @@ export const whatsappMsgTypeMapped = {
type: 'text',
data: (msg) => {
const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({...r, [v.type]: v}), {}) : null;
return { id: msg.wamid, text: templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || '', title: msg.template.name }
return { id: msg.wamid, text: autoLinkText(templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || ''), title: msg.template.name }
},
renderForReply: (msg) => {
const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({ ...r, [v.type]: v }), {}) : null;

@ -54,11 +54,11 @@ export class RealTimeAPI {
return this.webSocket.pipe(
// retry(10)
retry({
count: 10,
count: 20,
// delay: 3000,
delay: (errors, index) => {
this.onRetry(index);
return timer(3000);
return timer(10000);
},
resetOnSuccess: true,
}),

@ -69,7 +69,7 @@ const websocketSlice = (set, get) => ({
},
disconnectWebsocket: () => {
const { websocket } = get();
websocket.disconnect();
if (websocket) websocket.disconnect();
return set({ websocket: null });
},
reconnectWebsocket: (userId) => {
@ -125,6 +125,11 @@ const referenceMsgSlice = (set) => ({
setReferenceMsg: (referenceMsg) => set({ referenceMsg }),
});
const complexMsgSlice = (set) => ({
complexMsg: {},
setComplexMsg: (complexMsg) => set({ complexMsg }),
});
const conversationSlice = (set, get) => ({
conversationsList: [],
currentConversation: {},
@ -257,6 +262,7 @@ export const useConversationStore = create(devtools((set, get) => ({
...templatesSlice(set, get),
...messageSlice(set, get),
...referenceMsgSlice(set, get),
...complexMsgSlice(set, get),
// state actions
addError: (error) => set((state) => ({ errors: [...state.errors, error] })),

@ -43,7 +43,7 @@ console.info(loginUser)
useConversationStore.getState().fetchInitialData(userId);
}
return () => {
//useConversationStore.getState().disconnectWebsocket();
useConversationStore.getState().disconnectWebsocket();
}
}, [])

@ -112,12 +112,13 @@ const Conversations = () => {
{...item}
key={item.sn}
id={item.sn}
avatar={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.whatsapp_name.trim() || item.whatsapp_phone_number}`}
// 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}}
alt={`${item.whatsapp_name.trim()}`}
title={item.whatsapp_name.trim() || item.whatsapp_phone_number}
date={item.last_received_time}
unread={item.unread_msg_count}
className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary underline bg-whatsapp-me border-y-0 border-e-0 border-s-2 border-solid border-whatsapp-me ' : ''}
className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-2 border-solid border-whatsapp-me ' : ''}
onClick={() => onSwitchConversation(item)}
/>
</Dropdown>

@ -14,6 +14,7 @@ import {
CloseCircleOutlined,
} from '@ant-design/icons';
import useConversationStore from '@/stores/ConversationStore';
import { v4 as uuid } from 'uuid';
const props = {
name: 'file',
@ -25,15 +26,17 @@ const props = {
};
const ImageUpload = ({ disabled, invokeSendMessage }) => {
const { currentConversation, referenceMsg, setReferenceMsg } = useConversationStore();
const { currentConversation, setComplexMsg } = useConversationStore();
const [uploading, setUploading] = useState(false);
const handleSendImage = (src) => {
const msgObj = {
type: 'photo',
data: { uri: src, },
id: uuid(),
};
invokeSendMessage(msgObj);
setComplexMsg(msgObj);
// invokeSendMessage(msgObj);
};
return (
<Upload
@ -47,7 +50,7 @@ const ImageUpload = ({ disabled, invokeSendMessage }) => {
// message.success(`${info.file.name} file uploaded successfully`);
// test: src
// handleSendImage('blob:https://web.whatsapp.com/bbe878fc-7bde-447f-aa28-a4b929621a50');
// handleSendImage('https://images.chinahighlights.com//allpicture/2020/04/9330cd3c78a34c81afd3b1fb.jpg');
handleSendImage('https://images.chinahighlights.com//allpicture/2020/04/9330cd3c78a34c81afd3b1fb.jpg');
} else if (info.file.status === 'error') {
message.error(`图片添加失败`);
}

@ -24,8 +24,7 @@ const splitTemplate = (template) => {
const InputTemplate = ({ disabled = false, invokeSendMessage }) => {
const searchInputRef = useRef(null);
const { notification } = App.useApp();
const { loadUser } = useAuthStore();
const loginUser = loadUser();
const { loginUser } = useAuthStore()
const { currentConversation, templates } = useConversationStore();
// : customer, agent
const valueMapped = { ...cloneDeep(currentConversation), ...objectMapper(loginUser, { username: [{ key: 'agent_name' }, { key: 'your_name' }] }) };
@ -58,7 +57,7 @@ const InputTemplate = ({ disabled = false, invokeSendMessage }) => {
name: fromTemplate.name,
language: { code: fromTemplate.language },
components: fromTemplate.components_origin.map((citem) => {
const keys = (citem?.text || '').match(/{{(.*?)}}/g).map((key) => key.replace(/{{|}}/g, ''));
const keys = ((citem?.text || '').match(/{{(.*?)}}/g) || []).map((key) => key.replace(/{{|}}/g, ''));
const params = keys.map((v) => ({ type: 'text', text: getNestedValue(mergeInput, [v]) }));
const paramText = params.map((p) => p.text);
const fillTemplate = paramText.length ? replaceTemplateString(citem?.text || '', paramText) : citem?.text || '';

@ -1,9 +1,20 @@
import React, { useState } from 'react';
import { Input, Flex, Button, } from 'antd';
import { Input, Flex, Button, Image } from 'antd';
// import { Input } from 'react-chat-elements';
import useAuthStore from '@/stores/AuthStore'
import useAuthStore from '@/stores/AuthStore';
import useConversationStore from '@/stores/ConversationStore';
import { SendOutlined, MessageOutlined, SmileOutlined, PictureOutlined, CommentOutlined, UploadOutlined, CloudUploadOutlined, FolderAddOutlined, FilePdfOutlined, CloseCircleOutlined } from '@ant-design/icons';
import {
SendOutlined,
MessageOutlined,
SmileOutlined,
PictureOutlined,
CommentOutlined,
UploadOutlined,
CloudUploadOutlined,
FolderAddOutlined,
FilePdfOutlined,
CloseCircleOutlined,
} from '@ant-design/icons';
import { isEmpty } from '@/utils/utils';
import { v4 as uuid } from 'uuid';
import { sentMsgTypeMapped } from '@/lib/msgUtils';
@ -12,17 +23,15 @@ import InputImageUpload from './Input/ImageUpload';
import dayjs from 'dayjs';
const InputBox = () => {
const { loadUser } = useAuthStore();
const loginUser = loadUser();
const { userId } = loginUser;
const { websocket, websocketOpened, currentConversation, referenceMsg, setReferenceMsg, sentOrReceivedNewMessage } = useConversationStore();
const { loginUser: { userId } } = useAuthStore()
const { websocket, websocketOpened, currentConversation, referenceMsg, setReferenceMsg, complexMsg, setComplexMsg, sentOrReceivedNewMessage } = useConversationStore();
const [textContent, setTextContent] = useState('');
const talkabled = !isEmpty(currentConversation.sn) && websocketOpened;
const gt24h = currentConversation.last_received_time ? dayjs().diff(dayjs(currentConversation.last_received_time), 'hour') > 24 : true;
const textabled = (talkabled && !gt24h);
const textabled = talkabled && !gt24h;
const invokeSendMessage = (msgObj) => {
const msgObjMerge = {
@ -44,13 +53,15 @@ const InputBox = () => {
setTextContent('');
setReferenceMsg({});
setComplexMsg({});
};
const handleSendText = () => {
if (textContent.trim() !== '') {
if (textContent.trim() !== '' || !isEmpty(complexMsg) ) {
const msgObj = {
type: 'text',
text: textContent,
...complexMsg,
};
invokeSendMessage(msgObj);
}
@ -61,8 +72,19 @@ const InputBox = () => {
{referenceMsg.id && (
<Flex justify='space-between' className='reply-to bg-gray-100 p-1 rounded-none text-slate-500'>
<div className=' border-l-3 border-y-0 border-r-0 border-slate-300 border-solid pl-2 pr-1 py-1'>
<span className=' text-primary pr-1 italic'>{referenceMsg.senderName}</span>{referenceMsg.originText}</div>
<Button type='text' title='取消引用' className=' rounded-none text-slate-500' icon={<CloseCircleOutlined />} size={'middle'} onClick={() => (setReferenceMsg({}))} />
<span className=' text-primary pr-1 italic'>{referenceMsg.senderName}</span>
{referenceMsg.originText}
</div>
<Button type='text' title='取消引用' className=' rounded-none text-slate-500' icon={<CloseCircleOutlined />} size={'middle'} onClick={() => setReferenceMsg({})} />
</Flex>
)}
{complexMsg.id && (
<Flex justify='space-between' className='reply-to bg-gray-100 p-1 rounded-none text-slate-500'>
<div className='pl-2 pr-1 py-1'>
<Image width={100} src={complexMsg.data.uri} />
<span className=' pl-1 '></span>
</div>
<Button type='text' title='删除' className=' rounded-none text-slate-500' icon={<CloseCircleOutlined />} size={'middle'} onClick={() => setComplexMsg({})} />
</Flex>
)}
<Input.TextArea
@ -82,16 +104,14 @@ const InputBox = () => {
autoSize={{ minRows: 2, maxRows: 6 }}
/>
<Flex justify={'space-between'} className=' bg-gray-200 p-1 rounded-b'>
<Flex gap={4} className='divide-y-0 divide-x divide-solid divide-gray-500 *:text-primary *:rounded-none'>
<Flex gap={4} className='*:text-primary *:rounded-none'>
<InputTemplate key='templates' disabled={!talkabled} invokeSendMessage={invokeSendMessage} />
{/* <InputImageUpload key={'addNewPic'} disabled={!textabled} invokeSendMessage={invokeSendMessage} /> */}
{/* <Button type='text' className='' icon={<FolderAddOutlined />} size={'middle'} /> */}
{/* <Button type='text' className='' icon={<CloudUploadOutlined />} size={'middle'} /> */}
{/* <Button type='text' className='' icon={<FilePdfOutlined />} size={'middle'} /> */}
{/* <Button type='text' className='' icon={<FolderAddOutlined />} size={'middle'} />
<Button type='text' className='' icon={<CloudUploadOutlined />} size={'middle'} />
<Button type='text' className='' icon={<FilePdfOutlined />} size={'middle'} /> */}
</Flex>
<Button key={'send-btn'} onClick={handleSendText} type='primary' size='middle' icon={<SendOutlined />}
disabled={!textabled}
>
<Button key={'send-btn'} onClick={handleSendText} type='primary' size='middle' icon={<SendOutlined />} disabled={!textabled}>
Send
</Button>
</Flex>

@ -1,18 +1,29 @@
import { createContext, useContext, useEffect, useState } from 'react';
import useConversationStore from '@/stores/ConversationStore';
import { Flex, Typography, Avatar, Alert } from 'antd';
import useAuthStore from '@/stores/AuthStore'
import { Flex, Typography, Avatar, Alert, Button, Tooltip } from 'antd';
import { ReloadOutlined, ApiOutlined } from '@ant-design/icons';
import { LoadingOutlined } from '@ant-design/icons';
import LocalTimeClock from './LocalTimeClock';
const MessagesHeader = () => {
const {websocketOpened, websocketRetrying, websocketRetrytimes, currentConversation} = useConversationStore();
const { loginUser: { userId } } = useAuthStore()
const {websocketOpened, websocketRetrying, websocketRetrytimes, currentConversation, connectWebsocket} = useConversationStore();
return (
<>
{websocketOpened===false && <Alert type='error' message='断开连接' banner />}
{websocketOpened === false && (
<Alert
type='error'
message='断开连接'
banner
action={
websocketRetrytimes === -1 ? <Tooltip key={'connect'} title='点击重试'>
<Button key={'connect'} type='text' icon={<ApiOutlined />} onClick={() => connectWebsocket(userId)} className=' text-blue-500' />
</Tooltip> : null
}
/>
)}
{websocketRetrying && websocketRetrytimes > 0 && <Alert type={'warning'} message={`正在重连, ${websocketRetrytimes}次...`} banner icon={<LoadingOutlined />} />}
<Flex gap={16} className='p-1 flex-auto'>
{currentConversation.customer_name && <Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${currentConversation.customer_name}`} />}
{/* {currentConversation.customer_name && <Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${currentConversation.customer_name}`} />} */}
<Flex flex={'1'} justify='space-between'>
<Flex vertical={true} justify='space-between'>
<Typography.Text strong>{currentConversation.customer_name}</Typography.Text>

@ -15,6 +15,9 @@
background: linear-gradient(0deg,#00000014,#0000);
color: #00000073;
}
.chatwindow-wrapper .rce-mbox-text a{
color: #4f81a1;
}
.chatwindow-wrapper .rce-mbox-text:after{
content: none;
}
@ -31,3 +34,8 @@
height: 10px;
right: -9px;
}
.chatwindow-wrapper .rce-avatar-letter{
margin-top: 0;
margin-left: 5px;
justify-content: flex-start;
}

Loading…
Cancel
Save