dev/code-splitting
Lei OT 2 years ago
parent 3f56f5034d
commit 63965ffd69

@ -1,7 +1,7 @@
import { groupBy, pick, sortArrayByOrder } from '@/utils/utils'; import { groupBy, pick, sortArrayByOrder } from '@/utils/utils';
import { fetchJSON, postJSON } from '@/utils/request' import { fetchJSON, postJSON } from '@/utils/request'
import { parseRenderMessageList } from '@/lib/msgUtils'; import { parseRenderMessageList } from '@/channel/whatsappUtils';
import { API_HOST } from '@/config'; import { API_HOST } from '@/config';
import { isEmpty } from '@/utils/commons'; import { isEmpty } from '@/utils/commons';
import dayjs from 'dayjs'; import dayjs from 'dayjs';

@ -1,67 +0,0 @@
// websocketLib.js
import { WebSocketSubject } from 'rxjs/webSocket';
import { retryWhen, delay, take, catchError } from 'rxjs/operators';
class WebSocketLib {
constructor(url, authToken, protocol) {
this.url = url;
this.authToken = authToken;
this.protocol = protocol;
}
connect() {
this.socket$ = new WebSocketSubject({
url: this.url,
protocol: this.protocol, // Use protocol for message type
openObserver: {
next: () => {
console.log('Connection established');
// Send authentication message as soon as connection is established
this.socket$.next({ event: 'authenticate', token: this.authToken });
},
},
closeObserver: {
next: () => {
console.log('Connection closed');
},
},
});
this.socket$
.pipe(
retryWhen(errors =>
errors.pipe(
delay(1000), // Retry connection every 1 second
take(10), // Maximum of 10 retries
catchError(error => new Error(`Failed to reconnect: ${error}`)) // Throw error after 10 failed retries
)
)
)
.subscribe(
msg => console.log('Received message:', msg),
err => console.error('Received error:', err),
() => console.log('Connection closed')
);
}
sendMessage(message) {
if (!this.socket$) {
console.error('Must connect to WebSocket server before sending a message');
return;
}
this.socket$.next({ type: 'message', content: message });
}
disconnect() {
if (!this.socket$) {
console.error('Must connect to WebSocket server before disconnecting');
return;
}
this.socket$.complete();
this.socket$ = null;
}
}
export default WebSocketLib;

