发送图片链接方法; 状态管理: zustand; 删除context provider
parent
120741fade
commit
8b6679ba49
@ -0,0 +1,262 @@
|
||||
import { create } from 'zustand';
|
||||
import { RealTimeAPI } from '@/lib/realTimeAPI';
|
||||
import { olog, isEmpty } from '@/utils/utils';
|
||||
import { receivedMsgTypeMapped } from '@/lib/msgUtils';
|
||||
import { fetchConversationsList, fetchTemplates } from '@/actions/ConversationActions';
|
||||
|
||||
// const WS_URL = 'ws://202.103.68.144:8888/whatever/';
|
||||
// const WS_URL = 'ws://120.79.9.217:10022/whatever/';
|
||||
const WS_URL = 'wss://p9axztuwd7x8a7.mycht.cn/whatsapp_callback'; // prod:
|
||||
|
||||
const initialConversationState = {
|
||||
// websocket: null,
|
||||
// websocketOpened: null,
|
||||
// websocketRetrying: null,
|
||||
// websocketRetrytimes: null,
|
||||
|
||||
errors: [], // 错误信息
|
||||
|
||||
// templates: [],
|
||||
|
||||
// conversationsList: [], // 对话列表
|
||||
// currentConversation: {}, // 当前对话
|
||||
|
||||
// activeConversations: {}, // 激活的对话的消息列表: { [conversationId]: <messageItem>[] }
|
||||
|
||||
// referenceMsg: {},
|
||||
};
|
||||
olog('initialConversationState');
|
||||
|
||||
export const templatesSlice = (set) => ({
|
||||
templates: [],
|
||||
setTemplates: (templates) => set({ templates }),
|
||||
});
|
||||
|
||||
export const websocketSlice = (set, get) => ({
|
||||
websocket: null,
|
||||
websocketOpened: null,
|
||||
websocketRetrying: null,
|
||||
websocketRetrytimes: null,
|
||||
|
||||
setWebsocket: (websocket) => set({ websocket }),
|
||||
setWebsocketOpened: (opened) => set({ websocketOpened: opened }),
|
||||
setWebsocketRetrying: (retrying) => set({ websocketRetrying: retrying }),
|
||||
setWebsocketRetrytimes: (retrytimes) => set({ websocketRetrytimes: retrytimes, websocketRetrying: retrytimes > 0 }),
|
||||
|
||||
connectWebsocket: (userId) => {
|
||||
const { setWebsocket, setWebsocketOpened, setWebsocketRetrytimes, addError, handleMessage } = get();
|
||||
|
||||
const realtimeAPI = new RealTimeAPI(
|
||||
{
|
||||
url: `${WS_URL}?opisn=${userId || ''}&_spam=${Date.now().toString()}`,
|
||||
protocol: 'WhatsApp',
|
||||
},
|
||||
() => {
|
||||
setWebsocketOpened(true);
|
||||
setWebsocketRetrytimes(0);
|
||||
},
|
||||
() => setWebsocketOpened(false),
|
||||
(n) => setWebsocketRetrytimes(n)
|
||||
);
|
||||
|
||||
realtimeAPI.onError(() => addError('Error'));
|
||||
realtimeAPI.onMessage(handleMessage);
|
||||
realtimeAPI.onCompletion(() => addError('Connection broken'));
|
||||
|
||||
setWebsocket(realtimeAPI);
|
||||
},
|
||||
disconnectWebsocket: () => {
|
||||
const { websocket } = get();
|
||||
websocket.disconnect();
|
||||
return set({ websocket: null });
|
||||
},
|
||||
handleMessage: (data) => {
|
||||
console.log('handleMessage------------------');
|
||||
console.log(data);
|
||||
const { updateMessageItem, sentOrReceivedNewMessage } = get();
|
||||
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)) {
|
||||
updateMessageItem(msgUpdate);
|
||||
// return false;
|
||||
}
|
||||
if (!isEmpty(msgRender)) {
|
||||
sentOrReceivedNewMessage(msgRender.conversationid, msgRender);
|
||||
window.Notification.requestPermission().then(function (permission) {
|
||||
if (permission === 'granted') {
|
||||
const notification = new Notification(`${msgRender.senderName}`, {
|
||||
body: msgRender?.text || `[ ${msgRender.type} ]`,
|
||||
...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}),
|
||||
});
|
||||
notification.onclick = function () {
|
||||
window.parent.parent.focus();
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log('handleMessage*******************');
|
||||
},
|
||||
});
|
||||
|
||||
export const referenceMsgSlice = (set) => ({
|
||||
referenceMsg: [],
|
||||
setReferenceMsg: (referenceMsg) => set({ referenceMsg }),
|
||||
});
|
||||
|
||||
export const conversationSlice = (set, get) => ({
|
||||
conversationsList: [],
|
||||
currentConversation: {},
|
||||
|
||||
setConversationsList: (conversationsList) => {
|
||||
const conversationsMapped = conversationsList.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});
|
||||
return set({ conversationsList, activeConversations: conversationsMapped });
|
||||
},
|
||||
addToConversationList: (newList) => {
|
||||
const { activeConversations } = get();
|
||||
const conversationsIds = Object.keys(activeConversations);
|
||||
const newConversations = newList.filter((conversation) => !conversationsIds.includes(`${conversation.sn}`));
|
||||
const newConversationsMapped = newConversations.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});
|
||||
|
||||
return set((state) => ({
|
||||
conversationsList: [...newConversations, ...state.conversationsList],
|
||||
activeConversations: { ...activeConversations, ...newConversationsMapped }
|
||||
}));
|
||||
},
|
||||
delConversationitem: (conversation) => {
|
||||
const { conversationsList, activeConversations } = get();
|
||||
const targetId = conversation.sn;
|
||||
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
|
||||
conversationsList.splice(targetIndex, 1);
|
||||
|
||||
return set((state) => ({
|
||||
conversationsList: [...conversationsList],
|
||||
activeConversations: { ...activeConversations, [`${targetId}`]: [] },
|
||||
currentConversation: {},
|
||||
}));
|
||||
},
|
||||
setCurrentConversation: (conversation) => {
|
||||
// 清空未读
|
||||
const { conversationsList } = get();
|
||||
const targetId = conversation.sn;
|
||||
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
|
||||
targetIndex !== -1 ? conversationsList.splice(targetIndex, 1, {
|
||||
...conversationsList[targetIndex],
|
||||
unread_msg_count: 0,
|
||||
}) : null;
|
||||
|
||||
return set({ currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] });
|
||||
},
|
||||
});
|
||||
|
||||
export const messageSlice = (set, get) => ({
|
||||
activeConversations: {},
|
||||
receivedMessageList: (conversationid, msgList) => set((state) => ({
|
||||
activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList }
|
||||
})),
|
||||
updateMessageItem: (message) => { // msgUpdate
|
||||
console.log('UPDATE_SENT_MESSAGE_ITEM-----------------------------------------------------------------');
|
||||
// 更新会话中的消息
|
||||
const { activeConversations, conversationsList } = get();
|
||||
const targetId = message.conversationid;
|
||||
const targetMsgs = (activeConversations[String(targetId)] || []).map((ele) => {
|
||||
// 更新状态
|
||||
// * 已读的不再更新状态, 有时候投递结果在已读之后返回
|
||||
if (ele.id === ele.actionId && ele.actionId === message.actionId) {
|
||||
return { ...ele, id: message.id, status: ele.status === 'read' ? ele.status : message.status, dateString: message.dateString };
|
||||
} else if (ele.id === message.id) {
|
||||
return { ...ele, id: message.id, status: ele.status === 'read' ? ele.status : message.status, dateString: message.dateString };
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
// 显示会话中其他客户端发送的消息
|
||||
const targetMsgsIds = targetMsgs.map((ele) => ele.id);
|
||||
if (!targetMsgsIds.includes(message.id)) {
|
||||
targetMsgs.push(message);
|
||||
}
|
||||
|
||||
// 更新列表的时间
|
||||
// if (message.type !== 'error') {
|
||||
// const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
|
||||
// conversationsList.splice(targetIndex, 1, {
|
||||
// ...conversationsList[targetIndex],
|
||||
// last_received_time: message.date,
|
||||
// });
|
||||
// }
|
||||
|
||||
return set((state) => ({
|
||||
activeConversations: { ...activeConversations, [String(targetId)]: targetMsgs },
|
||||
conversationsList: [...conversationsList],
|
||||
}));
|
||||
},
|
||||
sentOrReceivedNewMessage: (targetId, message) => { // msgRender:
|
||||
const { activeConversations, conversationsList, currentConversation } = get();
|
||||
const targetMsgs = activeConversations[String(targetId)] || [];
|
||||
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
|
||||
const newConversation =
|
||||
targetId !== -1
|
||||
? {
|
||||
...conversationsList[targetIndex],
|
||||
last_received_time: message.type !== 'system' && message.sender !== 'me' ? message.date : conversationsList[targetIndex].last_received_time,
|
||||
unread_msg_count:
|
||||
String(targetId) !== String(currentConversation.sn) && message.sender !== 'me'
|
||||
? conversationsList[targetIndex].unread_msg_count + 1
|
||||
: conversationsList[targetIndex].unread_msg_count,
|
||||
}
|
||||
: {
|
||||
...message,
|
||||
sn: targetId,
|
||||
last_received_time: message.date,
|
||||
unread_msg_count: message.sender === 'me' ? 0 : 1,
|
||||
};
|
||||
conversationsList.splice(targetIndex, 1);
|
||||
conversationsList.unshift(newConversation);
|
||||
return set((state) => ({
|
||||
activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] },
|
||||
conversationsList: [...conversationsList],
|
||||
currentConversation: {
|
||||
...currentConversation,
|
||||
last_received_time: String(targetId) === String(currentConversation.sn) ? message.date : currentConversation.last_received_time,
|
||||
},
|
||||
}));
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
export const useConversationStore = create((set, get) => ({
|
||||
...initialConversationState,
|
||||
...websocketSlice(set, get),
|
||||
...conversationSlice(set, get),
|
||||
...templatesSlice(set, get),
|
||||
...messageSlice(set, get),
|
||||
...referenceMsgSlice(set, get),
|
||||
|
||||
// state actions
|
||||
addError: (error) => set((state) => ({ errors: [...state.errors, error] })),
|
||||
|
||||
// side effects
|
||||
fetchInitialData: async (userId) => {
|
||||
olog('fetch init');
|
||||
const { setConversationsList, setTemplates } = get();
|
||||
|
||||
const conversationsList = await fetchConversationsList({ opisn: userId });
|
||||
setConversationsList(conversationsList);
|
||||
|
||||
const templates = await fetchTemplates();
|
||||
setTemplates(templates);
|
||||
},
|
||||
}));
|
||||
// window.store = useConversationStore; // debug:
|
||||
export default useConversationStore;
|
Loading…
Reference in New Issue