diff --git a/react/package.json b/react/package.json index 916bf8b..3a1851c 100644 --- a/react/package.json +++ b/react/package.json @@ -7,9 +7,9 @@ "start": "umi dev" }, "dependencies": { - "@xkit-yx/call-kit": "^2.0.1", - "@xkit-yx/call-kit-react-ui": "^0.4.1", - "@xkit-yx/im-kit-ui": "^9.8.0", + "@xkit-yx/call-kit": "^3.0.0", + "@xkit-yx/call-kit-react-ui": "^0.6.0", + "@xkit-yx/im-kit-ui": "^10.x", "react-dom": "^16.8.0", "umi": "^3.5.40" }, diff --git a/react/src/YXUIKit/im-kit-ui/package.json b/react/src/YXUIKit/im-kit-ui/package.json index 04a8dc9..41732ae 100644 --- a/react/src/YXUIKit/im-kit-ui/package.json +++ b/react/src/YXUIKit/im-kit-ui/package.json @@ -1,6 +1,6 @@ { "name": "@xkit-yx/im-kit-ui", - "version": "9.8.0", + "version": "10.0.1", "description": "云信即时通讯组件", "license": "MIT", "main": "lib/index.js", @@ -33,7 +33,6 @@ "gulp-typescript": "^6.0.0-alpha.1", "less": "^4.1.1", "less-plugin-npm-import": "^2.1.0", - "nim-web-sdk-ng": "0.17.0", "postcss": "^8.3.5", "postcss-nested": "^5.0.5", "react": "^16.8.0", @@ -46,8 +45,7 @@ "typescript": "^4.6.4" }, "peerDependencies": { - "@xkit-yx/core-kit": "^0.3.2", - "@xkit-yx/utils": "^0.4.3", + "@xkit-yx/im-store-v2": "^0.0.1", "antd": "^4.16.3", "mobx": "^6.6.1", "mobx-react": "^7.5.2", @@ -56,13 +54,13 @@ }, "dependencies": { "@ant-design/icons": "^5.0.1", - "@xkit-yx/core-kit": "^0.13.0", - "@xkit-yx/im-store": "^0.2.0", - "@xkit-yx/utils": "^0.5.6", + "@xkit-yx/im-store-v2": "^0.1.1", + "@xkit-yx/utils": "^0.6.0", "antd": "^4.16.3", "mobx": "^6.6.1", "mobx-react": "^7.5.2", + "nim-web-sdk-ng": "10.2.700", "react-string-replace": "^1.1.0" }, - "gitHead": "2460a56b946207f24db9a8d74c10d3776aa5f267" + "gitHead": "4e09464e7bb40f8b578b2704e7931ea1e6928417" } diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/Container.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/Container.tsx index f3cad44..638ec32 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/Container.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/Container.tsx @@ -2,24 +2,26 @@ import React, { ReactNode } from 'react' import P2pChatContainer from './containers/p2pChatContainer' import TeamChatContainer from './containers/teamChatContainer' import { useStateContext, useEventTracking, Welcome, Utils } from '../common' -import { - IMMessage, - TMsgScene, -} from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' import { RenderP2pCustomMessageOptions } from './components/ChatP2pMessageList' import { RenderTeamCustomMessageOptions } from './components/ChatTeamMessageList' import { ChatMessageInputProps } from './components/ChatMessageInput' -import { Session } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/SessionServiceInterface' import { observer } from 'mobx-react' import packageJson from '../../package.json' import { GroupItemProps } from './components/ChatTeamSetting/GroupItem' import { MenuItem } from './components/ChatMessageItem' import { SettingActionItemProps } from './components/ChatActionBar' +import { + V2NIMConversationType, + V2NIMConversation, +} from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMConversationService' +import { V2NIMMessageForUI } from '@xkit-yx/im-store-v2/dist/types/types' +import sdkPkg from 'nim-web-sdk-ng/package.json' +import { V2NIMConst } from 'nim-web-sdk-ng' export interface ActionRenderProps extends ChatMessageInputProps { - scene: TMsgScene - to: string + conversationType: V2NIMConversationType + receiverId: string } export interface Action { @@ -38,7 +40,7 @@ export interface Action { } export interface MsgOperMenuItem extends MenuItem { - onClick?: (msg: IMMessage) => void + onClick?: (msg: V2NIMMessageForUI) => void } export interface ChatSettingActionItem extends SettingActionItemProps { @@ -47,9 +49,9 @@ export interface ChatSettingActionItem extends SettingActionItemProps { export interface ChatContainerProps { /** - 自定义选中的会话 sessionId。一般不用传,内部会处理好选中逻辑 + 自定义选中的会话 conversationId。一般不用传,内部会处理好选中逻辑 */ - selectedSession?: string + selectedConversation?: string /** 消息发送按钮组配置,不传使用默认的配置 */ @@ -71,8 +73,8 @@ export interface ChatContainerProps { */ onSendText?: (data: { value: string - scene: TMsgScene - to: string + conversationType: V2NIMConversationType + receiverId: string }) => Promise /** 转让群主后的回调 @@ -97,16 +99,16 @@ export interface ChatContainerProps { /** 自定义渲染 header */ - renderHeader?: (session: Session) => JSX.Element + renderHeader?: (conversation: V2NIMConversation) => JSX.Element /** 自定义渲染 p2p 聊天输入框 placeholder */ - renderP2pInputPlaceHolder?: (session: Session) => string + renderP2pInputPlaceHolder?: (conversation: V2NIMConversation) => string /** 自定义渲染群组聊天输入框 placeholder */ renderTeamInputPlaceHolder?: (params: { - session: Session + conversation: V2NIMConversation mute: boolean }) => string /** @@ -118,19 +120,25 @@ export interface ChatContainerProps { /** 自定义渲染消息头像 */ - renderMessageAvatar?: (msg: IMMessage) => JSX.Element | null | undefined + renderMessageAvatar?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined /** 自定义渲染消息昵称 */ - renderMessageName?: (msg: IMMessage) => JSX.Element | null | undefined + renderMessageName?: (msg: V2NIMMessageForUI) => JSX.Element | null | undefined /** 自定义渲染消息内容,气泡样式也需要自定义 */ - renderMessageOuterContent?: (msg: IMMessage) => JSX.Element | null | undefined + renderMessageOuterContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined /** 自定义渲染消息内容,气泡样式不需要自定义 */ - renderMessageInnerContent?: (msg: IMMessage) => JSX.Element | null | undefined + renderMessageInnerContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined /** 样式前缀 @@ -144,7 +152,7 @@ export interface ChatContainerProps { export const ChatContainer: React.FC = observer( ({ - selectedSession, + selectedConversation, actions, p2pSettingActions, teamSettingActions, @@ -166,29 +174,35 @@ export const ChatContainer: React.FC = observer( prefix = 'chat', commonPrefix = 'common', }) => { - const { store, nim, initOptions } = useStateContext() + const { store, nim } = useStateContext() - const finalSelectedSession = - selectedSession || store.uiStore.selectedSession || '' + const finalSelectedConversation = + selectedConversation || store.uiStore.selectedConversation || '' - const { scene, to } = Utils.parseSessionId(finalSelectedSession) + const receiverId = nim.V2NIMConversationIdUtil.parseConversationTargetId( + finalSelectedConversation + ) + const conversationType = nim.V2NIMConversationIdUtil.parseConversationType( + finalSelectedConversation + ) useEventTracking({ - appkey: initOptions.appkey, + appkey: nim.options.appkey, version: packageJson.version, component: 'ChatUIKit', - imVersion: nim.version, + imVersion: sdkPkg.version, }) - return finalSelectedSession ? ( - scene === 'p2p' ? ( + return finalSelectedConversation ? ( + conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P ? ( = observer( renderMessageInnerContent={renderMessageInnerContent} renderMessageOuterContent={renderMessageOuterContent} /> - ) : scene === 'team' ? ( + ) : conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM ? ( = ({
- setSelectedAccounts(selectedList.map((item) => item.account)) - } + onSelect={(selectedList) => setSelectedAccounts(selectedList)} selectedAccounts={selectedAccounts} />
diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCreateTeam/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCreateTeam/index.tsx index 93dd277..00f6b95 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCreateTeam/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCreateTeam/index.tsx @@ -122,9 +122,7 @@ const GroupCreate: React.FC = ({
- setSelectedAccounts(accounts.map((item) => item.account)) - } + onSelect={(accounts) => setSelectedAccounts(accounts)} selectedAccounts={selectedAccounts} />
diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatForwardModal/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatForwardModal/index.tsx index 2152d1b..7f9b45c 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatForwardModal/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatForwardModal/index.tsx @@ -7,15 +7,12 @@ import { CrudeAvatar, SelectModal, } from '../../../common' -import { - IMMessage, - TMsgScene, -} from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' -import { parseSessionId } from '../../../utils' import { SelectModalItemProps } from '../../../common/components/SelectModal' +import { V2NIMMessageForUI } from '@xkit-yx/im-store-v2/dist/types/types' +import { V2NIMConst } from 'nim-web-sdk-ng' export interface ChatForwardModalProps { - msg: IMMessage + msg?: V2NIMMessageForUI visible: boolean onSend: () => void onCancel: () => void @@ -24,11 +21,6 @@ export interface ChatForwardModalProps { commonPrefix?: string } -export interface ChatForwardModalItemProps { - scene: TMsgScene - to: string -} - const ChatForwardModal: React.FC = ({ msg, visible, @@ -38,7 +30,7 @@ const ChatForwardModal: React.FC = ({ commonPrefix = 'common', }) => { const { t } = useTranslation() - const { store } = useStateContext() + const { nim, store } = useStateContext() const [comment, setComment] = useState('') const [isSearching, setIsSearching] = useState(false) @@ -52,29 +44,44 @@ const ChatForwardModal: React.FC = ({ const datasource: SelectModalItemProps[] = useMemo(() => { if (isSearching) { const friends = store.uiStore.friends - .filter((item) => !store.relationStore.blacklist.includes(item.account)) + .filter( + (item) => !store.relationStore.blacklist.includes(item.accountId) + ) .map((item) => ({ - key: 'p2p-' + item.account, - label: store.uiStore.getAppellation({ account: item.account }), + key: nim.V2NIMConversationIdUtil.p2pConversationId(item.accountId), + label: store.uiStore.getAppellation({ account: item.accountId }), })) const teams = store.uiStore.teamList.map((item) => ({ - key: 'team-' + item.teamId, + key: nim.V2NIMConversationIdUtil.teamConversationId(item.teamId), label: item.name || item.teamId, })) return [...friends, ...teams] } - const res = [...store.sessionStore.sessions.values()] + const res = [...store.conversationStore.conversations.values()] .map((item) => { - if (item.scene === 'p2p') { + if ( + item.type === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P + ) { return { - key: item.id, - label: store.uiStore.getAppellation({ account: item.to }), + key: item.conversationId, + label: store.uiStore.getAppellation({ + account: nim.V2NIMConversationIdUtil.parseConversationTargetId( + item.conversationId + ), + }), } } - if (item.scene === 'team') { - const team = store.teamStore.teams.get(item.to) + if ( + item.type === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + ) { + const teamId = nim.V2NIMConversationIdUtil.parseConversationTargetId( + item.conversationId + ) + const team = store.teamStore.teams.get(teamId) return { - key: item.id, + key: item.conversationId, label: team?.name || team?.teamId || '', } } @@ -84,16 +91,23 @@ const ChatForwardModal: React.FC = ({ return res }, [ + nim.V2NIMConversationIdUtil, isSearching, - store.sessionStore.sessions, + store.conversationStore.conversations, store.teamStore.teams, store.relationStore.blacklist, store.uiStore, ]) const itemAvatarRender = (data: SelectModalItemProps) => { - const { scene, to } = parseSessionId(data.key) - if (scene === 'p2p') { + const to = nim.V2NIMConversationIdUtil.parseConversationTargetId(data.key) + const conversationType = nim.V2NIMConversationIdUtil.parseConversationType( + data.key + ) + if ( + conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P + ) { return ( = ({ /> ) } - if (scene === 'team') { + if ( + conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + ) { const team = store.teamStore.teams.get(to) return ( = ({ } const handleOk = async (data: SelectModalItemProps[]) => { - const { scene, to } = parseSessionId(data[0].key) - if (scene && to) { + if (data[0].key && msg) { try { - await store.msgStore.forwardMsgActive( - { - msg, - scene: scene as TMsgScene, - to, - }, - comment - ) + await store.msgStore.forwardMsgActive(msg, data[0].key, comment) onSend() } catch (error) { message.error(t('forwardFailedText')) diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatGroupTransferModal/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatGroupTransferModal/index.tsx index 485ca57..39aa115 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatGroupTransferModal/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatGroupTransferModal/index.tsx @@ -5,13 +5,13 @@ import { useStateContext, useTranslation, } from '../../../common' -import { TeamMember } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' -import { FriendProfile } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/FriendServiceInterface' import { SelectModal } from '../../../common' import { SelectModalItemProps } from '../../../common/components/SelectModal' +import { V2NIMTeamMember } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' +import { V2NIMConst } from 'nim-web-sdk-ng' interface GroupActionModalProps { visible: boolean - members: (TeamMember & Partial)[] // 成员列表 + members: V2NIMTeamMember[] // 成员列表 onOk: () => void // 确认操作的回调函数 onCancel: () => void commonPrefix?: string @@ -48,7 +48,7 @@ const GroupTransferModal: React.FC = ({ } catch (error: any) { switch (error?.code) { // 无权限 - case 802: + case 109427: message.error(t('noPermission')) break default: @@ -66,16 +66,18 @@ const GroupTransferModal: React.FC = ({ const _showMembers = members.map((item) => { return { ...item, - key: item.account, - disabled: item.type === 'owner', + key: item.accountId, + disabled: + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER, label: store.uiStore.getAppellation({ - account: item.account, + account: item.accountId, teamId: item.teamId, }), } }) return _showMembers - }, [[members]]) + }, [members, store.uiStore]) const itemAvatarRender = (data: SelectModalItemProps) => { return ( diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/ChatMentionMemberList.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/ChatMentionMemberList.tsx index 1b48ef6..5fb9188 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/ChatMentionMemberList.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/ChatMentionMemberList.tsx @@ -5,10 +5,10 @@ import { useStateContext, useTranslation, } from '../../../common' -import { TeamMember } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' import classNames from 'classnames' import { observer } from 'mobx-react' -import { storeConstants } from '@xkit-yx/im-store' +import { storeConstants } from '@xkit-yx/im-store-v2' +import { V2NIMTeamMember } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' export type MentionedMember = { account: string; appellation: string } @@ -16,7 +16,7 @@ export interface ChatMentionMemberList { allowAtAll?: boolean prefix?: string commonPrefix?: string - mentionMembers?: TeamMember[] + mentionMembers?: V2NIMTeamMember[] onSelect?: (member: MentionedMember) => void } @@ -61,9 +61,9 @@ export const ChatAtMemberList: React.FC = observer( } else { const member = mentionMembers[activeIndex] onSelect?.({ - account: member.account, + account: member.accountId, appellation: store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, ignoreAlias: true, }), @@ -104,12 +104,12 @@ export const ChatAtMemberList: React.FC = observer( className={classNames(`${_prefix}-item`, { [`${_prefix}-item-active`]: index === activeIndex, })} - key={member.account} + key={member.accountId} onClick={() => { onSelect?.({ - account: member.account, + account: member.accountId, appellation: store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, ignoreAlias: true, }), @@ -121,11 +121,11 @@ export const ChatAtMemberList: React.FC = observer( prefix={commonPrefix} canClick={false} size={28} - account={member.account} + account={member.accountId} /> {store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, })} diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/index.tsx index 5ee4692..c0bd0fc 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageInput/index.tsx @@ -14,11 +14,10 @@ import { Popover, message, Button, - Spin, Dropdown, Menu, + Spin, } from 'antd' -import { IMMessage } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' import { CommonIcon, getMsgContentTipByType, @@ -28,14 +27,16 @@ import { } from '../../../common' import { Action } from '../../Container' import { MAX_UPLOAD_FILE_SIZE } from '../../../constant' -import { storeConstants } from '@xkit-yx/im-store' +import { storeConstants } from '@xkit-yx/im-store-v2' import { LoadingOutlined, CloseOutlined } from '@ant-design/icons' -import { TMsgScene } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' import { observer } from 'mobx-react' import { TextAreaRef } from 'antd/lib/input/TextArea' -import { TeamMember } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' import ChatAtMemberList, { MentionedMember } from './ChatMentionMemberList' import { mergeActions } from '../../../utils' +import { V2NIMMessageForUI } from '@xkit-yx/im-store-v2/dist/types/types' +import { V2NIMTeamMember } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' +import { V2NIMConversationType } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMConversationService' +import { V2NIMConst } from 'nim-web-sdk-ng' const { TextArea } = Input @@ -43,16 +44,14 @@ export interface ChatMessageInputProps { prefix?: string commonPrefix?: string placeholder?: string - replyMsg?: IMMessage - mentionMembers?: TeamMember[] - scene: TMsgScene - to: string + replyMsg?: V2NIMMessageForUI + mentionMembers?: V2NIMTeamMember[] + conversationType: V2NIMConversationType + receiverId: string actions?: Action[] mute?: boolean allowAtAll?: boolean inputValue?: string - uploadImageLoading?: boolean - uploadFileLoading?: boolean setInputValue: (value: string) => void onSendText: (value: string, ext?: Record) => void onSendFile: (file: File) => void @@ -78,13 +77,11 @@ const ChatMessageInput = observer( placeholder = '', mentionMembers, actions, - scene, - to, + conversationType, + receiverId, mute = false, allowAtAll = true, inputValue = '', - uploadImageLoading = false, - uploadFileLoading = false, replyMsg, setInputValue, onSendText, @@ -207,22 +204,18 @@ const ChatMessageInput = observer( render: () => { return ( ) }, @@ -262,7 +255,7 @@ const ChatMessageInput = observer( const res = mentionMembers?.filter((member) => { return store.uiStore .getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, }) ?.includes(atMemberSearchText.replace('@', '')) @@ -276,7 +269,7 @@ const ChatMessageInput = observer( useEffect(() => { setAtMemberSearchText('') setAtVisible(false) - }, [to]) + }, [receiverId]) useEffect(() => { if (atMemberSearchText) { @@ -594,8 +587,12 @@ const ChatMessageInput = observer( const replyMsgContent = () => { if (replyMsg) { const nick = store.uiStore.getAppellation({ - account: replyMsg.from, - teamId: replyMsg.scene === 'team' ? replyMsg.to : undefined, + account: replyMsg.senderId, + teamId: + replyMsg.conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + ? replyMsg.receiverId + : undefined, ignoreAlias: true, }) let content = `${t('replyText')} ${nick}:` diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageItem/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageItem/index.tsx index 96f20bd..46cdd90 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageItem/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatMessageItem/index.tsx @@ -1,10 +1,6 @@ import React, { Fragment, useRef } from 'react' import { Dropdown, Menu, Tooltip } from 'antd' -import { - LoadingOutlined, - CheckCircleOutlined, - ExclamationCircleFilled, -} from '@ant-design/icons' +import { LoadingOutlined, ExclamationCircleFilled } from '@ant-design/icons' import classNames from 'classnames' import moment from 'moment' import { @@ -16,10 +12,11 @@ import { useStateContext, } from '../../../common' import { RollbackOutlined, DeleteOutlined } from '@ant-design/icons' -import { IMMessage } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' import { observer } from 'mobx-react' import { MsgOperMenuItem } from '../../Container' import { mergeActions } from '../../../utils' +import { V2NIMMessageForUI } from '@xkit-yx/im-store-v2/dist/types/types' +import { V2NIMConst } from 'nim-web-sdk-ng' export type MenuItemKey = 'recall' | 'delete' | 'reply' | 'forward' | string export type AvatarMenuItem = 'mention' @@ -36,21 +33,24 @@ export interface MenuItem { } export interface MessageItemProps { - myAccount: string - msg: IMMessage - replyMsg?: IMMessage + msg: V2NIMMessageForUI + replyMsg?: V2NIMMessageForUI normalStatusRenderer?: React.ReactNode msgOperMenu?: MsgOperMenuItem[] - onSendImg: (file: File, randomId?: string) => Promise - onSendVideo: (file: File, randomId?: string) => Promise - onResend: (msg: IMMessage) => void - onReeditClick: (msg: IMMessage) => void - onMessageAction: (key: MenuItemKey, msg: IMMessage) => void - onMessageAvatarAction?: (key: AvatarMenuItem, msg: IMMessage) => void - renderMessageAvatar?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageName?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageOuterContent?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageInnerContent?: (msg: IMMessage) => JSX.Element | null | undefined + onReeditClick: (msg: V2NIMMessageForUI) => void + onResend: (msg: V2NIMMessageForUI) => void + onMessageAction: (key: MenuItemKey, msg: V2NIMMessageForUI) => void + onMessageAvatarAction?: (key: AvatarMenuItem, msg: V2NIMMessageForUI) => void + renderMessageAvatar?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined + renderMessageName?: (msg: V2NIMMessageForUI) => JSX.Element | null | undefined + renderMessageOuterContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined + renderMessageInnerContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined prefix?: string commonPrefix?: string } @@ -59,15 +59,12 @@ export const ChatMessageItem: React.FC = observer( ({ msg, replyMsg, - myAccount, normalStatusRenderer, msgOperMenu, - onResend, - onSendImg, - onSendVideo, onMessageAction, onMessageAvatarAction, onReeditClick, + onResend, renderMessageAvatar, renderMessageName, renderMessageOuterContent, @@ -81,74 +78,68 @@ export const ChatMessageItem: React.FC = observer( const _prefix = `${prefix}-message-list-item` const { - from, - // fromNick, - body, - attach, - idClient, - status, - // @ts-ignore + text, + senderId, + receiverId, + messageClientId, + sendingState, + uploadProgress, - // @ts-ignore - uploadFileInfo, - time, - type, - scene, - to, + createTime, + messageType, + conversationType, + isSelf, + recallType = '', + canRecall = false, + canEdit = false, + errorCode, } = msg const messageActionDropdownContainerRef = useRef(null) const messageAvatarActionDropdownContainerRef = useRef(null) - const isSelf = from === myAccount - const nick = store.uiStore.getAppellation({ - account: from, - teamId: scene === 'team' ? to : undefined, + account: senderId, + teamId: + conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + ? receiverId + : undefined, }) const nickWithoutAlias = store.uiStore.getAppellation({ - account: from, - teamId: scene === 'team' ? to : undefined, + account: senderId, + teamId: + conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + ? receiverId + : undefined, ignoreAlias: true, }) - // 内存中插入的 msg 属性,具体内容参考 msg store - const { - type: attachType = '', - canRecall = false, - canEdit = false, - oldBody = '', - } = attach || { type: '', canRecall: false, canEdit: false, oldBody: '' } - const handleResendMsg = () => { - // 如果是上传过程中失败的图片和视频消息,则重新发送 - if (uploadFileInfo && ['image', 'video'].includes(msg.type)) { - switch (msg.type) { - case 'image': - onSendImg(uploadFileInfo.file, msg.idClient) - break - case 'video': - onSendVideo(uploadFileInfo.file, msg.idClient) - break - default: - break - } - } else { - onResend(msg) - } + onResend(msg) } const renderSendStatus = () => { - if (status === 'sending') { + if ( + sendingState === + V2NIMConst.V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SENDING + ) { return } - if (status === 'read') { - return - } - if (status === 'sendFailed') { + if ( + sendingState === + V2NIMConst.V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_FAILED + ) { + const title = + errorCode === 102426 + ? t('sendBlackFailedText') + : errorCode === 104404 + ? t('sendNotFriendFailedText') + : t('sendMsgFailedText') return ( - + = observer( ) } - if (status === 'refused') { - return ( - - onResend(msg)} - /> - - ) - } return normalStatusRenderer || null } const renderMsgDate = () => { - const date = moment(time) + const date = moment(createTime) const isCurrentDay = date.isSame(moment(), 'day') const isCurrentYear = date.isSame(moment(), 'year') return isCurrentDay @@ -188,7 +169,12 @@ export const ChatMessageItem: React.FC = observer( // icon: , // }, { - show: ['sending', 'sendFailed', 'refused', 'delete'].includes(status) + show: [ + V2NIMConst.V2NIMMessageSendingState + .V2NIM_MESSAGE_SENDING_STATE_SENDING, + V2NIMConst.V2NIMMessageSendingState + .V2NIM_MESSAGE_SENDING_STATE_FAILED, + ].includes(sendingState) ? 0 : 1, label: t('replyText'), @@ -203,8 +189,13 @@ export const ChatMessageItem: React.FC = observer( }, { show: - ['sending', 'sendFailed', 'refused', 'delete'].includes(status) || - type === 'audio' + [ + V2NIMConst.V2NIMMessageSendingState + .V2NIM_MESSAGE_SENDING_STATE_SENDING, + V2NIMConst.V2NIMMessageSendingState + .V2NIM_MESSAGE_SENDING_STATE_FAILED, + ].includes(sendingState) || + messageType === V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO ? 0 : 1, label: t('forwardText'), @@ -236,8 +227,8 @@ export const ChatMessageItem: React.FC = observer( const renderSpecialMsg = () => { return ( -
- {attachType === 'reCallMsg' ? ( +
+ {recallType === 'reCallMsg' ? ( <> {`${t('you')}${t('recallMessageText')}`} {canEdit ? ( @@ -256,9 +247,10 @@ export const ChatMessageItem: React.FC = observer( ) } - return attachType === 'reCallMsg' || attachType === 'beReCallMsg' ? ( + return recallType === 'reCallMsg' || recallType === 'beReCallMsg' ? ( renderSpecialMsg() - ) : type === 'notification' ? ( + ) : messageType === + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION ? ( ) : (
= observer( ) : ( = observer( >
@@ -305,7 +297,7 @@ export const ChatMessageItem: React.FC = observer( )} { - member: NimKitCoreTypes.IFriendInfo +export interface RenderP2pCustomMessageOptions extends MessageItemProps { + receiverId: string } export interface ChatP2pMessageListProps extends Omit { - msgs: IMMessage[] - replyMsgsMap: Record - member: NimKitCoreTypes.IFriendInfo + msgs: V2NIMMessageForUI[] + replyMsgsMap: Record + receiverId: string renderP2pCustomMessage?: ( options: RenderP2pCustomMessageOptions ) => JSX.Element | null | undefined @@ -39,17 +37,14 @@ const ChatP2pMessageList = observer( commonPrefix = 'common', msgs, replyMsgsMap, - member, + receiverId, receiveMsgBtnVisible = false, msgReceiptTime = 0, msgOperMenu, onReceiveMsgBtnClick, loadingMore, noMore, - myAccount, onResend, - onSendImg, - onSendVideo, onMessageAction, onReeditClick, onScroll, @@ -78,34 +73,29 @@ const ChatP2pMessageList = observer( {renderMsgs.map((msg) => { const msgItem = renderP2pCustomMessage?.({ msg, - replyMsg: replyMsgsMap[msg.idClient], - member, + replyMsg: replyMsgsMap[msg.messageClientId], + receiverId, onResend, - onSendImg, - onSendVideo, onReeditClick, onMessageAction, }) ?? ( ) : null } - myAccount={myAccount} onResend={onResend} - onSendImg={onSendImg} - onSendVideo={onSendVideo} onMessageAction={onMessageAction} onReeditClick={onReeditClick} renderMessageAvatar={renderMessageAvatar} @@ -115,7 +105,7 @@ const ChatP2pMessageList = observer( /> ) return ( -
+
{msgItem}
) @@ -130,13 +120,13 @@ const ChatP2pMessageList = observer(
) : null} - {store.uiStore.getRelation(member.account).relation === 'stranger' ? ( + {store.uiStore.getRelation(receiverId).relation === 'stranger' ? ( ) : null} diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMemberModal/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMemberModal/index.tsx index f1f5d75..e031974 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMemberModal/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMemberModal/index.tsx @@ -8,6 +8,7 @@ import { } from '../../../common' import { observer } from 'mobx-react' import { SelectModalItemProps } from '../../../common/components/SelectModal' +import { V2NIMConst } from 'nim-web-sdk-ng' export interface ChatTeamMemberModalProps { visible: boolean @@ -31,19 +32,23 @@ const ChatTeamMemberModal: React.FC = observer( const teamMembers = store.teamMemberStore .getTeamMember(teamId) - .filter((item) => item.account !== store.userStore.myUserInfo.account) + .filter((item) => item.accountId !== store.userStore.myUserInfo.accountId) const datasource = teamMembers.map((item) => ({ - key: item.account, + key: item.accountId, label: store.uiStore.getAppellation({ - account: item.account, + account: item.accountId, teamId: item.teamId, }), })) const teamManagerAccounts = teamMembers - .filter((item) => item.type === 'manager') - .map((item) => item.account) + .filter( + (item) => + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER + ) + .map((item) => item.accountId) const itemAvatarRender = (data: SelectModalItemProps) => { return ( @@ -65,25 +70,27 @@ const ChatTeamMemberModal: React.FC = observer( data.every((j) => j.key !== i) ) add.length && - (await store.teamStore.addTeamManagersActive({ + (await store.teamStore.updateTeamMemberRoleActive({ teamId, accounts: add, + role: V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER, })) remove.length && - (await store.teamStore.removeTeamManagersActive({ + (await store.teamStore.updateTeamMemberRoleActive({ teamId, accounts: remove, + role: V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_NORMAL, })) message.success(t('updateTeamManagerSuccessText')) onCancel() } catch (error: any) { switch (error?.code) { // 操作的人不在群中 - case 804: + case 191004: message.error(t('userNotInTeam')) break // 没权限 - case 802: + case 109432: message.error(t('noPermission')) break default: diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMessageList/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMessageList/index.tsx index 73dc824..bd75074 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMessageList/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamMessageList/index.tsx @@ -1,24 +1,23 @@ import React, { forwardRef } from 'react' -import { IMMessage } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' -import { TeamMember } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' import MessageListItem, { MessageItemProps } from '../ChatMessageItem' import { Alert, Spin } from 'antd' import { ArrowDownOutlined } from '@ant-design/icons' import { ReadPercent, useStateContext, useTranslation } from '../../../common' -import { storeUtils } from '@xkit-yx/im-store' +import { storeUtils } from '@xkit-yx/im-store-v2' import { MsgOperMenuItem } from '../../Container' +import { V2NIMTeamMember } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' +import { V2NIMMessageForUI } from '@xkit-yx/im-store-v2/dist/types/types' -export interface RenderTeamCustomMessageOptions - extends Omit { - members: TeamMember[] +export interface RenderTeamCustomMessageOptions extends MessageItemProps { + members: V2NIMTeamMember[] } export interface ChatTeamMessageListProps extends Omit { - msgs: IMMessage[] + msgs: V2NIMMessageForUI[] msgOperMenu?: MsgOperMenuItem[] - replyMsgsMap: Record - members: TeamMember[] + replyMsgsMap: Record + members: V2NIMTeamMember[] renderTeamCustomMessage?: ( options: RenderTeamCustomMessageOptions ) => JSX.Element | null | undefined @@ -49,10 +48,7 @@ const ChatTeamMessageList = forwardRef< onReceiveMsgBtnClick, loadingMore, noMore, - myAccount, onResend, - onSendImg, - onSendVideo, onMessageAction, onMessageAvatarAction, onReeditClick, @@ -82,35 +78,30 @@ const ChatTeamMessageList = forwardRef< {renderMsgs.map((msg) => { const msgItem = renderTeamCustomMessage?.({ msg, - replyMsg: replyMsgsMap[msg.idClient], + replyMsg: replyMsgsMap[msg.messageClientId], members, onResend, - onSendImg, - onSendVideo, onReeditClick, onMessageAction, }) ?? ( ) : null } - myAccount={myAccount} onResend={onResend} - onSendImg={onSendImg} - onSendVideo={onSendVideo} onMessageAction={onMessageAction} onMessageAvatarAction={onMessageAvatarAction} onReeditClick={onReeditClick} @@ -121,7 +112,7 @@ const ChatTeamMessageList = forwardRef< /> ) return ( -
+
{msgItem}
) diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupDetail.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupDetail.tsx index 4201167..2091c71 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupDetail.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupDetail.tsx @@ -1,12 +1,15 @@ import React, { FC, useEffect, useState } from 'react' import { Form, Input, Button } from 'antd' import { GroupAvatarSelect, useTranslation, CrudeAvatar } from '../../../common' -import { Team } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' +import { + V2NIMTeam, + V2NIMUpdatedTeamInfo, +} from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' export interface GroupDetailmProps { - team: Team + team: V2NIMTeam hasPower: boolean - onUpdateTeamInfo: (team: Partial) => void + onUpdateTeamInfo: (team: V2NIMUpdatedTeamInfo) => void prefix?: string commonPrefix?: string @@ -43,13 +46,13 @@ const GroupDetail: FC = ({ }, [team.name]) const onUpdateTeamInfoSubmitHandler = () => { - const obj: Partial = { avatar, name, intro } + const obj: V2NIMUpdatedTeamInfo = { avatar, name, intro } Object.keys(obj).forEach((key) => { if (obj[key] === team[key]) { delete obj[key] } }) - onUpdateTeamInfo({ ...obj, teamId: team.teamId }) + onUpdateTeamInfo(obj) } return ( diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupItem.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupItem.tsx index 380d50b..618583c 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupItem.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupItem.tsx @@ -6,13 +6,14 @@ import { useTranslation, useStateContext, } from '../../../common' -import { TeamMember } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' +import { V2NIMTeamMember } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' import { observer } from 'mobx-react' +import { V2NIMConst } from 'nim-web-sdk-ng' export interface GroupItemProps { - myMemberInfo: TeamMember - member: TeamMember - onRemoveTeamMemberClick: (member: TeamMember) => void + myMemberInfo: V2NIMTeamMember + member: V2NIMTeamMember + onRemoveTeamMemberClick: (member: V2NIMTeamMember) => void afterSendMsgClick?: () => void prefix?: string @@ -39,7 +40,7 @@ export const GroupItem: FC = observer( const [isActive, setIsActive] = useState(false) - const isSelf = member.account === myMemberInfo.account + const isSelf = member.accountId === myMemberInfo.accountId const renderRemoveBtn = () => { return ( @@ -64,21 +65,34 @@ export const GroupItem: FC = observer( } const renderButton = () => { - if (member.type === 'owner') { + if ( + member.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER + ) { return {t('teamOwnerText')} } - if (member.type === 'manager') { - return myMemberInfo.type === 'owner' && isActive ? ( + if ( + member.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER + ) { + return myMemberInfo.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER && + isActive ? ( renderRemoveBtn() ) : ( {t('teamManagerText')} ) } - if (member.type === 'normal') { - return (myMemberInfo.type === 'owner' || - myMemberInfo.type === 'manager') && + if ( + member.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_NORMAL + ) { + return (myMemberInfo.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER || + myMemberInfo.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER) && isActive && !isSelf ? renderRemoveBtn() @@ -104,11 +118,11 @@ export const GroupItem: FC = observer( prefix={commonPrefix} afterSendMsgClick={afterSendMsgClick} canClick={!isSelf} - account={member.account} + account={member.accountId} /> {store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, })} diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupList.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupList.tsx index e385c51..9c6891b 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupList.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupList.tsx @@ -1,14 +1,14 @@ import React, { FC, useMemo, useState } from 'react' import { GroupItem, GroupItemProps } from './GroupItem' -import { TeamMember } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' +import { V2NIMTeamMember } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' import { Input } from 'antd' import { SearchOutlined } from '@ant-design/icons' import { useStateContext, useTranslation } from '../../../common' export interface GroupListProps { - myMemberInfo: TeamMember - members: TeamMember[] - onRemoveTeamMemberClick: (member: TeamMember) => void + myMemberInfo: V2NIMTeamMember + members: V2NIMTeamMember[] + onRemoveTeamMemberClick: (member: V2NIMTeamMember) => void afterSendMsgClick?: () => void renderTeamMemberItem?: ( params: GroupItemProps @@ -39,12 +39,12 @@ const GroupList: FC = ({ if (groupSearchText) { _sortedMembers = members.filter((item) => store.uiStore - .getAppellation({ account: item.account, teamId: item.teamId }) + .getAppellation({ account: item.accountId, teamId: item.teamId }) .includes(groupSearchText) ) } return _sortedMembers - }, [members, groupSearchText]) + }, [members, groupSearchText, store.uiStore]) return (
@@ -68,7 +68,7 @@ const GroupList: FC = ({ } return ( renderTeamMemberItem?.(itemProps) ?? ( - + ) ) }) diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupPower.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupPower.tsx index 6d97c19..f119396 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupPower.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/GroupPower.tsx @@ -6,17 +6,19 @@ import { useTranslation, } from '../../../common' import { - Team, - TeamMember, -} from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' + V2NIMTeam, + V2NIMTeamMember, + V2NIMUpdatedTeamInfo, +} from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' import ChatTeamMemberModal from '../ChatTeamMemberModal' import { ALLOW_AT, TAllowAt } from '../../../constant' +import { V2NIMConst } from 'nim-web-sdk-ng' export interface GroupPowerProps { - onUpdateTeamInfo: (team: Partial) => void + onUpdateTeamInfo: (team: V2NIMUpdatedTeamInfo) => void onTeamMuteChange: (mute: boolean) => void - team: Team - managers: TeamMember[] + team: V2NIMTeam + managers: V2NIMTeamMember[] isGroupOwner: boolean afterSendMsgClick?: () => void @@ -59,12 +61,12 @@ const GroupPower: React.FC = ({ const ext: TAllowAt = useMemo(() => { let res = {} try { - res = JSON.parse(team.ext || '{}') + res = JSON.parse(team.serverExtension || '{}') } catch (error) { // } return res - }, [team.ext]) + }, [team.serverExtension]) return (
@@ -94,8 +96,8 @@ const GroupPower: React.FC = ({ ) : ( managers.map((item) => ( @@ -110,9 +112,22 @@ const GroupPower: React.FC = ({
@@ -121,9 +136,20 @@ const GroupPower: React.FC = ({
@@ -135,7 +161,7 @@ const GroupPower: React.FC = ({ value={ext[ALLOW_AT] || 'all'} onChange={(value) => { onUpdateTeamInfo({ - ext: JSON.stringify({ ...ext, [ALLOW_AT]: value }), + serverExtension: JSON.stringify({ ...ext, [ALLOW_AT]: value }), }) }} > @@ -144,7 +170,14 @@ const GroupPower: React.FC = ({
- +
void onAddMembersClick: () => void onTransferTeamClick: () => void - onRemoveTeamMemberClick: (member: TeamMember) => void - onUpdateTeamInfo: (team: Partial) => void - onUpdateMyMemberInfo: (params: UpdateMyMemberInfoOptions) => void + onRemoveTeamMemberClick: (member: V2NIMTeamMember) => void + onUpdateTeamInfo: (team: V2NIMUpdatedTeamInfo) => void + onUpdateMyMemberInfo: (params: V2NIMUpdateSelfMemberInfoParams) => void onTeamMuteChange: (mute: boolean) => void afterSendMsgClick?: () => void setNavHistoryStack: (stack: HistoryStack[]) => void @@ -107,8 +109,7 @@ const ChatTeamSetting: FC = ({ const handleUpdateMyMemberInfo = (e: React.FocusEvent) => { onUpdateMyMemberInfo({ - teamId: team.teamId, - nickInTeam, + teamNick: nickInTeam, }) } @@ -141,25 +142,38 @@ const ChatTeamSetting: FC = ({ const isOwnerOrManager = isGroupOwner || isGroupManager const hasUpdateTeamPower = useMemo(() => { - if (team.updateTeamMode === 'manager' && isOwnerOrManager) { + if ( + team.updateInfoMode === + V2NIMConst.V2NIMTeamUpdateInfoMode + .V2NIM_TEAM_UPDATE_INFO_MODE_MANAGER && + isOwnerOrManager + ) { return true } - return team.updateTeamMode === 'all' - }, [team.updateTeamMode, isOwnerOrManager]) + return ( + team.updateInfoMode === + V2NIMConst.V2NIMTeamUpdateInfoMode.V2NIM_TEAM_UPDATE_INFO_MODE_ALL + ) + }, [team.updateInfoMode, isOwnerOrManager]) const myMemberInfo = useMemo(() => { return ( - members.find((item) => item.account === myAccount) || ({} as TeamMember) + members.find((item) => item.accountId === myAccount) || + ({} as V2NIMTeamMember) ) }, [myAccount, members]) const teamManagers = useMemo(() => { - return members.filter((item) => item.type === 'manager') + return members.filter( + (item) => + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER + ) }, [members]) useEffect(() => { - setNickInTeam(myMemberInfo.nickInTeam || '') - }, [myMemberInfo.nickInTeam]) + setNickInTeam(myMemberInfo.teamNick || '') + }, [myMemberInfo.teamNick]) useEffect(() => { if (!navHistoryStack.length) { @@ -210,9 +224,9 @@ const ChatTeamSetting: FC = ({ {members.slice(0, 6).map((item) => { return ( ) @@ -231,7 +245,8 @@ const ChatTeamSetting: FC = ({ placeholder={t('editNickInTeamText')} />
- {team.type !== 'normal' && isOwnerOrManager ? ( + {team.teamType !== V2NIMConst.V2NIMTeamType.V2NIM_TEAM_TYPE_INVALID && + isOwnerOrManager ? (
Promise renderP2pCustomMessage?: ( options: RenderP2pCustomMessageOptions ) => JSX.Element | null | undefined - renderHeader?: (session: Session) => JSX.Element - renderP2pInputPlaceHolder?: (session: Session) => string - renderMessageAvatar?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageName?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageOuterContent?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageInnerContent?: (msg: IMMessage) => JSX.Element | null | undefined + renderHeader?: (conversation: V2NIMConversation) => JSX.Element + renderP2pInputPlaceHolder?: (conversation: V2NIMConversation) => string + renderMessageAvatar?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined + renderMessageName?: (msg: V2NIMMessageForUI) => JSX.Element | null | undefined + renderMessageOuterContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined + renderMessageInnerContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined prefix?: string commonPrefix?: string @@ -65,8 +72,8 @@ export interface P2pChatContainerProps { const P2pChatContainer: React.FC = observer( ({ - scene, - to, + conversationType, + receiverId, settingActions, actions, msgOperMenu, @@ -86,26 +93,30 @@ const P2pChatContainer: React.FC = observer( const { t } = useTranslation() - const sessionId = `${scene}-${to}` + const conversationId = + nim.V2NIMConversationIdUtil.p2pConversationId(receiverId) - const session = store.sessionStore.sessions.get(sessionId) + const conversation = + store.conversationStore.conversations.get(conversationId) - const msgs = store.msgStore.getMsg(sessionId) + const msgs = store.msgStore.getMsg(conversationId) // 当前输入框的回复消息 - const replyMsg = store.msgStore.replyMsgs.get(sessionId) + const replyMsg = store.msgStore.replyMsgs.get(conversationId) - const user = store.uiStore.getFriendWithUserNameCard(to) + const user = store.uiStore.getFriendWithUserNameCard(receiverId) const myUser = store.userStore.myUserInfo const userNickOrAccount = store.uiStore.getAppellation({ - account: user.account, + account: user.accountId, }) - const isOnline = store.eventStore.stateMap.get(to) === 'online' + // TODO sdk 暂不支持用户在线状态 + // const isOnline = store.eventStore.stateMap.get(receiverId) === 'online' + const isOnline = 'online' - const createDefaultAccounts = useMemo(() => [to], [to]) + const createDefaultAccounts = useMemo(() => [receiverId], [receiverId]) const messageListContainerDomRef = useRef(null) const settingDrawDomRef = useRef(null) @@ -115,12 +126,12 @@ const P2pChatContainer: React.FC = observer( return new VisibilityObserver({ root: messageListContainerDomRef.current, }) - }, [to]) + }, [receiverId]) // 以下是 UI 相关的 state,需要在切换会话时重置 - const [replyMsgsMap, setReplyMsgsMap] = useState>( - {} - ) // 回复消息的 map + const [replyMsgsMap, setReplyMsgsMap] = useState< + Record + >({}) // 回复消息的 map const [action, setAction] = useState(undefined) const [inputValue, setInputValue] = useState('') const [groupCreateVisible, setGroupCreateVisible] = useState(false) @@ -128,16 +139,16 @@ const P2pChatContainer: React.FC = observer( const [noMore, setNoMore] = useState(false) const [receiveMsgBtnVisible, setReceiveMsgBtnVisible] = useState(false) const [settingDrawerVisible, setSettingDrawerVisible] = useState(false) - const [forwardMessage, setForwardMessage] = useState( - undefined - ) + const [forwardMessage, setForwardMessage] = useState< + V2NIMMessageForUI | undefined + >(undefined) const getHistory = useCallback( async (endTime: number, lastMsgId?: string) => { try { setLoadingMore(true) const historyMsgs = await store.msgStore.getHistoryMsgActive({ - sessionId, + conversationId, endTime, lastMsgId, limit: storeConstants.HISTORY_LIMIT, @@ -153,17 +164,20 @@ const P2pChatContainer: React.FC = observer( message.error(t('getHistoryMsgFailedText')) } }, - [sessionId, store.msgStore, t] + [conversationId, store.msgStore, t] ) // 收消息,发消息时需要调用 - const scrollToBottom = useCallback(() => { - if (messageListContainerDomRef.current) { - messageListContainerDomRef.current.scrollTop = - messageListContainerDomRef.current.scrollHeight - } - setReceiveMsgBtnVisible(false) - }, []) + const scrollToBottom = useCallback( + debounce(() => { + if (messageListContainerDomRef.current) { + messageListContainerDomRef.current.scrollTop = + messageListContainerDomRef.current.scrollHeight + } + setReceiveMsgBtnVisible(false) + }, 300), + [] + ) const onMsgListScrollHandler = useCallback( debounce(async () => { @@ -185,14 +199,15 @@ const P2pChatContainer: React.FC = observer( const _msg = msgs.filter( (item) => !( - item.type === 'custom' && - ['beReCallMsg', 'reCallMsg'].includes(item.attach?.type || '') + item.messageType === + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM && + ['beReCallMsg', 'reCallMsg'].includes(item.recallType || '') ) )[0] if (_msg) { - await getHistory(_msg.time, _msg.idServer) + await getHistory(_msg.createTime, _msg.messageServerId) // 滚动到加载的那条消息 - document.getElementById(_msg.idClient)?.scrollIntoView() + document.getElementById(_msg.messageClientId)?.scrollIntoView() } } } @@ -226,9 +241,9 @@ const P2pChatContainer: React.FC = observer( }, []) const onReeditClick = useCallback( - (msg: IMMessage) => { - setInputValue(msg.attach?.oldBody || '') - const replyMsg = replyMsgsMap[msg.idClient] + (msg: V2NIMMessageForUI) => { + setInputValue(msg.oldText || '') + const replyMsg = replyMsgsMap[msg.messageClientId] replyMsg && store.msgStore.replyMsgActive(replyMsg) chatMessageInputRef.current?.input?.focus() }, @@ -236,16 +251,36 @@ const P2pChatContainer: React.FC = observer( ) const onResend = useCallback( - async (msg: IMMessage) => { + async (msg: V2NIMMessageForUI) => { try { - await store.msgStore.resendMsgActive(msg) - } catch (error) { - // message.error(t('sendMsgFailedText')) - } finally { + switch (msg.messageType) { + case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE: + case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO: + await store.msgStore.sendMessageActive({ + msg, + conversationId, + progress: () => true, + sendBefore: () => { + scrollToBottom() + }, + }) + break + default: + await store.msgStore.sendMessageActive({ + msg, + conversationId, + sendBefore: () => { + scrollToBottom() + }, + }) + break + } scrollToBottom() + } catch (error) { + // } }, - [scrollToBottom, store.msgStore] + [store.msgStore, conversationId, scrollToBottom] ) const onSendText = useCallback( @@ -254,14 +289,17 @@ const P2pChatContainer: React.FC = observer( if (onSendTextFromProps) { await onSendTextFromProps({ value, - scene, - to, + conversationType, + receiverId, }) } else { - await store.msgStore.sendTextMsgActive({ - scene, - to, - body: value, + const textMsg = nim.V2NIMMessageCreator.createTextMessage(value) + await store.msgStore.sendMessageActive({ + msg: textMsg, + conversationId, + sendBefore: () => { + scrollToBottom() + }, }) } } catch (error) { @@ -270,16 +308,30 @@ const P2pChatContainer: React.FC = observer( scrollToBottom() } }, - [onSendTextFromProps, scene, store.msgStore, to, scrollToBottom] + [ + onSendTextFromProps, + conversationType, + store.msgStore, + receiverId, + conversationId, + scrollToBottom, + nim.V2NIMMessageCreator, + ] ) const onSendFile = useCallback( async (file: File) => { try { - await store.msgStore.sendFileMsgActive({ - scene, - to, + const fileMsg = nim.V2NIMMessageCreator.createFileMessage( file, + file.name + ) + await store.msgStore.sendMessageActive({ + msg: fileMsg, + conversationId, + sendBefore: () => { + scrollToBottom() + }, }) } catch (error) { // message.error(t('sendMsgFailedText')) @@ -287,25 +339,21 @@ const P2pChatContainer: React.FC = observer( scrollToBottom() } }, - [scene, store.msgStore, to, scrollToBottom] + [store.msgStore, conversationId, scrollToBottom, nim.V2NIMMessageCreator] ) const onSendImg = useCallback( - async (file: File, randomId?: string) => { + async (file: File) => { try { const previewImg = await getImgDataUrl(file) - await store.msgStore.sendImageMsgActive({ - scene, - to, - file, + const imgMsg = nim.V2NIMMessageCreator.createImageMessage(file) + await store.msgStore.sendMessageActive({ + msg: imgMsg, + conversationId, previewImg, - randomId, - onUploadStart(task, taskId) { + progress: () => true, + sendBefore: () => { scrollToBottom() - addTask(taskId, task) - }, - onUploadDone(taskId) { - removeTask(taskId) }, }) } catch (error) { @@ -314,25 +362,21 @@ const P2pChatContainer: React.FC = observer( scrollToBottom() } }, - [scene, store.msgStore, to, scrollToBottom] + [store.msgStore, conversationId, scrollToBottom, nim.V2NIMMessageCreator] ) const onSendVideo = useCallback( - async (file: File, randomId?: string) => { + async (file: File) => { try { const previewImg = await getVideoFirstFrameDataUrl(file) - await store.msgStore.sendVideoMsgActive({ - scene, - to, - file, + const videoMsg = nim.V2NIMMessageCreator.createVideoMessage(file) + await store.msgStore.sendMessageActive({ + msg: videoMsg, + conversationId, previewImg, - randomId, - onUploadStart(task, taskId) { + progress: () => true, + sendBefore: () => { scrollToBottom() - addTask(taskId, task) - }, - onUploadDone(taskId) { - removeTask(taskId) }, }) } catch (error) { @@ -341,15 +385,15 @@ const P2pChatContainer: React.FC = observer( scrollToBottom() } }, - [scene, store.msgStore, to, scrollToBottom] + [store.msgStore, conversationId, scrollToBottom, nim.V2NIMMessageCreator] ) const onRemoveReplyMsg = useCallback(() => { - replyMsg && store.msgStore.removeReplyMsgActive(replyMsg.sessionId) + replyMsg && store.msgStore.removeReplyMsgActive(replyMsg.conversationId) }, [replyMsg, store.msgStore]) const onMessageAction = useCallback( - async (key: MenuItemKey, msg: IMMessage) => { + async (key: MenuItemKey, msg: V2NIMMessageForUI) => { const msgOperMenuItem = msgOperMenu?.find((item) => item.key === key) if (msgOperMenuItem?.onClick) { return msgOperMenuItem?.onClick(msg) @@ -362,7 +406,7 @@ const P2pChatContainer: React.FC = observer( await store.msgStore.reCallMsgActive(msg) break case 'reply': - await store.msgStore.replyMsgActive(msg) + store.msgStore.replyMsgActive(msg) chatMessageInputRef.current?.input?.focus() break case 'forward': @@ -394,15 +438,7 @@ const P2pChatContainer: React.FC = observer( resetSettingState() message.success(t('createTeamSuccessText')) } catch (error: any) { - switch (error?.code) { - // 无权限 - case 802: - message.error(t('noPermission')) - break - default: - message.error(t('createTeamFailedText')) - break - } + message.error(t('createTeamFailedText')) } }, [store.teamStore, t] @@ -434,11 +470,16 @@ const P2pChatContainer: React.FC = observer( useEffect(() => { const notMyMsgs = msgs - .filter((item) => item.from !== myUser.account) - .filter((item) => !!item.idServer) + .filter((item) => item.senderId !== myUser.accountId) + .filter((item) => !!item.messageServerId) .filter((item) => // 以下这些类型的消息不需要发送已读未读 - ['notification', 'tip', 'robot', 'g2'].every((j) => j !== item.type) + [ + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION, + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_TIPS, + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_ROBOT, + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL, + ].every((j) => j !== item.messageType) ) const visibleChangeHandler = (params: { @@ -448,7 +489,7 @@ const P2pChatContainer: React.FC = observer( if (params.visible) { // 发送已读 const msg = notMyMsgs.find( - (item) => item.idClient === params.target.id + (item) => item.messageClientId === params.target.id ) if (msg) { store.msgStore.sendMsgReceiptActive(msg).finally(() => { @@ -460,7 +501,7 @@ const P2pChatContainer: React.FC = observer( const handler = (isObserve: boolean) => { notMyMsgs.forEach((item) => { - const target = document.getElementById(item.idClient) + const target = document.getElementById(item.messageClientId) if (target) { if (isObserve) { visibilityObserver.observe(target) @@ -482,7 +523,7 @@ const P2pChatContainer: React.FC = observer( return () => { handler(false) } - }, [store.msgStore, msgs, visibilityObserver, myUser.account]) + }, [store.msgStore, msgs, visibilityObserver, myUser.accountId]) useEffect(() => { return () => { @@ -494,33 +535,33 @@ const P2pChatContainer: React.FC = observer( useEffect(() => { resetState() scrollToBottom() - }, [to, resetState, scrollToBottom]) + }, [receiverId, resetState, scrollToBottom]) // 切换会话时,如果内存中除了撤回消息的其他消息小于10条(差不多一屏幕),需要拉取历史消息 useEffect(() => { - if ( - store.msgStore - .getMsg(sessionId) - .filter( - (item) => - !['beReCallMsg', 'reCallMsg'].includes(item.attach?.type || '') - ).length < 10 - ) { + const memoryMsgs = conversationId + ? store.msgStore + .getMsg(conversationId) + .filter( + (item) => + !['beReCallMsg', 'reCallMsg'].includes(item.recallType || '') + ) + : [] + + if (memoryMsgs.length < 10) { getHistory(Date.now()).then((res) => { scrollToBottom() - if (session && !session.lastMsg && res && res[0]) { - store.sessionStore.addSession([{ ...session, lastMsg: res[0] }]) - } + // TODO 考虑以下这段代码是否还需要 + // if (conversation && !conversation.lastMessage && res && res[0]) { + // store.conversationStore.addConversation([ + // { ...conversation, lastMessage: res[0] }, + // ]) + // } }) + } else { + scrollToBottom() } - }, [ - store.msgStore, - store.sessionStore, - session, - sessionId, - getHistory, - scrollToBottom, - ]) + }, [store.msgStore, conversationId, getHistory, scrollToBottom]) // 处理消息 useEffect(() => { @@ -531,25 +572,27 @@ const P2pChatContainer: React.FC = observer( from: string to: string idServer: string + idClient: string time: number }> = [] - const idClients: Record = {} + const messageClientIds: Record = {} msgs.forEach((msg) => { - if (msg.ext) { + if (msg.serverExtension) { try { - const { yxReplyMsg } = JSON.parse(msg.ext) + const { yxReplyMsg } = JSON.parse(msg.serverExtension) if (yxReplyMsg) { const replyMsg = msgs.find( - (item) => item.idClient === yxReplyMsg.idClient + (item) => item.messageClientId === yxReplyMsg.idClient ) if (replyMsg) { - replyMsgsMap[msg.idClient] = replyMsg + replyMsgsMap[msg.messageClientId] = replyMsg } else { - replyMsgsMap[msg.idClient] = 'noFind' - const { scene, from, to, idServer, time } = yxReplyMsg - if (scene && from && to && idServer && time) { - reqMsgs.push({ scene, from, to, idServer, time }) - idClients[idServer] = msg.idClient + replyMsgsMap[msg.messageClientId] = 'noFind' + const { scene, from, to, idServer, idClient, time } = + yxReplyMsg + if (scene && from && to && idServer && idClient && time) { + reqMsgs.push({ scene, from, to, idServer, idClient, time }) + messageClientIds[idServer] = msg.messageClientId } } } @@ -557,10 +600,26 @@ const P2pChatContainer: React.FC = observer( } }) if (reqMsgs.length > 0) { - store.msgStore.getMsgByIdServerActive({ reqMsgs }).then((res) => { + nim.V2NIMMessageService.getMessageListByRefers( + reqMsgs.map((item) => ({ + senderId: item.from, + receiverId: item.to, + messageClientId: item.idClient, + messageServerId: item.idServer, + createTime: item.time, + conversationType: + item.scene === 'p2p' + ? V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P + : V2NIMConst.V2NIMConversationType + .V2NIM_CONVERSATION_TYPE_TEAM, + conversationId: nim.V2NIMConversationIdUtil.p2pConversationId( + item.to + ), + })) + ).then((res) => { res.forEach((item) => { - if (item.idServer) { - replyMsgsMap[idClients[item.idServer]] = item + if (item.messageServerId) { + replyMsgsMap[messageClientIds[item.messageServerId]] = item } }) setReplyMsgsMap({ ...replyMsgsMap }) @@ -569,11 +628,14 @@ const P2pChatContainer: React.FC = observer( setReplyMsgsMap({ ...replyMsgsMap }) } } - }, [msgs, store]) + }, [msgs, store, nim.V2NIMMessageService, nim.V2NIMConversationIdUtil]) useLayoutEffect(() => { - const onMsg = (msg: IMMessage) => { - if (messageListContainerDomRef.current && msg.sessionId === sessionId) { + const onMsg = (msg: V2NIMMessage[]) => { + if ( + messageListContainerDomRef.current && + msg[0].conversationId === conversationId + ) { // 当收到消息时,如果已经往上滚动了,是不需要滚动到最底部的 if ( messageListContainerDomRef.current.scrollTop < @@ -588,18 +650,18 @@ const P2pChatContainer: React.FC = observer( } } - nim.on('msg', onMsg) + nim.V2NIMMessageService.on('onReceiveMessages', onMsg) return () => { - nim.off('msg', onMsg) + nim.V2NIMMessageService.off('onReceiveMessages', onMsg) } - }, [nim, sessionId]) + }, [nim, conversationId, scrollToBottom]) - return session ? ( + return conversation ? (
{renderHeader ? ( - renderHeader(session) + renderHeader(conversation) ) : ( = observer( } avatar={ } @@ -630,18 +692,15 @@ const P2pChatContainer: React.FC = observer( msgs={msgs} msgOperMenu={msgOperMenu} replyMsgsMap={replyMsgsMap} - member={user} + receiverId={receiverId} noMore={noMore} loadingMore={loadingMore} - myAccount={myUser?.account || ''} receiveMsgBtnVisible={receiveMsgBtnVisible} - msgReceiptTime={session?.msgReceiptTime} + msgReceiptTime={conversation?.msgReceiptTime} onReceiveMsgBtnClick={scrollToBottom} - onResend={onResend} - onSendImg={onSendImg} - onSendVideo={onSendVideo} onMessageAction={onMessageAction} onReeditClick={onReeditClick} + onResend={onResend} onScroll={onMsgListScrollHandler} renderP2pCustomMessage={renderP2pCustomMessage} renderMessageAvatar={renderMessageAvatar} @@ -655,16 +714,14 @@ const P2pChatContainer: React.FC = observer( prefix={prefix} placeholder={ renderP2pInputPlaceHolder - ? renderP2pInputPlaceHolder(session) + ? renderP2pInputPlaceHolder(conversation) : `${t('sendToText')} ${userNickOrAccount}${t('sendUsageText')}` } replyMsg={replyMsg} - scene={scene} - to={to} + conversationType={conversationType} + receiverId={receiverId} actions={actions} inputValue={inputValue} - uploadImageLoading={store.uiStore.uploadImageLoading} - uploadFileLoading={store.uiStore.uploadFileLoading} setInputValue={setInputValue} onSendText={onSendText} onSendFile={onSendFile} @@ -681,8 +738,8 @@ const P2pChatContainer: React.FC = observer( > { setGroupCreateVisible(true) }} @@ -707,7 +764,7 @@ const P2pChatContainer: React.FC = observer( /> Promise afterTransferTeam?: (teamId: string) => Promise renderTeamCustomMessage?: ( options: RenderTeamCustomMessageOptions ) => JSX.Element | null | undefined - renderHeader?: (session: Session) => JSX.Element + renderHeader?: (conversation: V2NIMConversation) => JSX.Element renderTeamInputPlaceHolder?: (params: { - session: Session + conversation: V2NIMConversation mute: boolean }) => string renderTeamMemberItem?: ( params: GroupItemProps ) => JSX.Element | null | undefined - renderMessageAvatar?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageName?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageOuterContent?: (msg: IMMessage) => JSX.Element | null | undefined - renderMessageInnerContent?: (msg: IMMessage) => JSX.Element | null | undefined + renderMessageAvatar?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined + renderMessageName?: (msg: V2NIMMessageForUI) => JSX.Element | null | undefined + renderMessageOuterContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined + renderMessageInnerContent?: ( + msg: V2NIMMessageForUI + ) => JSX.Element | null | undefined prefix?: string commonPrefix?: string @@ -78,8 +86,8 @@ export interface TeamChatContainerProps { const TeamChatContainer: React.FC = observer( ({ - scene, - to, + conversationType, + receiverId, settingActions, actions, msgOperMenu, @@ -101,94 +109,106 @@ const TeamChatContainer: React.FC = observer( const { t } = useTranslation() - const sessionId = `${scene}-${to}` + const conversationId = + nim.V2NIMConversationIdUtil.teamConversationId(receiverId) - const session = store.sessionStore.sessions.get(sessionId) + const conversation = + store.conversationStore.conversations.get(conversationId) - const msgs = store.msgStore.getMsg(sessionId) + const msgs = store.msgStore.getMsg(conversationId) - const replyMsg = store.msgStore.replyMsgs.get(sessionId) + const replyMsg = store.msgStore.replyMsgs.get(conversationId) - const team: Team = store.teamStore.teams.get(to) || { - teamId: to, - type: 'normal', + const team: V2NIMTeam = store.teamStore.teams.get(receiverId) || { + teamId: receiverId, + teamType: V2NIMConst.V2NIMTeamType.V2NIM_TEAM_TYPE_ADVANCED, name: '', avatar: '', intro: '', announcement: '', - joinMode: 'noVerify', - beInviteMode: 'noVerify', - inviteMode: 'manager', - updateTeamMode: 'manager', - updateExtMode: 'manager', - owner: '', - level: 0, - memberNum: 0, - memberUpdateTime: Date.now(), + joinMode: V2NIMConst.V2NIMTeamJoinMode.V2NIM_TEAM_JOIN_MODE_FREE, + agreeMode: V2NIMConst.V2NIMTeamAgreeMode.V2NIM_TEAM_AGREE_MODE_NO_AUTH, + inviteMode: V2NIMConst.V2NIMTeamInviteMode.V2NIM_TEAM_INVITE_MODE_MANAGER, + updateInfoMode: + V2NIMConst.V2NIMTeamUpdateInfoMode.V2NIM_TEAM_UPDATE_INFO_MODE_MANAGER, + updateExtensionMode: + V2NIMConst.V2NIMTeamUpdateExtensionMode + .V2NIM_TEAM_UPDATE_EXTENSION_MODE_MANAGER, + ownerAccountId: '', + memberCount: 0, createTime: Date.now(), updateTime: Date.now(), - ext: '', - serverExt: '', - valid: false, - validToCurrentUser: false, - mute: false, - muteType: 'none', + serverExtension: '', + isValidTeam: false, + chatBannedMode: + V2NIMConst.V2NIMTeamChatBannedMode.V2NIM_TEAM_CHAT_BANNED_MODE_UNBAN, + memberLimit: 200, + messageNotifyMode: + V2NIMConst.V2NIMTeamMessageNotifyMode + .V2NIM_TEAM_MESSAGE_NOTIFY_MODE_ALL, } - const teamMembers = store.teamMemberStore.getTeamMember(to) + const teamMembers = store.teamMemberStore.getTeamMember(receiverId) const myUser = store.userStore.myUserInfo const teamNameOrTeamId = team?.name || team?.teamId || '' - const isGroupOwner = myUser?.account === team.owner + const isGroupOwner = myUser?.accountId === team.ownerAccountId const isGroupManager = teamMembers - .filter((item) => item.type === 'manager') - .some((item) => item.account === myUser?.account) - - const mentionMembers = useMemo(() => { - return teamMembers.filter( - (member) => member.account !== myUser?.account - // if (member.account !== myUser?.account) { - // member.alias = store.uiStore.getAppellation({ - // account: member.account, - // teamId: member.teamId, - // }) - // member.nickInTeam = store.uiStore.getAppellation({ - // account: member.account, - // teamId: member.teamId, - // ignoreAlias: true, - // }) - // return true - // } - // return false + .filter( + (item) => + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER ) - }, [teamMembers, myUser?.account]) + .some((item) => item.accountId === myUser?.accountId) const sortedMembers = useMemo(() => { - const owner = teamMembers.filter((item) => item.type === 'owner') + const owner = teamMembers.filter( + (item) => + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER + ) const manager = teamMembers - .filter((item) => item.type === 'manager') + .filter( + (item) => + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER + ) .sort((a, b) => a.joinTime - b.joinTime) const other = teamMembers - .filter((item) => !['owner', 'manager'].includes(item.type)) + .filter( + (item) => + item.memberRole === + V2NIMConst.V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_NORMAL + ) .sort((a, b) => a.joinTime - b.joinTime) const _sortedMembers = [...owner, ...manager, ...other] return _sortedMembers }, [teamMembers]) + const mentionMembers = useMemo(() => { + return sortedMembers.filter( + (member) => member.accountId !== myUser?.accountId + ) + }, [sortedMembers, myUser?.accountId]) + const teamMute = useMemo(() => { - if (team.mute) { + if ( + team.chatBannedMode === + V2NIMConst.V2NIMTeamChatBannedMode + .V2NIM_TEAM_CHAT_BANNED_MODE_BANNED_NORMAL + ) { return !isGroupOwner && !isGroupManager } - return team.mute - }, [team.mute, isGroupOwner, isGroupManager]) + return false + }, [team.chatBannedMode, isGroupOwner, isGroupManager]) const allowAtAll = useMemo(() => { let ext: TAllowAt = {} try { - ext = JSON.parse(team.ext || '{}') + ext = JSON.parse(team.serverExtension || '{}') } catch (error) { // } @@ -196,13 +216,13 @@ const TeamChatContainer: React.FC = observer( return isGroupOwner || isGroupManager } return true - }, [team.ext, isGroupOwner, isGroupManager]) + }, [team.serverExtension, isGroupOwner, isGroupManager]) const teamDefaultAddMembers = useMemo(() => { return teamMembers - .filter((item) => item.account !== myUser?.account) - .map((item) => item.account) - }, [teamMembers, myUser?.account]) + .filter((item) => item.accountId !== myUser?.accountId) + .map((item) => item.accountId) + }, [teamMembers, myUser?.accountId]) const messageListContainerDomRef = useRef(null) const settingDrawDomRef = useRef(null) @@ -212,15 +232,15 @@ const TeamChatContainer: React.FC = observer( return new VisibilityObserver({ root: messageListContainerDomRef.current, }) - }, [to]) + }, [receiverId]) const [groupTransferModalVisible, setGroupTransferModalVisible] = useState(false) // 以下是 UI 相关的 state,需要在切换会话时重置 - const [replyMsgsMap, setReplyMsgsMap] = useState>( - {} - ) // 回复消息的 map + const [replyMsgsMap, setReplyMsgsMap] = useState< + Record + >({}) // 回复消息的 map const [inputValue, setInputValue] = useState('') const [navHistoryStack, setNavHistoryStack] = useState([]) const [action, setAction] = useState(undefined) @@ -229,9 +249,9 @@ const TeamChatContainer: React.FC = observer( const [groupAddMembersVisible, setGroupAddMembersVisible] = useState(false) const [receiveMsgBtnVisible, setReceiveMsgBtnVisible] = useState(false) const [settingDrawerVisible, setSettingDrawerVisible] = useState(false) - const [forwardMessage, setForwardMessage] = useState( - undefined - ) + const [forwardMessage, setForwardMessage] = useState< + V2NIMMessageForUI | undefined + >(undefined) const SETTING_NAV_TITLE_MAP: { [key in ChatAction]: string } = useMemo( () => ({ @@ -267,7 +287,7 @@ const TeamChatContainer: React.FC = observer( try { setLoadingMore(true) const historyMsgs = await store.msgStore.getHistoryMsgActive({ - sessionId, + conversationId, endTime, lastMsgId, limit: storeConstants.HISTORY_LIMIT, @@ -277,22 +297,32 @@ const TeamChatContainer: React.FC = observer( setNoMore(true) } return historyMsgs - } catch (error) { + } catch (error: any) { setLoadingMore(false) - message.error(t('getHistoryMsgFailedText')) + switch (error.code) { + case 109404: + message.error(t('teamMemberNotExist')) + break + default: + message.error(t('getHistoryMsgFailedText')) + break + } } }, - [sessionId, store.msgStore, t] + [conversationId, store.msgStore, t] ) // 收消息,发消息时需要调用 - const scrollToBottom = useCallback(() => { - if (messageListContainerDomRef.current) { - messageListContainerDomRef.current.scrollTop = - messageListContainerDomRef.current.scrollHeight - } - setReceiveMsgBtnVisible(false) - }, []) + const scrollToBottom = useCallback( + debounce(() => { + if (messageListContainerDomRef.current) { + messageListContainerDomRef.current.scrollTop = + messageListContainerDomRef.current.scrollHeight + } + setReceiveMsgBtnVisible(false) + }, 300), + [] + ) const onMsgListScrollHandler = useCallback( debounce(async () => { @@ -314,14 +344,15 @@ const TeamChatContainer: React.FC = observer( const _msg = msgs.filter( (item) => !( - item.type === 'custom' && - ['beReCallMsg', 'reCallMsg'].includes(item.attach?.type || '') + item.messageType === + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM && + ['beReCallMsg', 'reCallMsg'].includes(item.recallType || '') ) )[0] if (_msg) { - await getHistory(_msg.time, _msg.idServer) + await getHistory(_msg.createTime, _msg.messageServerId) // 滚动到加载的那条消息 - document.getElementById(_msg.idClient)?.scrollIntoView() + document.getElementById(_msg.messageClientId)?.scrollIntoView() } } } @@ -356,14 +387,14 @@ const TeamChatContainer: React.FC = observer( }, []) const onReeditClick = useCallback( - (msg: IMMessage) => { - const replyMsg = replyMsgsMap[msg.idClient] + (msg: V2NIMMessageForUI) => { + const replyMsg = replyMsgsMap[msg.messageClientId] replyMsg && store.msgStore.replyMsgActive(replyMsg) // 处理 @ 消息 - const { ext } = msg - if (ext) { + const { serverExtension } = msg + if (serverExtension) { try { - const extObj = JSON.parse(ext) + const extObj = JSON.parse(serverExtension) const yxAitMsg = extObj.yxAitMsg if (yxAitMsg) { const mentionedMembers: MentionedMember[] = [] @@ -375,13 +406,13 @@ const TeamChatContainer: React.FC = observer( }) } else { const member = teamMembers.find( - (item) => item.account === key + (item) => item.accountId === key ) member && mentionedMembers.push({ - account: member.account, + account: member.accountId, appellation: store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, ignoreAlias: true, }), @@ -394,23 +425,43 @@ const TeamChatContainer: React.FC = observer( } } catch {} } - setInputValue(msg.attach?.oldBody || '') + setInputValue(msg.oldText || '') chatMessageInputRef.current?.input?.focus() }, [replyMsgsMap, store.msgStore, teamMembers, store.uiStore, t] ) const onResend = useCallback( - async (msg: IMMessage) => { + async (msg: V2NIMMessageForUI) => { try { - await store.msgStore.resendMsgActive(msg) - } catch (error) { - // message.error(t('sendMsgFailedText')) - } finally { + switch (msg.messageType) { + case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE: + case V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO: + await store.msgStore.sendMessageActive({ + msg, + conversationId, + progress: () => true, + sendBefore: () => { + scrollToBottom() + }, + }) + break + default: + await store.msgStore.sendMessageActive({ + msg, + conversationId, + sendBefore: () => { + scrollToBottom() + }, + }) + break + } scrollToBottom() + } catch (error) { + // } }, - [scrollToBottom, store.msgStore] + [store.msgStore, conversationId, scrollToBottom] ) const onSendText = useCallback( @@ -419,15 +470,18 @@ const TeamChatContainer: React.FC = observer( if (onSendTextFromProps) { await onSendTextFromProps({ value, - scene, - to, + conversationType, + receiverId, }) } else { - await store.msgStore.sendTextMsgActive({ - scene, - to, - body: value, - ext, + const textMsg = nim.V2NIMMessageCreator.createTextMessage(value) + await store.msgStore.sendMessageActive({ + msg: textMsg, + conversationId, + serverExtension: ext, + sendBefore: () => { + scrollToBottom() + }, }) } } catch (error) { @@ -436,16 +490,30 @@ const TeamChatContainer: React.FC = observer( scrollToBottom() } }, - [onSendTextFromProps, scene, store.msgStore, to, scrollToBottom] + [ + onSendTextFromProps, + conversationType, + store.msgStore, + receiverId, + conversationId, + scrollToBottom, + nim.V2NIMMessageCreator, + ] ) const onSendFile = useCallback( async (file: File) => { try { - await store.msgStore.sendFileMsgActive({ - scene, - to, + const fileMsg = nim.V2NIMMessageCreator.createFileMessage( file, + file.name + ) + await store.msgStore.sendMessageActive({ + msg: fileMsg, + conversationId, + sendBefore: () => { + scrollToBottom() + }, }) } catch (error) { // message.error(t('sendMsgFailedText')) @@ -453,25 +521,21 @@ const TeamChatContainer: React.FC = observer( scrollToBottom() } }, - [scene, store.msgStore, to, scrollToBottom] + [store.msgStore, conversationId, scrollToBottom, nim.V2NIMMessageCreator] ) const onSendImg = useCallback( - async (file: File, randomId?: string) => { + async (file: File) => { try { const previewImg = await getImgDataUrl(file) - await store.msgStore.sendImageMsgActive({ - scene, - to, - file, + const imgMsg = nim.V2NIMMessageCreator.createImageMessage(file) + await store.msgStore.sendMessageActive({ + msg: imgMsg, + conversationId, previewImg, - randomId, - onUploadStart(task, taskId) { + progress: () => true, + sendBefore: () => { scrollToBottom() - addTask(taskId, task) - }, - onUploadDone(taskId) { - removeTask(taskId) }, }) } catch (error) { @@ -480,25 +544,21 @@ const TeamChatContainer: React.FC = observer( scrollToBottom() } }, - [scene, store.msgStore, to, scrollToBottom] + [store.msgStore, conversationId, scrollToBottom, nim.V2NIMMessageCreator] ) const onSendVideo = useCallback( - async (file: File, randomId?: string) => { + async (file: File) => { try { const previewImg = await getVideoFirstFrameDataUrl(file) - await store.msgStore.sendVideoMsgActive({ - scene, - to, - file, + const videoMsg = nim.V2NIMMessageCreator.createVideoMessage(file) + await store.msgStore.sendMessageActive({ + msg: videoMsg, + conversationId, previewImg, - randomId, - onUploadStart(task, taskId) { + progress: () => true, + sendBefore: () => { scrollToBottom() - addTask(taskId, task) - }, - onUploadDone(taskId) { - removeTask(taskId) }, }) } catch (error) { @@ -507,15 +567,15 @@ const TeamChatContainer: React.FC = observer( scrollToBottom() } }, - [scene, store.msgStore, to, scrollToBottom] + [store.msgStore, conversationId, scrollToBottom, nim.V2NIMMessageCreator] ) const onRemoveReplyMsg = useCallback(() => { - replyMsg && store.msgStore.removeReplyMsgActive(replyMsg.sessionId) + replyMsg && store.msgStore.removeReplyMsgActive(replyMsg.conversationId) }, [replyMsg, store.msgStore]) const onMessageAction = useCallback( - async (key: MenuItemKey, msg: IMMessage) => { + async (key: MenuItemKey, msg: V2NIMMessageForUI) => { const msgOperMenuItem = msgOperMenu?.find((item) => item.key === key) if (msgOperMenuItem?.onClick) { return msgOperMenuItem?.onClick(msg) @@ -529,18 +589,18 @@ const TeamChatContainer: React.FC = observer( break case 'reply': const member = mentionMembers.find( - (item) => item.account === msg.from + (item) => item.accountId === msg.senderId ) member && chatMessageInputRef.current?.onAtMemberSelectHandler({ - account: member.account, + account: member.accountId, appellation: store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, ignoreAlias: true, }), }) - await store.msgStore.replyMsgActive(msg) + store.msgStore.replyMsgActive(msg) chatMessageInputRef.current?.input?.focus() break case 'forward': @@ -554,17 +614,17 @@ const TeamChatContainer: React.FC = observer( ) const onMessageAvatarAction = useCallback( - async (key: AvatarMenuItem, msg: IMMessage) => { + async (key: AvatarMenuItem, msg: V2NIMMessageForUI) => { switch (key) { case 'mention': const member = mentionMembers.find( - (item) => item.account === msg.from + (item) => item.accountId === msg.senderId ) member && chatMessageInputRef.current?.onAtMemberSelectHandler({ - account: member.account, + account: member.accountId, appellation: store.uiStore.getAppellation({ - account: member.account, + account: member.accountId, teamId: member.teamId, ignoreAlias: true, }), @@ -584,7 +644,7 @@ const TeamChatContainer: React.FC = observer( } catch (error: any) { switch (error?.code) { // 无权限 - case 802: + case 109427: message.error(t('noPermission')) break default: @@ -599,15 +659,7 @@ const TeamChatContainer: React.FC = observer( await store.teamStore.leaveTeamActive(team.teamId) message.success(t('leaveTeamSuccessText')) } catch (error: any) { - switch (error?.code) { - // 无权限 - case 802: - message.error(t('noPermission')) - break - default: - message.error(t('leaveTeamFailedText')) - break - } + message.error(t('leaveTeamFailedText')) } }, [store.teamStore, team.teamId, t]) @@ -621,7 +673,12 @@ const TeamChatContainer: React.FC = observer( }, [afterTransferTeam, team.teamId]) const onAddMembersClick = useCallback(() => { - if (team.inviteMode === 'manager' && !isGroupOwner && !isGroupManager) { + if ( + team.inviteMode === + V2NIMConst.V2NIMTeamInviteMode.V2NIM_TEAM_INVITE_MODE_MANAGER && + !isGroupOwner && + !isGroupManager + ) { message.error(t('noPermission')) } else { setGroupAddMembersVisible(true) @@ -640,7 +697,7 @@ const TeamChatContainer: React.FC = observer( } catch (error: any) { switch (error?.code) { // 无权限 - case 802: + case 109306: message.error(t('noPermission')) break default: @@ -653,17 +710,17 @@ const TeamChatContainer: React.FC = observer( ) const onRemoveTeamMember = useCallback( - async (member: TeamMember) => { + async (member: V2NIMTeamMember) => { try { await store.teamMemberStore.removeTeamMemberActive({ teamId: team.teamId, - accounts: [member.account], + accounts: [member.accountId], }) message.success(t('removeTeamMemberSuccessText')) } catch (error: any) { switch (error?.code) { // 无权限 - case 802: + case 109306: message.error(t('noPermission')) break default: @@ -676,17 +733,17 @@ const TeamChatContainer: React.FC = observer( ) const onUpdateTeamInfo = useCallback( - async (params: Partial) => { + async (params: V2NIMUpdatedTeamInfo) => { try { await store.teamStore.updateTeamActive({ - ...params, teamId: team.teamId, + info: params, }) message.success(t('updateTeamSuccessText')) } catch (error: any) { switch (error?.code) { // 无权限 - case 802: + case 109432: message.error(t('noPermission')) break default: @@ -699,45 +756,35 @@ const TeamChatContainer: React.FC = observer( ) const onUpdateMyMemberInfo = useCallback( - async (params: UpdateMyMemberInfoOptions) => { - const nickTipVisible = params.nickInTeam !== void 0 - const bitConfigVisible = params.bitConfigMask !== void 0 + async (params: V2NIMUpdateSelfMemberInfoParams) => { + const nickTipVisible = params.teamNick !== void 0 try { - await store.teamMemberStore.updateMyMemberInfo(params) + await store.teamMemberStore.updateMyMemberInfoActive({ + teamId: team.teamId, + memberInfo: params, + }) if (nickTipVisible) { message.success(t('updateMyMemberNickSuccess')) } - if (bitConfigVisible) { - message.success(t('updateBitConfigMaskSuccess')) - } } catch (error: any) { - switch (error?.code) { - // 无权限 - case 802: - message.error(t('noPermission')) - break - default: - { - if (nickTipVisible) { - message.error(t('updateMyMemberNickFailed')) - } - if (bitConfigVisible) { - message.error(t('updateBitConfigMaskFailed')) - } - } - break + if (nickTipVisible) { + message.error(t('updateMyMemberNickFailed')) } } }, - [store.teamMemberStore, t] + [store.teamMemberStore, team.teamId, t] ) const onTeamMuteChange = useCallback( async (mute: boolean) => { try { - await store.teamStore.muteTeamActive({ + await store.teamStore.setTeamChatBannedActive({ teamId: team.teamId, - mute, + chatBannedMode: mute + ? V2NIMConst.V2NIMTeamChatBannedMode + .V2NIM_TEAM_CHAT_BANNED_MODE_BANNED_NORMAL + : V2NIMConst.V2NIMTeamChatBannedMode + .V2NIM_TEAM_CHAT_BANNED_MODE_UNBAN, }) message.success( mute ? t('muteAllTeamSuccessText') : t('unmuteAllTeamSuccessText') @@ -745,7 +792,7 @@ const TeamChatContainer: React.FC = observer( } catch (error: any) { switch (error?.code) { // 无权限 - case 802: + case 109432: message.error(t('noPermission')) break default: @@ -790,11 +837,16 @@ const TeamChatContainer: React.FC = observer( useEffect(() => { const notMyMsgs = msgs - .filter((item) => item.from !== myUser.account) - .filter((item) => !!item.idServer) + .filter((item) => item.senderId !== myUser.accountId) + .filter((item) => !!item.messageServerId) .filter((item) => // 以下这些类型的消息不需要发送已读未读 - ['notification', 'tip', 'robot', 'g2'].every((j) => j !== item.type) + [ + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION, + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_TIPS, + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_ROBOT, + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL, + ].every((j) => j !== item.messageType) ) const visibleChangeHandler = (params: { @@ -804,17 +856,11 @@ const TeamChatContainer: React.FC = observer( if (params.visible) { // 发送已读 const msg = notMyMsgs.find( - (item) => item.idClient === params.target.id + (item) => item.messageClientId === params.target.id ) if (msg) { store.msgStore - .sendTeamMsgReceiptActive([ - { - teamId: team.teamId, - idClient: msg.idClient, - idServer: msg.idServer ? msg.idServer : '', - }, - ]) + .sendTeamMsgReceiptActive([msg]) .catch((err) => { // 忽略这个报错 }) @@ -827,7 +873,7 @@ const TeamChatContainer: React.FC = observer( const handler = (isObserve: boolean) => { notMyMsgs.forEach((item) => { - const target = document.getElementById(item.idClient) + const target = document.getElementById(item.messageClientId) if (target) { if (isObserve) { visibilityObserver.observe(target) @@ -849,7 +895,13 @@ const TeamChatContainer: React.FC = observer( return () => { handler(false) } - }, [store.msgStore, msgs, visibilityObserver, team.teamId, myUser.account]) + }, [ + store.msgStore, + msgs, + visibilityObserver, + team.teamId, + myUser.accountId, + ]) useEffect(() => { return () => { @@ -861,34 +913,66 @@ const TeamChatContainer: React.FC = observer( useEffect(() => { resetState() scrollToBottom() - store.teamStore.getTeamActive(to) - store.teamMemberStore.getTeamMemberActive(to) - }, [store.teamStore, store.teamMemberStore, to, resetState, scrollToBottom]) + store.teamStore.getTeamActive(receiverId).catch((err) => { + console.warn('获取群组失败:', err.toString()) + }) + store.teamMemberStore + .getTeamMemberActive({ + teamId: receiverId, + type: 1, + queryOption: { + limit: Math.max(team.memberLimit, 200), + roleQueryType: 0, + }, + }) + .catch((err) => { + console.warn('获取群组成员失败:', err.toString()) + }) + }, [ + team.memberLimit, + store.teamStore, + store.teamMemberStore, + receiverId, + resetState, + scrollToBottom, + ]) // 切换会话时,如果内存中除了撤回消息的其他消息小于10条(差不多一屏幕),需要拉取历史消息 useEffect(() => { - if ( - store.msgStore - .getMsg(sessionId) - .filter( - (item) => - !['beReCallMsg', 'reCallMsg'].includes(item.attach?.type || '') - ).length < 10 - ) { + const memoryMsgs = conversationId + ? store.msgStore + .getMsg(conversationId) + .filter( + (item) => + !['beReCallMsg', 'reCallMsg'].includes(item.recallType || '') + ) + : [] + + if (memoryMsgs.length < 10) { getHistory(Date.now()).then((res) => { scrollToBottom() - if (session && !session.lastMsg && res && res[0]) { - store.sessionStore.addSession([{ ...session, lastMsg: res[0] }]) - } + // TODO 考虑以下这段代码是否还需要 + // if (conversation && !conversation.lastMessage && res && res[0]) { + // store.conversationStore.addConversation([ + // { ...conversation, lastMessage: res[0] }, + // ]) + // } }) + } else { + // 获取自己发出去的消息 + const myMsgs = memoryMsgs.filter( + (item) => item.senderId === myUser.accountId + ) + // 获取群组已读未读数 + store.msgStore.getTeamMsgReadsActive(myMsgs, conversationId) + scrollToBottom() } }, [ store.msgStore, - store.sessionStore, - session, - sessionId, + conversationId, getHistory, scrollToBottom, + myUser.accountId, ]) // 处理消息 @@ -900,25 +984,27 @@ const TeamChatContainer: React.FC = observer( from: string to: string idServer: string + idClient: string time: number }> = [] - const idClients: string[] = [] + const messageClientIds: string[] = [] msgs.forEach((msg) => { - if (msg.ext) { + if (msg.serverExtension) { try { - const { yxReplyMsg } = JSON.parse(msg.ext) + const { yxReplyMsg } = JSON.parse(msg.serverExtension) if (yxReplyMsg) { const replyMsg = msgs.find( - (item) => item.idClient === yxReplyMsg.idClient + (item) => item.messageClientId === yxReplyMsg.idClient ) if (replyMsg) { - replyMsgsMap[msg.idClient] = replyMsg + replyMsgsMap[msg.messageClientId] = replyMsg } else { - replyMsgsMap[msg.idClient] = 'noFind' - const { scene, from, to, idServer, time } = yxReplyMsg - if (scene && from && to && idServer && time) { - reqMsgs.push({ scene, from, to, idServer, time }) - idClients.push(msg.idClient) + replyMsgsMap[msg.messageClientId] = 'noFind' + const { scene, from, to, idServer, idClient, time } = + yxReplyMsg + if (scene && from && to && idServer && idClient && time) { + reqMsgs.push({ scene, from, to, idServer, idClient, time }) + messageClientIds.push(msg.messageClientId) } } } @@ -926,9 +1012,25 @@ const TeamChatContainer: React.FC = observer( } }) if (reqMsgs.length > 0) { - store.msgStore.getMsgByIdServerActive({ reqMsgs }).then((res) => { + nim.V2NIMMessageService.getMessageListByRefers( + reqMsgs.map((item) => ({ + senderId: item.from, + receiverId: item.to, + messageClientId: item.idClient, + messageServerId: item.idServer, + createTime: item.time, + conversationType: + item.scene === 'p2p' + ? V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P + : V2NIMConst.V2NIMConversationType + .V2NIM_CONVERSATION_TYPE_TEAM, + conversationId: nim.V2NIMConversationIdUtil.teamConversationId( + item.to + ), + })) + ).then((res) => { res.forEach((item, index) => { - replyMsgsMap[idClients[index]] = item + replyMsgsMap[messageClientIds[index]] = item }) setReplyMsgsMap({ ...replyMsgsMap }) }) @@ -936,11 +1038,20 @@ const TeamChatContainer: React.FC = observer( setReplyMsgsMap({ ...replyMsgsMap }) } } - }, [msgs, store, team.teamId]) + }, [ + msgs, + store, + team.teamId, + nim.V2NIMMessageService, + nim.V2NIMConversationIdUtil, + ]) useLayoutEffect(() => { - const onMsg = (msg: IMMessage) => { - if (messageListContainerDomRef.current && msg.sessionId === sessionId) { + const onMsg = (msg: V2NIMMessageForUI[]) => { + if ( + messageListContainerDomRef.current && + msg[0].conversationId === conversationId + ) { // 当收到消息时,如果已经往上滚动了,是不需要滚动到最底部的 if ( messageListContainerDomRef.current.scrollTop < @@ -955,88 +1066,50 @@ const TeamChatContainer: React.FC = observer( } } - nim.on('msg', onMsg) + nim.V2NIMMessageService.on('onReceiveMessages', onMsg) return () => { - nim.off('msg', onMsg) + nim.V2NIMMessageService.off('onReceiveMessages', onMsg) } - }, [nim, sessionId]) + }, [nim, conversationId, scrollToBottom]) useEffect(() => { - // const onDismissTeam = (data: { teamId: string }) => { - // const _sessionId = `team-${data.teamId}` - // if (_sessionId === sessionId) { - // message.warning(t('onDismissTeamText')) - // } - // } - // const onAddTeamMembers = (data: { - // team: Team - // // 以下两个参数是增量 - // accounts: string[] - // members: TeamMember[] - // }) => { - // const _sessionId = `team-${data.team.teamId}` - // if (_sessionId === sessionId) { - // const nicks = data.members.map( - // (item) => item.nickInTeam || item.account - // ) - // message.info(`${nicks.join(',')}${t('enterTeamText')}`) - // } - // } - // const onRemoveTeamMembers = (data: { - // team: Team - // accounts: string[] - // }) => { - // const _sessionId = `team-${data.team.teamId}` - // if (_sessionId === sessionId) { - // if (data.accounts.includes(myUser.account)) { - // message.warning(t('onRemoveTeamText')) - // } else { - // const _tms = store.teamMemberStore.teamMembers.get(data.team.teamId) - // let nicks: string[] = [] - // if (_tms) { - // nicks = data.accounts - // .map((item) => { - // const _t = _tms.get(item) - // if (_t) { - // return _t.nickInTeam || _t.account - // } - // return '' - // }) - // .filter((item) => !!item) - // } - // message.info(`${nicks.join(',')}${t('leaveTeamText')}`) - // } - // } - // } - // 根据 onMsg 处理提示 - const onMsgToast = (msg: IMMessage) => { - if (msg.sessionId === sessionId && msg.type === 'notification') { - switch (msg.attach?.type) { + const onMsgToast = (msgs: V2NIMMessageForUI[]) => { + const msg = msgs[0] + if ( + msg.conversationId === conversationId && + msg.messageType === + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION + ) { + const attachment = + msg.attachment as V2NIMMessageNotificationAttachment + switch (attachment?.type) { // 主动离开群聊 - case 'leaveTeam': { - if (msg.from === myUser.account) { + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_LEAVE: { + if (msg.senderId === myUser.accountId) { message.success(t('leaveTeamSuccessText')) } else { message.info( `${store.uiStore.getAppellation({ - account: msg.from, - teamId: msg.to, + account: msg.senderId, + teamId: msg.receiverId, })}${t('leaveTeamText')}` ) } break } // 踢出群聊 - case 'removeTeamMembers': { - if (msg.attach?.accounts.includes(myUser.account)) { + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_KICK: { + if ((attachment?.targetIds || []).includes(myUser.accountId)) { message.warning(t('onRemoveTeamText')) } else { - const nicks = msg.attach?.accounts.map((item) => + const nicks = (attachment?.targetIds || []).map((item) => store.uiStore.getAppellation({ account: item, - teamId: msg.to, + teamId: msg.receiverId, }) ) message.info(`${nicks.join(',')}${t('leaveTeamText')}`) @@ -1044,28 +1117,35 @@ const TeamChatContainer: React.FC = observer( break } // 解散群聊 - case 'dismissTeam': + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_DISMISS: message.warning(t('onDismissTeamText')) break // 有人主动加入群聊 - case 'passTeamApply': + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_APPLY_PASS: // 邀请加入群聊对方同意 - case 'acceptTeamInvite': + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_INVITE_ACCEPT: { - if (msg.from === myUser.account) { + if (msg.senderId === myUser.accountId) { message.info( `${store.uiStore.getAppellation({ - account: msg.from, - teamId: msg.to, + account: msg.senderId, + teamId: msg.receiverId, })}${t('enterTeamText')}` ) } } break // 邀请加入群聊无需验证 - case 'addTeamMembers': { - const nicks = msg.attach?.accounts.map((item) => - store.uiStore.getAppellation({ account: item, teamId: msg.to }) + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_INVITE: { + const nicks = (attachment?.targetIds || []).map((item) => + store.uiStore.getAppellation({ + account: item, + teamId: msg.receiverId, + }) ) message.info(`${nicks.join(',')}${t('enterTeamText')}`) break @@ -1074,29 +1154,23 @@ const TeamChatContainer: React.FC = observer( } } - nim.on('msg', onMsgToast) - // nim.on('dismissTeam', onDismissTeam) - // nim.on('addTeamMembers', onAddTeamMembers) - // nim.on('removeTeamMembers', onRemoveTeamMembers) + nim.V2NIMMessageService.on('onReceiveMessages', onMsgToast) return () => { - nim.off('msg', onMsgToast) - // nim.off('dismissTeam', onDismissTeam) - // nim.off('addTeamMembers', onAddTeamMembers) - // nim.off('removeTeamMembers', onRemoveTeamMembers) + nim.V2NIMMessageService.off('onReceiveMessages', onMsgToast) } - }, [nim, sessionId, myUser.account, store.uiStore, t]) + }, [nim, conversationId, myUser.accountId, store.uiStore, t]) - return session ? ( + return conversation ? (
{renderHeader ? ( - renderHeader(session) + renderHeader(conversation) ) : ( = observer( members={teamMembers} noMore={noMore} loadingMore={loadingMore} - myAccount={myUser?.account || ''} receiveMsgBtnVisible={receiveMsgBtnVisible} onReceiveMsgBtnClick={scrollToBottom} onResend={onResend} - onSendImg={onSendImg} - onSendVideo={onSendVideo} onMessageAction={onMessageAction} onMessageAvatarAction={onMessageAvatarAction} onReeditClick={onReeditClick} @@ -1140,7 +1211,7 @@ const TeamChatContainer: React.FC = observer( placeholder={ renderTeamInputPlaceHolder ? renderTeamInputPlaceHolder({ - session: session, + conversation, mute: teamMute, }) : teamMute @@ -1149,14 +1220,12 @@ const TeamChatContainer: React.FC = observer( } replyMsg={replyMsg} mentionMembers={mentionMembers} - scene={scene} - to={to} + conversationType={conversationType} + receiverId={receiverId} actions={actions} inputValue={inputValue} mute={teamMute} allowAtAll={allowAtAll} - uploadImageLoading={store.uiStore.uploadImageLoading} - uploadFileLoading={store.uiStore.uploadFileLoading} setInputValue={setInputValue} onRemoveReplyMsg={onRemoveReplyMsg} onSendText={onSendText} @@ -1174,7 +1243,7 @@ const TeamChatContainer: React.FC = observer( = observer( /> = observer( members={sortedMembers} onOk={handleTransferTeam} onCancel={handleGroupActionCancel} - teamId={to} + teamId={receiverId} />
) : null diff --git a/react/src/YXUIKit/im-kit-ui/src/common/components/CommonParseSession/index.tsx b/react/src/YXUIKit/im-kit-ui/src/common/components/CommonParseSession/index.tsx index 87f59e3..20feca8 100644 --- a/react/src/YXUIKit/im-kit-ui/src/common/components/CommonParseSession/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/common/components/CommonParseSession/index.tsx @@ -1,18 +1,25 @@ import React, { useEffect, useRef, useState } from 'react' -import { Image, Popover, Progress } from 'antd' +import { Image, Popover, Progress, message } from 'antd' import reactStringReplace from 'react-string-replace' import CommonIcon from '../CommonIcon' import { getFileType, parseFileSize, addUrlSearch } from '@xkit-yx/utils' -import { handleEmojiTranslate } from '../../../utils' -import { IMMessage } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/MsgServiceInterface' +import { handleEmojiTranslate, logger } from '../../../utils' +import { + V2NIMMessage, + V2NIMMessageImageAttachment, + V2NIMMessageFileAttachment, + V2NIMMessageAudioAttachment, + V2NIMMessageLocationAttachment, + V2NIMMessageVideoAttachment, + V2NIMMessageNotificationAttachment, +} from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMMessageService' +import { V2NIMTeam } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' import { useTranslation, useStateContext } from '../../index' -import { Team } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/TeamServiceInterface' import { observer } from 'mobx-react' -import { parseSessionId } from '../../../utils' import { ALLOW_AT, TAllowAt } from '../../../constant' -import { UserNameCard } from 'nim-web-sdk-ng/dist/NIM_BROWSER_SDK/UserServiceInterface' import { getBlobImg } from '../../../urlToBlob' -import { abortTask } from '../../../uploadingTask' +import { V2NIMMessageForUI } from '@xkit-yx/im-store-v2/dist/types/types' +import { V2NIMConst } from 'nim-web-sdk-ng' // 对话框中要展示的文件icon标识 const fileIconMap = { @@ -29,8 +36,8 @@ const fileIconMap = { export interface IParseSessionProps { prefix?: string - msg: IMMessage - replyMsg?: IMMessage + msg: V2NIMMessageForUI + replyMsg?: V2NIMMessage } export const pauseAllAudio = (): HTMLAudioElement => { @@ -60,35 +67,43 @@ export const pauseAllVideo = (): void => { export const ParseSession: React.FC = observer( ({ prefix = 'common', msg, replyMsg }) => { const _prefix = `${prefix}-parse-session` - const { store, localOptions } = useStateContext() - // const imagePreview = useRef(null) + const { nim, store, localOptions } = useStateContext() const { t } = useTranslation() const locationDomRef = useRef(null) const audioContainerRef = useRef(null) const notSupportMessageText = t('notSupportMessageText') - // const { type, body, idClient, sessionId, ext } = msg const [audioIconType, setAudioIconType] = useState('icon-yuyin3') - const { scene, to } = parseSessionId(msg.sessionId) const [imgUrl, setImgUrl] = useState('') const [replyImgUrl, setReplyImgUrl] = useState('') + const myAccount = store.userStore.myUserInfo.accountId useEffect(() => { - if (msg.type === 'image' && msg.attach && msg.attach.url) { - const url = `${msg.attach.url}?download=${msg.attach.name}` + if ( + msg.messageType === + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE && + msg.attachment && + (msg.attachment as V2NIMMessageImageAttachment).url + ) { + const url = `${ + (msg.attachment as V2NIMMessageImageAttachment).url + }?download=${(msg.attachment as V2NIMMessageImageAttachment).name}` getBlobImg(url).then((blobUrl) => { setImgUrl(blobUrl) }) } - }, [msg.attach, msg.type]) + }, [msg.attachment, msg.messageType]) useEffect(() => { if ( replyMsg && - replyMsg.type === 'image' && - replyMsg.attach && - replyMsg.attach.url + replyMsg.messageType === + V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE && + replyMsg.attachment && + (replyMsg.attachment as V2NIMMessageImageAttachment).url ) { - const url = `${replyMsg.attach.url}?download=${replyMsg.attach.name}` + const url = `${ + (replyMsg.attachment as V2NIMMessageImageAttachment).url + }?download=${(replyMsg.attachment as V2NIMMessageImageAttachment).name}` getBlobImg(url).then((blobUrl) => { setReplyImgUrl(blobUrl) }) @@ -97,50 +112,68 @@ export const ParseSession: React.FC = observer( let animationFlag = false - const teamId = scene === 'team' ? to : '' + const teamId = + msg.conversationType === + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + ? nim.V2NIMConversationIdUtil.parseConversationTargetId( + msg.conversationId + ) + : '' const { EMOJI_ICON_MAP_CONFIG, INPUT_EMOJI_SYMBOL_REG } = handleEmojiTranslate(t) - const renderCustomText = (msg: IMMessage) => { - const { body, idClient, ext } = msg + const renderCustomText = (msg: V2NIMMessageForUI) => { + const { text, messageClientId, serverExtension } = msg - let text = reactStringReplace(body, /(https?:\/\/\S+)/gi, (match, i) => ( - - {match} - - )) - text = reactStringReplace(text, INPUT_EMOJI_SYMBOL_REG, (match, i) => { - return ( - + let finalText = reactStringReplace( + text, + /(https?:\/\/\S+)/gi, + (match, i) => ( + + {match} + ) - }) - if (ext) { + ) + finalText = reactStringReplace( + finalText, + INPUT_EMOJI_SYMBOL_REG, + (match, i) => { + return ( + + ) + } + ) + if (serverExtension) { try { - const extObj = JSON.parse(ext) + const extObj = JSON.parse(serverExtension) const yxAitMsg = extObj.yxAitMsg if (yxAitMsg && localOptions.needMention) { Object.keys(yxAitMsg).forEach((key) => { const item = yxAitMsg[key] - text = reactStringReplace(text, item.text, (match, i) => { - return ( - - {match} - - ) - }) + finalText = reactStringReplace( + finalText, + item.text, + (match, i) => { + return ( + + {match} + + ) + } + ) }) } } catch {} } - return
{text}
+ return
{finalText}
} const playAudioAnimation = () => { @@ -161,9 +194,8 @@ export const ParseSession: React.FC = observer( handler() } - const renderUploadMsg = (msg: IMMessage) => { - // @ts-ignore - const { uploadProgress, previewImg, idClient } = msg + const renderUploadMsg = (msg: V2NIMMessageForUI) => { + const { uploadProgress, previewImg, sendingState } = msg return (
@@ -174,33 +206,46 @@ export const ParseSession: React.FC = observer( 'https://yx-web-nosdn.netease.im/common/33d3e1fa8de771277ea4466564ef37aa/emptyImg.png' } /> -
-
{ - abortTask(idClient) - }} - > - + {sendingState === + V2NIMConst.V2NIMMessageSendingState + .V2NIM_MESSAGE_SENDING_STATE_SENDING && + uploadProgress !== void 0 && + uploadProgress < 100 ? ( +
+
{ + store.msgStore + .cancelMessageAttachmentUploadActive(msg) + .catch(() => { + message.error(t('cancelUploadFailedText')) + }) + }} + > + +
-
+ ) : null}
) } - const renderImage = (msg: IMMessage, isReplyMsg: boolean) => { - // @ts-ignore - const { uploadProgress, uploadFileInfo } = msg + const renderImage = (msg: V2NIMMessageForUI, isReplyMsg: boolean) => { + const { uploadProgress, sendingState } = msg + const attachment = msg.attachment as V2NIMMessageImageAttachment - // uploadProgress 属性只会在上传中存在,uploadFileInfo 在上传中和上传失败时都存在 - if (uploadProgress !== void 0 || uploadFileInfo) { + // 上传中或没有真实 url 时走这里 + if ( + (uploadProgress !== void 0 && uploadProgress < 100) || + !attachment.url + ) { return renderUploadMsg(msg) } @@ -226,50 +271,73 @@ export const ParseSession: React.FC = observer( ) } - const renderFile = (msg: IMMessage) => { + const renderFile = (msg: V2NIMMessageForUI) => { + let downloadHref = '' + try { + downloadHref = addUrlSearch( + (msg.attachment as V2NIMMessageFileAttachment)?.url, + `download=${(msg.attachment as V2NIMMessageFileAttachment)?.name}` + ) + } catch (error) { + // + } + return (
- + {(msg.attachment as V2NIMMessageFileAttachment)?.name} + + ) : ( + + {(msg.attachment as V2NIMMessageFileAttachment)?.name} + + )} + + {parseFileSize( + (msg.attachment as V2NIMMessageFileAttachment)?.size )} - target="_blank" - > - {msg?.attach?.name} - - {parseFileSize(msg?.attach?.size)} +
) } - const renderNotification = (msg: IMMessage) => { - switch (msg.attach?.type) { - case 'updateTeam': { - const team: Team = msg.attach?.team || {} + const renderNotification = (msg: V2NIMMessageForUI) => { + const attachment = msg.attachment as V2NIMMessageNotificationAttachment + switch (attachment?.type) { + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_UPDATE_TINFO: { + const team: V2NIMTeam = (attachment?.updatedTeamInfo || + {}) as V2NIMTeam const content: string[] = [] - if (team.avatar !== undefined) { + if (team.avatar !== void 0) { content.push(t('updateTeamAvatar')) } - if (team.name !== undefined) { + if (team.name !== void 0) { content.push(`${t('updateTeamName')}“${team.name}”`) } - if (team.intro !== undefined) { + if (team.intro !== void 0) { content.push(t('updateTeamIntro')) } - if (team.inviteMode) { + if (team.inviteMode !== void 0) { content.push( `${t('updateTeamInviteMode')}“${ - team.inviteMode === 'all' + team.inviteMode === + V2NIMConst.V2NIMTeamInviteMode.V2NIM_TEAM_INVITE_MODE_ALL ? t('teamAll') : localOptions.teamManagerVisible ? t('teamOwnerAndManagerText') @@ -277,10 +345,12 @@ export const ParseSession: React.FC = observer( }”` ) } - if (team.updateTeamMode) { + if (team.updateInfoMode !== void 0) { content.push( `${t('updateTeamUpdateTeamMode')}“${ - team.updateTeamMode === 'all' + team.updateInfoMode === + V2NIMConst.V2NIMTeamUpdateInfoMode + .V2NIM_TEAM_UPDATE_INFO_MODE_ALL ? t('teamAll') : localOptions.teamManagerVisible ? t('teamOwnerAndManagerText') @@ -288,21 +358,25 @@ export const ParseSession: React.FC = observer( }”` ) } - if (team.muteType) { + if (team.chatBannedMode !== void 0) { content.push( `${t('updateTeamMute')}${ - team.muteType === 'none' ? t('closeText') : t('openText') + team.chatBannedMode === + V2NIMConst.V2NIMTeamChatBannedMode + .V2NIM_TEAM_CHAT_BANNED_MODE_UNBAN + ? t('closeText') + : t('openText') }` ) } - if (team.ext) { + if (team.serverExtension !== void 0) { let ext: TAllowAt = {} try { - ext = JSON.parse(team.ext) + ext = JSON.parse(team.serverExtension) } catch (error) { // } - if (ext[ALLOW_AT] !== undefined) { + if (ext[ALLOW_AT] !== void 0) { content.push( `${t('updateAllowAt')}“${ ext[ALLOW_AT] === 'manager' @@ -314,50 +388,41 @@ export const ParseSession: React.FC = observer( ) } } - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === msg.from - ) return content.length ? (
{store.uiStore.getAppellation({ - account: msg.from, + account: msg.senderId, teamId, - nickFromMsg: attachUser?.nick, })}{' '} {content.join('、')}
) : null } - // 有人主动加入群聊 - case 'passTeamApply': + // 申请加入群聊成功 + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_APPLY_PASS: // 邀请加入群聊对方同意 - case 'acceptTeamInvite': { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === msg.from - ) + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_INVITE_ACCEPT: { return (
{store.uiStore.getAppellation({ - account: msg.from, + account: msg.senderId, teamId, - nickFromMsg: attachUser?.nick, })}{' '} {t('joinTeamText')}
) } // 邀请加入群聊无需验证 - case 'addTeamMembers': { - const accounts: string[] = msg.attach?.accounts || [] + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_INVITE: { + const accounts: string[] = attachment?.targetIds || [] const nicks = accounts .map((item) => { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === item - ) return store.uiStore.getAppellation({ account: item, teamId, - nickFromMsg: attachUser?.nick, }) }) .filter((item) => !!item) @@ -369,17 +434,14 @@ export const ParseSession: React.FC = observer( ) } // 踢出群聊 - case 'removeTeamMembers': { - const accounts: string[] = msg.attach?.accounts || [] + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_KICK: { + const accounts: string[] = attachment?.targetIds || [] const nicks = accounts .map((item) => { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === item - ) return store.uiStore.getAppellation({ account: item, teamId, - nickFromMsg: attachUser?.nick, }) }) .filter((item) => !!item) @@ -391,17 +453,14 @@ export const ParseSession: React.FC = observer( ) } // 增加群管理员 - case 'addTeamManagers': { - const accounts: string[] = msg.attach?.accounts || [] + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_ADD_MANAGER: { + const accounts: string[] = attachment?.targetIds || [] const nicks = accounts .map((item) => { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === item - ) return store.uiStore.getAppellation({ account: item, teamId, - nickFromMsg: attachUser?.nick, }) }) .filter((item) => !!item) @@ -413,17 +472,14 @@ export const ParseSession: React.FC = observer( ) } // 移除群管理员 - case 'removeTeamManagers': { - const accounts: string[] = msg.attach?.accounts || [] + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_REMOVE_MANAGER: { + const accounts: string[] = attachment?.targetIds || [] const nicks = accounts .map((item) => { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === item - ) return store.uiStore.getAppellation({ account: item, teamId, - nickFromMsg: attachUser?.nick, }) }) .filter((item) => !!item) @@ -435,33 +491,27 @@ export const ParseSession: React.FC = observer( ) } // 主动退出群聊 - case 'leaveTeam': { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === msg.from - ) + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_LEAVE: { return (
{store.uiStore.getAppellation({ - account: msg.from, + account: msg.senderId, teamId, - nickFromMsg: attachUser?.nick, })}{' '} {t('leaveTeamText')}
) } // 转让群主 - case 'transferTeam': { - const attachUser = (msg.attach?.users as UserNameCard[]).find( - (_) => _.account === msg.attach?.account - ) + case V2NIMConst.V2NIMMessageNotificationType + .V2NIM_MESSAGE_NOTIFICATION_TYPE_TEAM_OWNER_TRANSFER: { return (
{store.uiStore.getAppellation({ - account: msg.attach?.account, + account: (attachment?.targetIds || [])[0], teamId, - nickFromMsg: attachUser?.nick, })} {t('newGroupOwnerText')} @@ -473,9 +523,9 @@ export const ParseSession: React.FC = observer( } } - const renderAudio = (msg: IMMessage) => { - const { flow, attach } = msg - const duration = Math.floor(attach?.dur / 1000) || 0 + const renderAudio = (msg: V2NIMMessageForUI) => { + const attachment = msg.attachment as V2NIMMessageAudioAttachment + const duration = Math.floor(attachment?.duration / 1000) || 0 const containerWidth = 80 + 15 * (duration - 1) @@ -487,21 +537,23 @@ export const ParseSession: React.FC = observer( >
{ pauseAllVideo() const oldAudio = pauseAllAudio() const msgId = oldAudio?.getAttribute('msgId') // 如果是自己,暂停动画 - if (msgId === msg.idClient) { + if (msgId === msg.messageClientId) { animationFlag = false return } - const audio = new Audio(attach?.url) + const audio = new Audio(attachment?.url) // 播放音频,并开始动画 audio.id = 'yx-audio-message' - audio.setAttribute('msgId', msg.idClient) + audio.setAttribute('msgId', msg.messageClientId) audio.play() audioContainerRef.current?.appendChild(audio) audio.addEventListener('ended', () => { @@ -525,25 +577,37 @@ export const ParseSession: React.FC = observer( ) } - const renderVideo = (msg: IMMessage) => { - // @ts-ignore - const { uploadProgress, uploadFileInfo, attach } = msg + const renderVideo = (msg: V2NIMMessageForUI) => { + const uploadProgress = msg.uploadProgress + const attachment = msg.attachment as V2NIMMessageVideoAttachment + + let url = attachment.url + + if (attachment.file && !url) { + try { + url = URL.createObjectURL(attachment.file) + } catch (error) { + logger.warn('createObjectURL fail: ', attachment) + } + } - // uploadProgress 属性只会在上传中存在,uploadFileInfo 在上传中和上传失败时都存在 - if (uploadProgress !== void 0 || uploadFileInfo) { + // 上传中或没有真实 url 时走这里 + if ((uploadProgress !== void 0 && uploadProgress < 100) || !url) { return renderUploadMsg(msg) } - const url = `${attach?.url}?download=${msg.idClient}.${attach?.ext}` + url = url.startsWith('blob:') + ? url + : `${url}?download=${msg.messageClientId}.${attachment?.ext}` return (