@ -1,7 +1,7 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { RealTimeAPI } from '@/lib/realTimeAPI'; import { RealTimeAPI } from '@/channel/realTimeAPI';
import { olog, isEmpty } from '@/utils/utils'; import { olog, isEmpty } from '@/utils/utils';
import { receivedMsgTypeMapped, handleNotification } from '@/lib/msgUtils'; import { receivedMsgTypeMapped, handleNotification } from '@/channel/whatsappUtils';
import { fetchConversationsList, fetchTemplates, fetchMessages } from '@/actions/ConversationActions'; import { fetchConversationsList, fetchTemplates, fetchMessages } from '@/actions/ConversationActions';
import { devtools } from 'zustand/middleware'; import { devtools } from 'zustand/middleware';
import { WS_URL } from '@/config'; import { WS_URL } from '@/config';
@ -89,7 +89,7 @@ const websocketSlice = (set, get) => ({
realtimeAPI.onMessage(handleMessage); realtimeAPI.onMessage(handleMessage);
realtimeAPI.onCompletion(() => addError('Connection broken')); realtimeAPI.onCompletion(() => addError('Connection broken'));
olog('Connecting to websocket...', realtimeAPI); olog('Connecting to websocket...');
setWebsocket(realtimeAPI); setWebsocket(realtimeAPI);
}, },
disconnectWebsocket: () => { disconnectWebsocket: () => {

@ -1,334 +0,0 @@
import { createContext, useContext, useState, useEffect, useRef } from 'react';
import { receivedMsgTypeMapped, sentMsgTypeMapped } from '@/lib/msgUtils';
import { groupBy, isEmpty, pick } from '@/utils/utils';
import { v4 as uuid } from 'uuid';
export const ConversationContext = createContext();
export const useConversationContext = () => useContext(ConversationContext);
export async function fetchJSON(url, data) {
let params = '';
let ifp = '';
if (data) {
params = new URLSearchParams(data).toString();
ifp = params ? '?' : ifp;
}
ifp = url.includes('?') ? '' : ifp;
const host = /^https?:\/\//i.test(url) ? '' : ''; // HT_HOST;
const response = await fetch(`${host}${url}${ifp}${params}`);
return await response.json();
}
export async function postJSON(url, obj) {
try {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(obj),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
return await response.json();
} catch (error) {
console.error('fetch error:', error);
throw error;
}
}
// const API_HOST = 'http://202.103.68.144:8888';
const API_HOST = 'https://p9axztuwd7x8a7.mycht.cn/whatsapp_server';
export const useConversations = ({loginUser, realtimeAPI}) => {
const { userId } = loginUser;
const [errors, setErrors] = useState([]);
const [messages, setMessages] = useState([]); // active conversation
const [activeConversations, setActiveConversations] = useState({}); // all active conversation
const [currentID, setCurrentID] = useState();
const [conversationsList, setConversationsList] = useState([]); // open conversations
const [currentConversation, setCurrentConversation] = useState({ sn: '', customer_name: '', coli_sn: '' });
const currentConversationRef = useRef(currentConversation);
useEffect(() => {
currentConversationRef.current = currentConversation;
}, [currentConversation]);
useEffect(() => {
console.log(errors, 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee');
return () => {};
}, [errors])
useEffect(() => {
realtimeAPI.onError(addError.bind(null, 'Error'));
realtimeAPI.onMessage(handleMessage);
realtimeAPI.onCompletion(addError.bind(null, 'Not Connected to Server'));
realtimeAPI.keepAlive(); // Ping Server
// Cleanup function to remove the event listeners when the component is unmounted
return () => {
realtimeAPI.disconnect();
};
}, []);
useEffect(() => {
getConversationsList(); // todo: 和刷新订单会话页面有冲突
getTemplates();
return () => {};
}, []);
// useEffect(() => {
// if (!currentConversation.id && conversationsList.length > 0) {
// switchConversation(conversationsList[0]);
// }
// return () => {};
// }, [conversationsList]);
const poseConversationItemClose = async (item) => {
const res = await postJSON(`${API_HOST}/closeconversation`, { opisn: userId, conversationid: item.sn });
}
const getConversationsList = async () => {
const { result: data } = await fetchJSON(`${API_HOST}/getconversations`, { opisn: userId });
// const _data = [];
const _data = testConversations;
const list = [..._data, ...data.map(ele => ({...ele, customer_name: ele.whatsapp_name.trim()}))];
const dataMapped = list.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});
setConversationsList(list);
setActiveConversations({...dataMapped, ...activeConversations});
console.log(list, dataMapped);
if (list && list.length) {
switchConversation(list[0]);
}
};
const [templatesList, setTemplatesList] = useState([]);
const getTemplates = async () => {
const data = await fetchJSON(`${API_HOST}/listtemplates`);
const canUseTemplates = (data?.result?.items || []).filter((_t) => _t.status !== 'REJECTED').map((ele) => ({ ...ele, components: groupBy(ele.components, (_c) => _c.type.toLowerCase()) }));
setTemplatesList(canUseTemplates);
};
const [customerOrderProfile, setCustomerProfile] = useState({});
const getCustomerProfile = async (colisn) => {
const { result } = await fetchJSON(`${API_HOST}/getorderinfo`, { colisn });
const data = result?.[0] || {};
setCustomerProfile(data);
if (!isEmpty(data.conversation)) {
setConversationsList((pre) => [...data.conversations, ...pre]);
setCurrentConversation(data.conversation[0]);
const thisCMapped = data.conversation.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});
setActiveConversations((pre) => ({ ...pre, ...thisCMapped }));
setMessages([]); // todo: 获取当前会话的历史消息
} else {
// reset chat window
setMessages([]);
if (isEmpty(data.contact?.[0]?.whatsapp_phone_number)) {
setCurrentConversation({ sn: '', customer_name: '', coli_sn: '' });
return false;
}
// 加入新会话
const newChat = {
'sn': uuid(),
// 'opi_sn': 354,
'coli_sn': colisn,
'whatsapp_phone_number': data.contact[0].whatsapp_phone_number,
"last_received_time": '',
"last_send_time": '',
'unread_msg_count': 0,
'whatsapp_name': data.contact[0].name,
'customer_name': data.contact[0].name,
};
setConversationsList((pre) => [...pre, newChat]);
setActiveConversations((pre) => ({ ...pre, [`${newChat.sn}`]: [] }));
setCurrentConversation(pick(newChat, ['sn', 'coli_sn', 'whatsapp_phone_number', 'whatsapp_name', 'customer_name']));
}
};
const switchConversation = (cc) => {
setCurrentID(`${cc.sn}`);
setCurrentConversation({...cc, id: cc.sn, customer_name: cc.whatsapp_name.trim()});
if (isEmpty(cc.coli_sn) || cc.coli_sn === '0') {
setCustomerProfile({});
}
};
// Get customer profile when switching conversation
useEffect(() => {
console.log('currentConversation', currentConversation);
setCurrentID(currentConversation.sn);
setMessages([...(activeConversations[currentConversation.sn] || [])]);
return () => {};
}, [currentConversation]);
/**
* *****************************************************************************************************
* websocket --------------------------------------------------------------------------------------------
* *****************************************************************************************************
*/
const addError = (reason) => {
setErrors((prevErrors) => [...prevErrors, { reason }]);
};
const addMessageToConversations = (targetId, message) => {
setActiveConversations((prevList) => ({
...prevList,
[targetId]: [...(prevList[targetId] || []), message],
}));
// console.log('activeConversations', activeConversations);
if (targetId !== currentID) {
setConversationsList((prevList) => {
return prevList.map((ele) => {
if (ele.sn === targetId) {
return { ...ele, new_msgs: ele.new_msgs + 1, last_received_time: message.date };
}
return ele;
});
});
}
};
useEffect(() => {
console.log(messages, 'messages');
return () => {
}
}, [messages])
const addMessage = (message) => {
addMessageToConversations(message.conversationid, message);
if (message.conversationid === currentID) {
setMessages((prevMessages) => [...prevMessages, message]);
}
};
const updateMessage = (message) => {
let targetMsgs;
setMessages((prevMessages) => {
targetMsgs = prevMessages.map(ele => {
if (ele.id === ele.actionId && ele.actionId === message.actionId) {
return {...ele, id: message.id, status: message.status, dateString: message.dateString};
}
else if (ele.id === message.id) {
return {...ele, id: message.id, status: message.status, dateString: message.dateString};
}
return ele;
});
return targetMsgs;
});
// 更新会话中的消息
const targetId = message.conversationid; // currentConversationRef.current.sn;
setActiveConversations((prevList) => ({
...prevList,
[targetId]: targetMsgs,
}));
// 更新列表的时间
setConversationsList((prevList) => {
return prevList.map((ele) => {
if (ele.sn === targetId) {
return { ...ele, new_msgs: ele.new_msgs + 1, last_received_time: message.date };
}
return ele;
});
});
};
const handleMessage = (data) => {
console.log('handleMessage------------------', );
/**
* ! event handlers in JavaScript capture the environment (including variables) at the time they are defined, not when they are executed.
*/
const { errcode, errmsg, result } = data;
if (!result) {
return false;
}
let resultType = result?.action || result.type;
if (errcode !== 0) {
// addError('Error Connecting to Server');
resultType = 'error';
}
console.log(resultType, 'result.type');
const msgObj = receivedMsgTypeMapped[resultType].getMsg(result);
const msgRender = receivedMsgTypeMapped[resultType].contentToRender(msgObj);
const msgUpdate = receivedMsgTypeMapped[resultType].contentToUpdate(msgObj);
console.log('msgRender msgUpdate', msgRender, msgUpdate);
if ( ['whatsapp.message.updated', 'message', 'error'].includes(resultType)) {
updateMessage(msgUpdate);
// return false;
}
if ( ! isEmpty(msgRender)) {
addMessage(msgRender);
}
console.log('handleMessage*******************', );
};
const sendMessage = (msgObj) => {
const contentToSend = sentMsgTypeMapped[msgObj.type].contentToSend(msgObj);
realtimeAPI.sendMessage({ ...contentToSend, opi_sn: userId });
const contentToRender = sentMsgTypeMapped[msgObj.type].contentToRender(msgObj);
console.log(contentToRender, 'contentToRender sendMessage------------------');
addMessage(contentToRender);
};
return {
errors,
messages,
conversationsList,
currentConversation,
sendMessage,
getConversationsList,
switchConversation,
templates: templatesList, // setTemplates, getTemplates,
customerOrderProfile, getCustomerProfile,
poseConversationItemClose
};
};
// test:
const testConversations = [
{
'sn': 3001,
'opi_sn': 354,
'coli_sn': 0,
'whatsapp_phone_number': '+8618777396951',
"last_received_time": new Date().toDateString(),
"last_send_time": new Date().toDateString(),
'unread_msg_count': Math.floor(Math.random() * (100 - 2 + 1) + 2),
'whatsapp_name': 'LiaoYijun',
'customer_name': 'LiaoYijun',
},
{
'sn': 3002,
'opi_sn': 354,
'coli_sn': 0,
'whatsapp_phone_number': '+8613317835586',
"last_received_time": new Date().toDateString(),
"last_send_time": new Date().toDateString(),
'unread_msg_count': Math.floor(Math.random() * (100 - 2 + 1) + 2),
'whatsapp_name': 'QinQianSheng',
'customer_name': 'QinQianSheng',
},
// {
// 'sn': 3003,
// 'opi_sn': 354,
// 'coli_sn': 240129003,
// 'whatsapp_phone_number': '+8618777396951',
// "last_received_time": new Date().toDateString(),
// "last_send_time": new Date().toDateString(),
// 'unread_msg_count': Math.floor(Math.random() * (100 - 2 + 1) + 2),
// 'whatsapp_name': 'LeiYuanTing',
// },
];

