diff --git a/package.json b/package.json index bbe2a40..759ff1b 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "format": "prettier --write .", "lint": "eslint --ext .js,.jsx,.ts,.tsx src", "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx --fix --quiet src", - "prepare": "husky install" + "prepare": "husky install", + "postinstall": "patch-package" }, "devDependencies": { "@commitlint/cli": "^17.4.4", @@ -44,6 +45,7 @@ "eslint-plugin-simple-import-sort": "^10.0.0", "husky": "^8.0.3", "lint-staged": "^13.1.2", + "patch-package": "^8.0.0", "postcss": "^8.4.21", "postcss-nesting": "^12.0.2", "prettier": "^2.8.4", @@ -72,21 +74,14 @@ }, "dependencies": { "@ant-design/icons": "^5.1.4", - "@ckeditor/ckeditor5-basic-styles": "^42.0.0", - "@ckeditor/ckeditor5-editor-classic": "^42.0.0", - "@ckeditor/ckeditor5-essentials": "^42.0.0", - "@ckeditor/ckeditor5-image": "^42.0.0", - "@ckeditor/ckeditor5-paragraph": "^42.0.0", "@ckeditor/ckeditor5-react": "^8.0.0", - "@ckeditor/ckeditor5-theme-lark": "^42.0.0", - "@ckeditor/vite-plugin-ckeditor5": "^0.1.3", "@livekit/components-react": "^2.3.1", "@livekit/components-styles": "^1.0.12", - "@openim/wasm-client-sdk": "^3.8.1", - "@tailwindcss/line-clamp": "^0.4.4", + "@openim/wasm-client-sdk": "^3.8.2", "ahooks": "^3.7.7", - "antd": "^5.6.0", + "antd": "5.10.0", "axios": "^1.4.0", + "ckeditor5": "^43.0.0", "clsx": "^1.2.1", "date-fns": "^2.30.0", "dayjs": "^1.11.7", diff --git a/patches/@ckeditor+ckeditor5-ui+43.0.0.patch b/patches/@ckeditor+ckeditor5-ui+43.0.0.patch new file mode 100644 index 0000000..d820bd8 --- /dev/null +++ b/patches/@ckeditor+ckeditor5-ui+43.0.0.patch @@ -0,0 +1,42 @@ +diff --git a/node_modules/@ckeditor/ckeditor5-ui/src/editorui/poweredby.js b/node_modules/@ckeditor/ckeditor5-ui/src/editorui/poweredby.js +index 191aba1..324f6d2 100644 +--- a/node_modules/@ckeditor/ckeditor5-ui/src/editorui/poweredby.js ++++ b/node_modules/@ckeditor/ckeditor5-ui/src/editorui/poweredby.js +@@ -112,12 +112,9 @@ export default class PoweredBy extends /* #__PURE__ */ DomEmitterMixin() { + if (!this._balloonView) { + this._createBalloonView(); + } +- this._balloonView.pin(attachOptions); ++ // this._balloonView.pin(attachOptions); + } + } +- /** +- * Hides the "powered by" balloon if already visible. +- */ + _hideBalloon() { + if (this._balloonView) { + this._balloonView.unpin(); +diff --git a/node_modules/@ckeditor/ckeditor5-ui/src/icon/iconview.js b/node_modules/@ckeditor/ckeditor5-ui/src/icon/iconview.js +index dd6d2ed..7af8e61 100644 +--- a/node_modules/@ckeditor/ckeditor5-ui/src/icon/iconview.js ++++ b/node_modules/@ckeditor/ckeditor5-ui/src/icon/iconview.js +@@ -42,17 +42,14 @@ class IconView extends View { + } + }); + } +- /** +- * @inheritDoc +- */ + render() { + super.render(); +- this._updateXMLContent(); ++ // this._updateXMLContent(); + this._colorFillPaths(); + // This is a hack for lack of innerHTML binding. + // See: https://github.com/ckeditor/ckeditor5-ui/issues/99. + this.on('change:content', () => { +- this._updateXMLContent(); ++ // this._updateXMLContent(); + this._colorFillPaths(); + }); + this.on('change:fillColor', () => { diff --git a/public/openIM.wasm b/public/openIM.wasm index 86a32c0..8a29a22 100644 Binary files a/public/openIM.wasm and b/public/openIM.wasm differ diff --git a/src/components/CKEditor/index.tsx b/src/components/CKEditor/index.tsx index 15ee928..8d1e1e2 100644 --- a/src/components/CKEditor/index.tsx +++ b/src/components/CKEditor/index.tsx @@ -1,4 +1,5 @@ import "./index.scss"; +import "ckeditor5/ckeditor5.css"; import { ClassicEditor } from "@ckeditor/ckeditor5-editor-classic"; import { Essentials } from "@ckeditor/ckeditor5-essentials"; diff --git a/src/layout/LeftNavBar/index.tsx b/src/layout/LeftNavBar/index.tsx index 8bf16d7..c58a5b3 100644 --- a/src/layout/LeftNavBar/index.tsx +++ b/src/layout/LeftNavBar/index.tsx @@ -17,8 +17,6 @@ import message_icon_active from "@/assets/images/nav/nav_bar_message_active.png" import change_avatar from "@/assets/images/profile/change_avatar.png"; import OIMAvatar from "@/components/OIMAvatar"; import { useContactStore, useConversationStore, useUserStore } from "@/store"; -import { ChildWindowOptions } from "@/types/common"; -import { openAbout, openPersonalSettings } from "@/utils/childWindows"; import { feedbackToast } from "@/utils/common"; import emitter from "@/utils/events"; diff --git a/src/layout/TopSearchBar/SearchUserOrGroup.tsx b/src/layout/TopSearchBar/SearchUserOrGroup.tsx index ab9f301..278f2aa 100644 --- a/src/layout/TopSearchBar/SearchUserOrGroup.tsx +++ b/src/layout/TopSearchBar/SearchUserOrGroup.tsx @@ -16,6 +16,7 @@ import { searchBusinessUserInfo } from "@/api/login"; import DraggableModalWrap from "@/components/DraggableModalWrap"; import { OverlayVisibleHandle, useOverlayVisible } from "@/hooks/useOverlayVisible"; import { CardInfo } from "@/pages/common/UserCardModal"; +import { useContactStore } from "@/store"; import { feedbackToast } from "@/utils/common"; import { IMSDK } from "../MainContentWrap"; @@ -71,13 +72,12 @@ const SearchUserOrGroup: ForwardRefRenderFunction< message.warning(t("empty.noSearchResults")); return; } - const { data } = await IMSDK.getUsersInfoWithCache({ - userIDList: [users[0].userID], - }); - const friendInfo = data[0].friendInfo; + const friendInfo = useContactStore + .getState() + .friendList.find((friend) => friend.userID === users[0].userID); openUserCardWithData({ - ...friendInfo, + ...(friendInfo ?? {}), ...users[0], }); } catch (error) { diff --git a/src/layout/TopSearchBar/index.tsx b/src/layout/TopSearchBar/index.tsx index c690940..702f6eb 100644 --- a/src/layout/TopSearchBar/index.tsx +++ b/src/layout/TopSearchBar/index.tsx @@ -66,7 +66,7 @@ const TopSearchBar = () => { let rtcInvite = undefined as undefined | RtcInvite; data.map((message) => { if (message.contentType === MessageType.CustomMessage) { - const customData = JSON.parse(message.customElem.data); + const customData = JSON.parse(message.customElem!.data); if (customData.customType === CustomType.CallingInvite) { rtcInvite = customData.data; } diff --git a/src/layout/useGlobalEvents.tsx b/src/layout/useGlobalEvents.tsx index 8caedf2..3331d6e 100644 --- a/src/layout/useGlobalEvents.tsx +++ b/src/layout/useGlobalEvents.tsx @@ -231,7 +231,7 @@ export function useGlobalEvent() { } data.map((message) => { if (message.contentType === MessageType.CustomMessage) { - const customData = JSON.parse(message.customElem.data); + const customData = JSON.parse(message.customElem!.data); if ( CustomType.CallingInvite <= customData.customType && customData.customType <= CustomType.CallingHungup diff --git a/src/pages/chat/queryChat/MessageItem/MediaMessageRender.tsx b/src/pages/chat/queryChat/MessageItem/MediaMessageRender.tsx index 256f6a7..0411a54 100644 --- a/src/pages/chat/queryChat/MessageItem/MediaMessageRender.tsx +++ b/src/pages/chat/queryChat/MessageItem/MediaMessageRender.tsx @@ -16,14 +16,14 @@ const MediaMessageRender: FC = ({ message }) => { const isVideoMessage = message.contentType === MessageType.VideoMessage; const imageHeight = isVideoMessage - ? message.videoElem.snapshotHeight - : message.pictureElem.sourcePicture.height; + ? message.videoElem!.snapshotHeight + : message.pictureElem!.sourcePicture.height; const imageWidth = isVideoMessage - ? message.videoElem.snapshotWidth - : message.pictureElem.sourcePicture.width; + ? message.videoElem!.snapshotWidth + : message.pictureElem!.sourcePicture.width; const snapshotMaxHeight = isVideoMessage ? 320 - : message.pictureElem.snapshotPicture.height; + : message.pictureElem!.snapshotPicture.height; const minHeight = min(200, imageWidth) * (imageHeight / imageWidth) + 2; const adaptedHight = min(minHeight, snapshotMaxHeight) + 10; const adaptedWidth = min(imageWidth, 200) + 10; @@ -31,8 +31,8 @@ const MediaMessageRender: FC = ({ message }) => { const isSucceed = message.status === MessageStatus.Succeed; const sourceUrl = isVideoMessage - ? message.videoElem.snapshotUrl - : message.pictureElem.snapshotPicture.url; + ? message.videoElem!.snapshotUrl + : message.pictureElem!.snapshotPicture.url; const isSending = message.status === MessageStatus.Sending; const minStyle = { minHeight: `${adaptedHight}px`, minWidth: `${adaptedWidth}px` }; @@ -42,7 +42,7 @@ const MediaMessageRender: FC = ({ message }) => {
isVideoMessage && showVideoPlayer(message.videoElem.videoUrl)} + onClick={() => isVideoMessage && showVideoPlayer(message.videoElem!.videoUrl)} > = ({ message }) => { /> {isVideoMessage && (
- {secondsToMS(message.videoElem.duration)} + {secondsToMS(message.videoElem!.duration)}
)} {isVideoMessage && isSucceed && ( diff --git a/src/pages/chat/queryChat/MessageItem/TextMessageRender.tsx b/src/pages/chat/queryChat/MessageItem/TextMessageRender.tsx index f7d9efd..ce95494 100644 --- a/src/pages/chat/queryChat/MessageItem/TextMessageRender.tsx +++ b/src/pages/chat/queryChat/MessageItem/TextMessageRender.tsx @@ -7,7 +7,7 @@ import { IMessageItemProps } from "."; import styles from "./message-item.module.scss"; const TextMessageRender: FC = ({ message }) => { - let content = message.textElem?.content; + let content = message.textElem?.content || ""; content = formatBr(content); diff --git a/src/pages/common/RtcCallModal/RtcControl.tsx b/src/pages/common/RtcCallModal/RtcControl.tsx index 400336b..c0feeb8 100644 --- a/src/pages/common/RtcCallModal/RtcControl.tsx +++ b/src/pages/common/RtcCallModal/RtcControl.tsx @@ -90,7 +90,7 @@ export const RtcControl = ({ const newMessageHandler = ({ data }: WSEvent) => { data.map((message) => { if (message.contentType === MessageType.CustomMessage) { - const customData = JSON.parse(message.customElem.data) as { + const customData = JSON.parse(message.customElem!.data) as { data: RtcInvite; customType: CustomType; }; diff --git a/src/pages/common/UserCardModal/index.tsx b/src/pages/common/UserCardModal/index.tsx index 0930260..9f11eb2 100644 --- a/src/pages/common/UserCardModal/index.tsx +++ b/src/pages/common/UserCardModal/index.tsx @@ -108,11 +108,12 @@ const UserCardModal: ForwardRefRenderFunction< return selfInfo; } let userInfo: CardInfo | null = null; - const { data } = await IMSDK.getUsersInfoWithCache({ - userIDList: [userID!], - groupID: groupID, - }); - userInfo = { ...(data[0]?.friendInfo ?? data[0]?.publicInfo) }; + const friendInfo = useContactStore + .getState() + .friendList.find((item) => item.userID === userID); + if (friendInfo) { + userInfo = { ...friendInfo }; + } try { const { diff --git a/src/store/contact.ts b/src/store/contact.ts index f5f02e1..6107b7f 100644 --- a/src/store/contact.ts +++ b/src/store/contact.ts @@ -3,7 +3,6 @@ import { BlackUserItem, FriendApplicationItem, FriendUserItem, - FullUserItem, GroupApplicationItem, GroupItem, } from "@openim/wasm-client-sdk/lib/types/entity"; @@ -28,7 +27,7 @@ export const useContactStore = create()((set, get) => ({ getFriendListByReq: async () => { try { let offset = 0; - let tmpList = [] as FullUserItem[]; + let tmpList = [] as FriendUserItem[]; let initialFetch = true; // eslint-disable-next-line while (true) { @@ -41,7 +40,7 @@ export const useContactStore = create()((set, get) => ({ } // const { data } = await IMSDK.getFriendList(); set(() => ({ - friendList: tmpList.map((item) => item.friendInfo!), + friendList: [...tmpList], })); } catch (error) { feedbackToast({ error, msg: t("toast.getFriendListFailed") }); diff --git a/src/utils/imCommon.ts b/src/utils/imCommon.ts index bd369c3..c91df0b 100644 --- a/src/utils/imCommon.ts +++ b/src/utils/imCommon.ts @@ -3,20 +3,12 @@ import calendar from "dayjs/plugin/calendar"; import relativeTime from "dayjs/plugin/relativeTime"; import updateLocale from "dayjs/plugin/updateLocale"; import { t } from "i18next"; -import default_group from "@/assets/images/contact/my_groups.png"; -import { v4 as uuidv4 } from "uuid"; -import { - CustomMessageType, - GroupSessionTypes, - SystemMessageTypes, -} from "@/constants/im"; +import { GroupSessionTypes } from "@/constants/im"; import { useConversationStore, useUserStore } from "@/store"; import { useContactStore } from "@/store/contact"; -import { generateAvatar, secondsToTime } from "./common"; import { - AtTextElem, ConversationItem, MessageItem, PublicUserItem, @@ -72,7 +64,7 @@ export const notificationMessageFormat = (msg: MessageItem) => { case MessageType.FriendAdded: return t("messageDescription.alreadyFriendMessage"); case MessageType.GroupCreated: - const groupCreatedDetail = JSON.parse(msg.notificationElem.detail); + const groupCreatedDetail = JSON.parse(msg.notificationElem!.detail); const groupCreatedUser = groupCreatedDetail.opUser; return t("messageDescription.createGroupMessage", { creator: linkWrap({ @@ -81,7 +73,7 @@ export const notificationMessageFormat = (msg: MessageItem) => { }), }); case MessageType.MemberQuit: - const quitDetails = JSON.parse(msg.notificationElem.detail); + const quitDetails = JSON.parse(msg.notificationElem!.detail); const quitUser = quitDetails.quitUser; return t("messageDescription.quitGroupMessage", { name: linkWrap({ @@ -90,7 +82,7 @@ export const notificationMessageFormat = (msg: MessageItem) => { }), }); case MessageType.MemberInvited: - const inviteDetails = JSON.parse(msg.notificationElem.detail); + const inviteDetails = JSON.parse(msg.notificationElem!.detail); const inviteOpUser = inviteDetails.opUser; const invitedUserList = inviteDetails.invitedUserList ?? []; let inviteStr = ""; @@ -116,7 +108,7 @@ export const notificationMessageFormat = (msg: MessageItem) => { }`, }); case MessageType.MemberKicked: - const kickDetails = JSON.parse(msg.notificationElem.detail); + const kickDetails = JSON.parse(msg.notificationElem!.detail); const kickOpUser = kickDetails.opUser; const kickdUserList = kickDetails.kickedUserList ?? []; let kickStr = ""; @@ -136,7 +128,7 @@ export const notificationMessageFormat = (msg: MessageItem) => { kickedUser: `${kickStr}${kickdUserList.length > 3 ? "..." : ""}`, }); case MessageType.MemberEnter: - const enterDetails = JSON.parse(msg.notificationElem.detail); + const enterDetails = JSON.parse(msg.notificationElem!.detail); const enterUser = enterDetails.entrantUser; return t("messageDescription.joinGroupMessage", { name: linkWrap({ @@ -145,7 +137,7 @@ export const notificationMessageFormat = (msg: MessageItem) => { }), }); case MessageType.OANotification: - const customNoti = JSON.parse(msg.notificationElem.detail); + const customNoti = JSON.parse(msg.notificationElem!.detail); return customNoti.text; default: return ""; @@ -203,7 +195,7 @@ export const formatMessageByType = (message?: MessageItem): string => { try { switch (message.contentType) { case MessageType.TextMessage: - return message.textElem?.content; + return message.textElem?.content || ""; case MessageType.PictureMessage: return t("messageDescription.imageMessage"); case MessageType.VideoMessage: @@ -211,19 +203,19 @@ export const formatMessageByType = (message?: MessageItem): string => { case MessageType.FriendAdded: return t("messageDescription.alreadyFriendMessage"); case MessageType.MemberEnter: - const enterDetails = JSON.parse(message.notificationElem.detail); + const enterDetails = JSON.parse(message.notificationElem!.detail); const enterUser = enterDetails.entrantUser; return t("messageDescription.joinGroupMessage", { name: getName(enterUser), }); case MessageType.GroupCreated: - const groupCreatedDetail = JSON.parse(message.notificationElem.detail); + const groupCreatedDetail = JSON.parse(message.notificationElem!.detail); const groupCreatedUser = groupCreatedDetail.opUser; return t("messageDescription.createGroupMessage", { creator: getName(groupCreatedUser), }); case MessageType.MemberInvited: - const inviteDetails = JSON.parse(message.notificationElem.detail); + const inviteDetails = JSON.parse(message.notificationElem!.detail); const inviteOpUser = inviteDetails.opUser; const invitedUserList = inviteDetails.invitedUserList ?? []; let inviteStr = ""; @@ -242,7 +234,7 @@ export const formatMessageByType = (message?: MessageItem): string => { }`, }); case MessageType.MemberKicked: - const kickDetails = JSON.parse(message.notificationElem.detail); + const kickDetails = JSON.parse(message.notificationElem!.detail); const kickOpUser = kickDetails.opUser; const kickdUserList = kickDetails.kickedUserList ?? []; let kickStr = ""; @@ -259,13 +251,13 @@ export const formatMessageByType = (message?: MessageItem): string => { }`, }); case MessageType.MemberQuit: - const quitDetails = JSON.parse(message.notificationElem.detail); + const quitDetails = JSON.parse(message.notificationElem!.detail); const quitUser = quitDetails.quitUser; return t("messageDescription.quitGroupMessage", { name: getName(quitUser), }); case MessageType.OANotification: - const customNoti = JSON.parse(message.notificationElem.detail); + const customNoti = JSON.parse(message.notificationElem!.detail); return customNoti.text; default: return ""; diff --git a/tailwind.config.js b/tailwind.config.js index ce1bc3f..12d0ae9 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,6 +26,5 @@ module.exports = { }, }, }, - plugins: [require("@tailwindcss/line-clamp")], darkMode: "class", }; diff --git a/vite.config.ts b/vite.config.ts index 9ca35f3..10df8df 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,7 +6,6 @@ import electron from "vite-electron-plugin"; import { customStart, loadViteEnv } from "vite-electron-plugin/plugin"; import pkg from "./package.json"; import legacy from "@vitejs/plugin-legacy"; -import ckeditor5 from "@ckeditor/vite-plugin-ckeditor5"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); // import visualizer from "rollup-plugin-visualizer"; @@ -25,7 +24,6 @@ export default defineConfig(({ command }) => { }, plugins: [ react(), - ckeditor5({ theme: require.resolve("@ckeditor/ckeditor5-theme-lark") }), electron({ include: ["electron"], transformOptions: {