新消息提醒和闪烁

dev/mobile
Lei OT 2 years ago
parent c81e807790
commit 487aff1f3c

@ -370,3 +370,35 @@ export const whatsappError = {
'131048': '[131048] 账户被风控.', // 消息发送太多, 达到垃圾数量限制
'131031': '[131031] 账户已锁定.',
};
/**
* 系统弹窗通知
*/
export const handleNotification = (title, _options) => {
const options = {
requireInteraction: true, // 设置手动关闭
tag: 'global-sales-notification', // 通知ID同类通知建议设置相同ID避免通知过多遮挡桌面
// icon: '/favicon.ico', // 通知图标
..._options,
};
// 检查用户是否同意接受通知
if (Notification.permission === 'granted') {
var notification = new Notification(title, options);
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
var notification = new Notification(title, options);
}
});
} else {
// 用户拒绝通知权限
}
// 通知弹窗被点击后的回调
notification.onclick = () => {
// window.parent.parent.focus();
window.focus(); // 显示当前标签页
notification.close(); // 关闭通知,适用于设置了手动关闭的通知
};
};

@ -1,7 +1,7 @@
import { create } from 'zustand';
import { RealTimeAPI } from '@/lib/realTimeAPI';
import { olog, isEmpty } from '@/utils/utils';
import { receivedMsgTypeMapped } from '@/lib/msgUtils';
import { receivedMsgTypeMapped, handleNotification } from '@/lib/msgUtils';
import { fetchConversationsList, fetchTemplates } from '@/actions/ConversationActions';
import { devtools } from 'zustand/middleware';
import { WS_URL } from '@/config';
@ -57,7 +57,7 @@ const websocketSlice = (set, get) => ({
setWebsocketRetrytimes(0);
},
() => {
setWebsocketOpened(false)
setWebsocketOpened(false);
const newMsgList = Object.keys(activeConversations).reduce((acc, key) => {
const newMsgList = activeConversations[key].slice(-10);
acc[key] = newMsgList;
@ -72,7 +72,7 @@ const websocketSlice = (set, get) => ({
realtimeAPI.onMessage(handleMessage);
realtimeAPI.onCompletion(() => addError('Connection broken'));
olog('Connecting to websocket...', realtimeAPI)
olog('Connecting to websocket...', realtimeAPI);
setWebsocket(realtimeAPI);
},
disconnectWebsocket: () => {
@ -81,7 +81,7 @@ const websocketSlice = (set, get) => ({
return set({ websocket: null });
},
reconnectWebsocket: (userId) => {
const {disconnectWebsocket, connectWebsocket} = get();
const { disconnectWebsocket, connectWebsocket } = get();
disconnectWebsocket();
setTimeout(() => {
connectWebsocket(userId);
@ -112,18 +112,9 @@ const websocketSlice = (set, get) => ({
}
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} ]`,
requireInteraction: true, // 设置手动关闭
tag: 'global-sales-notification', // 通知ID同类通知建议设置相同ID避免通知过多遮挡桌面
...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}),
});
notification.onclick = function () {
window.parent.parent.focus();
};
}
handleNotification(msgRender.senderName, {
body: msgRender?.text || `[ ${msgRender.type} ]`,
...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}),
});
}
console.log('handleMessage*******************');
@ -160,7 +151,7 @@ const conversationSlice = (set, get) => ({
return set((state) => ({
conversationsList: [...newConversations, ...state.conversationsList],
activeConversations: { ...activeConversations, ...newConversationsMapped },
totalNotify: state.totalNotify + newConversations.map(ele => ele.unread_msg_count).reduce((acc, cur) => acc + (cur || 0), 0),
totalNotify: state.totalNotify + newConversations.map((ele) => ele.unread_msg_count).reduce((acc, cur) => acc + (cur || 0), 0),
}));
},
delConversationitem: (conversation) => {
@ -180,10 +171,12 @@ const conversationSlice = (set, get) => ({
const { conversationsList, totalNotify } = 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;
targetIndex !== -1
? conversationsList.splice(targetIndex, 1, {
...conversationsList[targetIndex],
unread_msg_count: 0,
})
: null;
return set({ totalNotify: totalNotify - (conversation.unread_msg_count || 0), currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] });
},
@ -194,11 +187,13 @@ const messageSlice = (set, get) => ({
msgListLoading: false,
activeConversations: {},
setMsgLoading: (msgListLoading) => set({ msgListLoading }),
receivedMessageList: (conversationid, msgList) => set((state) => ({
msgListLoading: false,
activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList }
})),
updateMessageItem: (message) => { // msgUpdate
receivedMessageList: (conversationid, msgList) =>
set((state) => ({
msgListLoading: false,
activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList },
})),
updateMessageItem: (message) => {
// msgUpdate
console.log('UPDATE_SENT_MESSAGE_ITEM-----------------------------------------------------------------');
// 更新会话中的消息
const { activeConversations, conversationsList } = get();
@ -233,7 +228,8 @@ const messageSlice = (set, get) => ({
// conversationsList: [...conversationsList],
});
},
sentOrReceivedNewMessage: (targetId, message) => { // msgRender:
sentOrReceivedNewMessage: (targetId, message) => {
// msgRender:
const { activeConversations, conversationsList, currentConversation, totalNotify } = get();
const targetMsgs = activeConversations[String(targetId)] || [];
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
@ -257,7 +253,7 @@ const messageSlice = (set, get) => ({
conversationsList.splice(targetIndex, 1);
conversationsList.unshift(newConversation);
return set({
totalNotify: totalNotify+newConversation.unread_msg_count,
totalNotify: totalNotify + newConversation.unread_msg_count,
activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] },
conversationsList: [...conversationsList],
currentConversation: {
@ -265,35 +261,36 @@ const messageSlice = (set, get) => ({
...(String(targetId) === String(currentConversation.sn) ? { last_received_time: message.date } : {}),
},
});
},
});
export const useConversationStore = create(devtools((set, get) => ({
...initialConversationState,
...websocketSlice(set, get),
...conversationSlice(set, get),
...templatesSlice(set, get),
...messageSlice(set, get),
...referenceMsgSlice(set, get),
...complexMsgSlice(set, get),
// state actions
addError: (error) => set((state) => ({ errors: [...state.errors, error] })),
setInitial: (v) => set({ initialState: v }),
// side effects
fetchInitialData: async (userId) => {
const { addToConversationList, setTemplates, setInitial } = get();
const conversationsList = await fetchConversationsList({ opisn: userId });
addToConversationList(conversationsList);
const templates = await fetchTemplates();
setTemplates(templates);
setInitial(true);
},
})));
export const useConversationStore = create(
devtools((set, get) => ({
...initialConversationState,
...websocketSlice(set, get),
...conversationSlice(set, get),
...templatesSlice(set, get),
...messageSlice(set, get),
...referenceMsgSlice(set, get),
...complexMsgSlice(set, get),
// state actions
addError: (error) => set((state) => ({ errors: [...state.errors, error] })),
setInitial: (v) => set({ initialState: v }),
// side effects
fetchInitialData: async (userId) => {
const { addToConversationList, setTemplates, setInitial } = get();
const conversationsList = await fetchConversationsList({ opisn: userId });
addToConversationList(conversationsList);
const templates = await fetchTemplates();
setTemplates(templates);
setInitial(true);
},
}))
);
export default useConversationStore;

@ -43,6 +43,12 @@ function AuthApp() {
}
}, [])
useEffect(() => {
Notification.requestPermission();
return () => {};
}, [])
let defaultPath = 'follow'
if (href !== '/') {
@ -54,7 +60,9 @@ function AuthApp() {
token: { colorBgContainer },
} = theme.useToken()
// Flicker title when new message received
/**
* 标签页标题闪烁
*/
const [isTitleVisible, setIsTitleVisible] = useState(true);
useEffect(() => {
let interval;
@ -62,36 +70,13 @@ function AuthApp() {
interval = setInterval(() => {
document.title = isTitleVisible ? `✉🔔🔥【${totalNotify}条新消息】` : '聊天式销售平台';
setIsTitleVisible(!isTitleVisible);
}, 600);
}, 500);
} else {
document.title = '聊天式销售平台';
}
return () => clearInterval(interval);
}, [totalNotify, isTitleVisible]);
// Display browser notification and change favicon when new message received
// useEffect(() => {
// if (hasNewMessage) {
// if (Notification.permission === "granted") {
// new Notification("New messages received!", {
// body: "You have new messages.",
// icon: "path-to-your-icon.png",
// });
// } else if (Notification.permission !== "denied") {
// Notification.requestPermission().then(permission => {
// if (permission === "granted") {
// new Notification("New messages received!", {
// body: "You have new messages.",
// icon: "path-to-your-icon.png",
// });
// }
// });
// }
// setFavicon('path-to-your-new-favicon.ico');
// }
// }, [hasNewMessage]);
return (
<ConfigProvider
theme={{

@ -40,7 +40,7 @@ const Conversations = () => {
const getOrderConversationList = async (colisn) => {
const { whatsapp_phone_number } = switchToC;
const whatsappID = coli_guest_WhatsApp || whatsapp_phone_number || '';
let findCurrent = -1; // conversationsList.findIndex((item) => item.coli_sn === Number(colisn));
let findCurrent = conversationsList.findIndex((item) => item.coli_sn === Number(colisn));
if (!isEmpty(whatsappID)) {
const data = await fetchOrderConversationsList({ opisn: userId, colisn: colisn, whatsappid: whatsappID });
if (!isEmpty(data)) {

@ -128,7 +128,7 @@ const InputComposer = () => {
/>
<Flex justify={'space-between'} className=' bg-gray-200 p-1 rounded-b'>
<Flex gap={4} className='*:text-primary *:rounded-none'>
<InputTemplate key='templates' disabled={!talkabled} invokeSendMessage={invokeSendMessage} />
<InputTemplate key='templates' disabled={textabled} invokeSendMessage={invokeSendMessage} />
<InputEmoji key='emoji' disabled={!textabled} inputEmoji={(s) => setTextContent(`${textContent}${s}`)} />
{/* <InputImageUpload key={'addNewPic'} disabled={!textabled} invokeSendMessage={invokeSendMessage} /> */}
{/* <Button type='text' className='' icon={<YoutubeOutlined />} size={'middle'} />

Loading…
Cancel
Save