@ -3,7 +3,7 @@ import { useState } from 'react';
import { FileAddOutlined } from '@ant-design/icons'; import { FileAddOutlined } from '@ant-design/icons';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { API_HOST } from '@/config'; import { API_HOST } from '@/config';
import { whatsappSupportFileTypes } from '@/lib/msgUtils'; import { whatsappSupportFileTypes } from '@/channel/whatsappUtils';
import { isEmpty, } from '@/utils/utils'; import { isEmpty, } from '@/utils/utils';
// import useConversationStore from '@/stores/ConversationStore'; // import useConversationStore from '@/stores/ConversationStore';

@ -4,7 +4,7 @@ import { MessageOutlined, SendOutlined } from '@ant-design/icons';
import useAuthStore from '@/stores/AuthStore'; import useAuthStore from '@/stores/AuthStore';
import useConversationStore from '@/stores/ConversationStore'; import useConversationStore from '@/stores/ConversationStore';
import { cloneDeep, getNestedValue, objectMapper, sortArrayByOrder } from '@/utils/utils'; import { cloneDeep, getNestedValue, objectMapper, sortArrayByOrder } from '@/utils/utils';
import { replaceTemplateString } from '@/lib/msgUtils'; import { replaceTemplateString } from '@/channel/whatsappUtils';
import { isEmpty } from '@/utils/commons'; import { isEmpty } from '@/utils/commons';
const splitTemplate = (template) => { const splitTemplate = (template) => {

@ -19,7 +19,7 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { isEmpty, } from '@/utils/utils'; import { isEmpty, } from '@/utils/utils';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { sentMsgTypeMapped, whatsappSupportFileTypes } from '@/lib/msgUtils'; import { sentMsgTypeMapped, whatsappSupportFileTypes } from '@/channel/whatsappUtils';
import InputTemplate from './Input/Template'; import InputTemplate from './Input/Template';
import InputEmoji from './Input/Emoji'; import InputEmoji from './Input/Emoji';
import InputMediaUpload from './Input/MediaUpload'; import InputMediaUpload from './Input/MediaUpload';

Loading…
Cancel
Save