Files
tongxunruanjian/pc-client/src/layout/useGlobalEvents.tsx
T
bbc4dce0b6 fix: 验收包写入公司内网地址,避免 Windows 空 VITE 与 Android 假域名
Windows 补 .env.production 与 endpoints 兜底;Android 默认 host/端口改为内网,启动后重写 HttpUtil/ApiService,Urls 改为 getter。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-08-20 16:21:40 +08:00

487 lines
17 KiB
TypeScript

import { CbEvents, LogLevel } from "@openim/wasm-client-sdk";
import { MessageType, SessionType } from "@openim/wasm-client-sdk";
import {
BlackUserItem,
ConversationItem,
FriendApplicationItem,
FriendUserItem,
GroupApplicationItem,
GroupItem,
GroupMemberItem,
MessageItem,
RevokedInfo,
SelfUserInfo,
WSEvent,
WsResponse,
} from "@openim/wasm-client-sdk/lib/types/entity";
import { t } from "i18next";
import { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { CustomType } from "@/constants";
import { API_URL, WS_URL } from "@/config/endpoints";
import {
pushNewMessage,
updateOneMessage,
} from "@/pages/chat/queryChat/useHistoryMessageList";
import { useConversationStore, useUserStore } from "@/store";
import { useContactStore } from "@/store/contact";
import { feedbackToast } from "@/utils/common";
import { initStore } from "@/utils/imCommon";
import { clearIMProfile, getIMToken, getIMUserID } from "@/utils/storage";
import { IMSDK } from "./MainContentWrap";
export function useGlobalEvent() {
const navigate = useNavigate();
const resume = useRef(false);
// user
const updateSyncState = useUserStore((state) => state.updateSyncState);
const updateProgressState = useUserStore((state) => state.updateProgressState);
const updateReinstallState = useUserStore((state) => state.updateReinstallState);
const updateIsLogining = useUserStore((state) => state.updateIsLogining);
const updateConnectState = useUserStore((state) => state.updateConnectState);
const updateSelfInfo = useUserStore((state) => state.updateSelfInfo);
const userLogout = useUserStore((state) => state.userLogout);
// conversation
const updateConversationList = useConversationStore(
(state) => state.updateConversationList,
);
const updateUnReadCount = useConversationStore((state) => state.updateUnReadCount);
const updateCurrentGroupInfo = useConversationStore(
(state) => state.updateCurrentGroupInfo,
);
const getCurrentGroupInfoByReq = useConversationStore(
(state) => state.getCurrentGroupInfoByReq,
);
const setCurrentMemberInGroup = useConversationStore(
(state) => state.setCurrentMemberInGroup,
);
const getCurrentMemberInGroupByReq = useConversationStore(
(state) => state.getCurrentMemberInGroupByReq,
);
const tryUpdateCurrentMemberInGroup = useConversationStore(
(state) => state.tryUpdateCurrentMemberInGroup,
);
const getConversationListByReq = useConversationStore(
(state) => state.getConversationListByReq,
);
const getUnReadCountByReq = useConversationStore(
(state) => state.getUnReadCountByReq,
);
// contact
const getFriendListByReq = useContactStore((state) => state.getFriendListByReq);
const getGroupListByReq = useContactStore((state) => state.getGroupListByReq);
const updateFriend = useContactStore((state) => state.updateFriend);
const pushNewFriend = useContactStore((state) => state.pushNewFriend);
const updateBlack = useContactStore((state) => state.updateBlack);
const pushNewBlack = useContactStore((state) => state.pushNewBlack);
const updateGroup = useContactStore((state) => state.updateGroup);
const pushNewGroup = useContactStore((state) => state.pushNewGroup);
const updateRecvFriendApplication = useContactStore(
(state) => state.updateRecvFriendApplication,
);
const updateSendFriendApplication = useContactStore(
(state) => state.updateSendFriendApplication,
);
const updateRecvGroupApplication = useContactStore(
(state) => state.updateRecvGroupApplication,
);
const updateSendGroupApplication = useContactStore(
(state) => state.updateSendGroupApplication,
);
useEffect(() => {
loginCheck();
setIMListener();
setIpcListener();
window.addEventListener("online", () => {
IMSDK.networkStatusChanged();
});
window.addEventListener("offline", () => {
IMSDK.networkStatusChanged();
});
return () => {
disposeIMListener();
};
}, []);
const loginCheck = async () => {
const IMToken = (await getIMToken()) as string;
const IMUserID = (await getIMUserID()) as string;
if (!IMToken || !IMUserID) {
clearIMProfile();
navigate("/login");
return;
}
tryLogin();
};
const tryLogin = async () => {
updateIsLogining(true);
const IMToken = (await getIMToken()) as string;
const IMUserID = (await getIMUserID()) as string;
try {
const apiAddr = API_URL;
const wsAddr = WS_URL;
if (window.electronAPI) {
await IMSDK.initSDK({
platformID: window.electronAPI?.getPlatform() ?? 5,
apiAddr,
wsAddr,
dataDir: window.electronAPI.getDataPath("sdkResources") || "./",
logFilePath: window.electronAPI.getDataPath("logsPath") || "./",
logLevel: LogLevel.Debug,
isLogStandardOutput: false,
systemType: "electron",
});
await IMSDK.login({
userID: IMUserID,
token: IMToken,
});
} else {
await IMSDK.login({
userID: IMUserID,
token: IMToken,
platformID: 5,
apiAddr,
wsAddr,
logLevel: LogLevel.Debug,
});
}
initStore();
} catch (error) {
console.error(error);
if ((error as WsResponse).errCode !== 10102) {
navigate("/login");
}
}
updateIsLogining(false);
};
const setIMListener = () => {
// account
IMSDK.on(CbEvents.OnSelfInfoUpdated, selfUpdateHandler);
IMSDK.on(CbEvents.OnConnecting, connectingHandler);
IMSDK.on(CbEvents.OnConnectFailed, connectFailedHandler);
IMSDK.on(CbEvents.OnConnectSuccess, connectSuccessHandler);
IMSDK.on(CbEvents.OnKickedOffline, kickHandler);
IMSDK.on(CbEvents.OnUserTokenExpired, expiredHandler);
IMSDK.on(CbEvents.OnUserTokenInvalid, expiredHandler);
// sync
IMSDK.on(CbEvents.OnSyncServerStart, syncStartHandler);
IMSDK.on(CbEvents.OnSyncServerProgress, syncProgressHandler);
IMSDK.on(CbEvents.OnSyncServerFinish, syncFinishHandler);
IMSDK.on(CbEvents.OnSyncServerFailed, syncFailedHandler);
// message
IMSDK.on(CbEvents.OnRecvNewMessages, newMessageHandler);
IMSDK.on(CbEvents.OnNewRecvMessageRevoked, revokedMessageHandler);
// conversation
IMSDK.on(CbEvents.OnConversationChanged, conversationChnageHandler);
IMSDK.on(CbEvents.OnNewConversation, newConversationHandler);
IMSDK.on(CbEvents.OnTotalUnreadMessageCountChanged, totalUnreadChangeHandler);
// friend
IMSDK.on(CbEvents.OnFriendInfoChanged, friednInfoChangeHandler);
IMSDK.on(CbEvents.OnFriendAdded, friednAddedHandler);
IMSDK.on(CbEvents.OnFriendDeleted, friednDeletedHandler);
// blacklist
IMSDK.on(CbEvents.OnBlackAdded, blackAddedHandler);
IMSDK.on(CbEvents.OnBlackDeleted, blackDeletedHandler);
// group
IMSDK.on(CbEvents.OnJoinedGroupAdded, joinedGroupAddedHandler);
IMSDK.on(CbEvents.OnJoinedGroupDeleted, joinedGroupDeletedHandler);
IMSDK.on(CbEvents.OnGroupDismissed, joinedGroupDismissHandler);
IMSDK.on(CbEvents.OnGroupInfoChanged, groupInfoChangedHandler);
IMSDK.on(CbEvents.OnGroupMemberAdded, groupMemberAddedHandler);
IMSDK.on(CbEvents.OnGroupMemberDeleted, groupMemberDeletedHandler);
IMSDK.on(CbEvents.OnGroupMemberInfoChanged, groupMemberInfoChangedHandler);
// application
IMSDK.on(CbEvents.OnFriendApplicationAdded, friendApplicationProcessedHandler);
IMSDK.on(CbEvents.OnFriendApplicationAccepted, friendApplicationProcessedHandler);
IMSDK.on(CbEvents.OnFriendApplicationRejected, friendApplicationProcessedHandler);
IMSDK.on(CbEvents.OnGroupApplicationAdded, groupApplicationProcessedHandler);
IMSDK.on(CbEvents.OnGroupApplicationAccepted, groupApplicationProcessedHandler);
IMSDK.on(CbEvents.OnGroupApplicationRejected, groupApplicationProcessedHandler);
};
const selfUpdateHandler = ({ data }: WSEvent<SelfUserInfo>) => {
updateSelfInfo(data);
};
const connectingHandler = () => {
updateConnectState("loading");
console.log("connecting...");
};
const connectFailedHandler = ({ errCode, errMsg }: WSEvent) => {
updateConnectState("failed");
console.error("connectFailedHandler", errCode, errMsg);
if (errCode === 705) {
tryOut(t("toast.loginExpiration"));
}
};
const connectSuccessHandler = () => {
updateConnectState("success");
console.log("connect success...");
};
const kickHandler = () => tryOut(t("toast.accountKicked"));
const expiredHandler = () => tryOut(t("toast.loginExpiration"));
const tryOut = (msg: string) =>
feedbackToast({
msg,
error: msg,
onClose: () => {
userLogout(true);
},
});
// sync
const syncStartHandler = ({ data }: WSEvent<boolean>) => {
updateSyncState("loading");
updateReinstallState(data);
};
const syncProgressHandler = ({ data }: WSEvent<number>) => {
updateProgressState(data);
};
const syncFinishHandler = () => {
updateSyncState("success");
getFriendListByReq();
getGroupListByReq();
getConversationListByReq(false);
getUnReadCountByReq();
};
const syncFailedHandler = () => {
updateSyncState("failed");
feedbackToast({ msg: t("toast.syncFailed"), error: t("toast.syncFailed") });
};
// message
const newMessageHandler = ({ data }: WSEvent<MessageItem[]>) => {
if (useUserStore.getState().syncState === "loading" || resume.current) {
return;
}
data.map((message) => handleNewMessage(message));
};
const revokedMessageHandler = ({ data }: WSEvent<RevokedInfo>) => {
updateOneMessage({
clientMsgID: data.clientMsgID,
contentType: MessageType.RevokeMessage,
notificationElem: {
detail: JSON.stringify(data),
},
} as MessageItem);
};
const notPushType = [MessageType.TypingMessage, MessageType.RevokeMessage];
const handleNewMessage = (newServerMsg: MessageItem) => {
if (newServerMsg.contentType === MessageType.CustomMessage) {
const customData = JSON.parse(newServerMsg.customElem!.data);
if (
CustomType.CallingInvite <= customData.customType &&
customData.customType <= CustomType.CallingHungup
) {
return;
}
}
if (!inCurrentConversation(newServerMsg)) return;
if (!notPushType.includes(newServerMsg.contentType)) {
pushNewMessage(newServerMsg);
}
};
const inCurrentConversation = (newServerMsg: MessageItem) => {
switch (newServerMsg.sessionType) {
case SessionType.Single:
return (
newServerMsg.sendID ===
useConversationStore.getState().currentConversation?.userID ||
(newServerMsg.sendID === useUserStore.getState().selfInfo.userID &&
newServerMsg.recvID ===
useConversationStore.getState().currentConversation?.userID)
);
case SessionType.Group:
case SessionType.WorkingGroup:
return (
newServerMsg.groupID ===
useConversationStore.getState().currentConversation?.groupID
);
case SessionType.Notification:
return (
newServerMsg.sendID ===
useConversationStore.getState().currentConversation?.userID
);
default:
return false;
}
};
// conversation
const conversationChnageHandler = ({ data }: WSEvent<ConversationItem[]>) => {
updateConversationList(data, "filter");
};
const newConversationHandler = ({ data }: WSEvent<ConversationItem[]>) => {
updateConversationList(data, "push");
};
const totalUnreadChangeHandler = ({ data }: WSEvent<number>) => {
if (data === useConversationStore.getState().unReadCount) return;
updateUnReadCount(data);
};
// friend
const friednInfoChangeHandler = ({ data }: WSEvent<FriendUserItem>) => {
updateFriend(data);
};
const friednAddedHandler = ({ data }: WSEvent<FriendUserItem>) => {
pushNewFriend(data);
};
const friednDeletedHandler = ({ data }: WSEvent<FriendUserItem>) => {
updateFriend(data, true);
};
// blacklist
const blackAddedHandler = ({ data }: WSEvent<BlackUserItem>) => {
pushNewBlack(data);
};
const blackDeletedHandler = ({ data }: WSEvent<BlackUserItem>) => {
IMSDK.getSpecifiedFriendsInfo({
friendUserIDList: [data.userID],
}).then(({ data }) => {
if (data.length) {
pushNewFriend(data[0]);
}
});
updateBlack(data, true);
};
// group
const joinedGroupAddedHandler = ({ data }: WSEvent<GroupItem>) => {
if (data.groupID === useConversationStore.getState().currentConversation?.groupID) {
updateCurrentGroupInfo(data);
getCurrentMemberInGroupByReq(data.groupID);
}
pushNewGroup(data);
};
const joinedGroupDeletedHandler = ({ data }: WSEvent<GroupItem>) => {
if (data.groupID === useConversationStore.getState().currentConversation?.groupID) {
getCurrentGroupInfoByReq(data.groupID);
setCurrentMemberInGroup();
}
updateGroup(data, true);
};
const joinedGroupDismissHandler = ({ data }: WSEvent<GroupItem>) => {
if (data.groupID === useConversationStore.getState().currentConversation?.groupID) {
getCurrentMemberInGroupByReq(data.groupID);
}
};
const groupInfoChangedHandler = ({ data }: WSEvent<GroupItem>) => {
updateGroup(data);
if (data.groupID === useConversationStore.getState().currentConversation?.groupID) {
updateCurrentGroupInfo(data);
}
};
const groupMemberAddedHandler = ({ data }: WSEvent<GroupMemberItem>) => {
if (
data.groupID === useConversationStore.getState().currentConversation?.groupID &&
data.userID === useUserStore.getState().selfInfo.userID
) {
getCurrentMemberInGroupByReq(data.groupID);
}
};
const groupMemberDeletedHandler = ({ data }: WSEvent<GroupMemberItem>) => {
if (
data.groupID === useConversationStore.getState().currentConversation?.groupID &&
data.userID === useUserStore.getState().selfInfo.userID
) {
getCurrentMemberInGroupByReq(data.groupID);
}
};
const groupMemberInfoChangedHandler = ({ data }: WSEvent<GroupMemberItem>) => {
if (data.groupID === useConversationStore.getState().currentConversation?.groupID) {
tryUpdateCurrentMemberInGroup(data);
}
};
//application
const friendApplicationProcessedHandler = ({
data,
}: WSEvent<FriendApplicationItem>) => {
const isRecv = data.toUserID === useUserStore.getState().selfInfo.userID;
if (isRecv) {
updateRecvFriendApplication(data);
} else {
updateSendFriendApplication(data);
}
};
const groupApplicationProcessedHandler = ({
data,
}: WSEvent<GroupApplicationItem>) => {
const isRecv = data.userID !== useUserStore.getState().selfInfo.userID;
if (isRecv) {
updateRecvGroupApplication(data);
} else {
updateSendGroupApplication(data);
}
};
const disposeIMListener = () => {
IMSDK.off(CbEvents.OnSelfInfoUpdated, selfUpdateHandler);
IMSDK.off(CbEvents.OnConnecting, connectingHandler);
IMSDK.off(CbEvents.OnConnectFailed, connectFailedHandler);
IMSDK.off(CbEvents.OnConnectSuccess, connectSuccessHandler);
IMSDK.off(CbEvents.OnKickedOffline, kickHandler);
IMSDK.off(CbEvents.OnUserTokenExpired, expiredHandler);
IMSDK.off(CbEvents.OnUserTokenInvalid, expiredHandler);
// sync
IMSDK.off(CbEvents.OnSyncServerStart, syncStartHandler);
IMSDK.off(CbEvents.OnSyncServerProgress, syncProgressHandler);
IMSDK.off(CbEvents.OnSyncServerFinish, syncFinishHandler);
IMSDK.off(CbEvents.OnSyncServerFailed, syncFailedHandler);
// message
IMSDK.off(CbEvents.OnRecvNewMessages, newMessageHandler);
// conversation
IMSDK.off(CbEvents.OnConversationChanged, conversationChnageHandler);
IMSDK.off(CbEvents.OnNewConversation, newConversationHandler);
IMSDK.off(CbEvents.OnTotalUnreadMessageCountChanged, totalUnreadChangeHandler);
// friend
IMSDK.off(CbEvents.OnFriendInfoChanged, friednInfoChangeHandler);
IMSDK.off(CbEvents.OnFriendAdded, friednAddedHandler);
IMSDK.off(CbEvents.OnFriendDeleted, friednDeletedHandler);
// blacklist
IMSDK.off(CbEvents.OnBlackAdded, blackAddedHandler);
IMSDK.off(CbEvents.OnBlackDeleted, blackDeletedHandler);
// group
IMSDK.off(CbEvents.OnJoinedGroupAdded, joinedGroupAddedHandler);
IMSDK.off(CbEvents.OnJoinedGroupDeleted, joinedGroupDeletedHandler);
IMSDK.off(CbEvents.OnGroupDismissed, joinedGroupDismissHandler);
IMSDK.off(CbEvents.OnGroupInfoChanged, groupInfoChangedHandler);
IMSDK.off(CbEvents.OnGroupMemberAdded, groupMemberAddedHandler);
IMSDK.off(CbEvents.OnGroupMemberDeleted, groupMemberDeletedHandler);
IMSDK.off(CbEvents.OnGroupMemberInfoChanged, groupMemberInfoChangedHandler);
// application
IMSDK.off(CbEvents.OnFriendApplicationAdded, friendApplicationProcessedHandler);
IMSDK.off(CbEvents.OnFriendApplicationAccepted, friendApplicationProcessedHandler);
IMSDK.off(CbEvents.OnFriendApplicationRejected, friendApplicationProcessedHandler);
IMSDK.off(CbEvents.OnGroupApplicationAdded, groupApplicationProcessedHandler);
IMSDK.off(CbEvents.OnGroupApplicationAccepted, groupApplicationProcessedHandler);
IMSDK.off(CbEvents.OnGroupApplicationRejected, groupApplicationProcessedHandler);
};
const setIpcListener = () => {
window.electronAPI?.subscribe("appResume", () => {
if (resume.current) {
return;
}
resume.current = true;
setTimeout(() => {
resume.current = false;
}, 5000);
});
};
}