From 10e766d27cfc9c6e01d11985880eb1fe5d1987b7 Mon Sep 17 00:00:00 2001 From: leeing <372711472@qq.com> Date: Mon, 22 Jul 2024 15:36:22 +0800 Subject: [PATCH] update im --- react/src/YXUIKit/im-kit-ui/package.json | 7 +- .../YXUIKit/im-kit-ui/src/chat/Container.tsx | 6 +- .../chat/components/ChatAddMembers/index.tsx | 8 +- .../ChatCollectionList/CollectionItem.tsx | 2 + .../components/ChatForwardModal/index.tsx | 4 +- .../ChatGroupTransferModal/index.tsx | 29 +-- .../components/ChatTeamMemberModal/index.tsx | 49 +++-- .../components/ChatTeamSetting/GroupList.tsx | 85 ++++++-- .../chat/components/ChatTeamSetting/index.tsx | 6 +- .../ChatTeamSetting/style/groupList.less | 8 +- .../src/chat/containers/p2pChatContainer.tsx | 35 ++-- .../src/chat/containers/teamChatContainer.tsx | 44 ++-- .../components/CreateTeamModal/index.tsx | 10 +- .../common/components/FriendSelect/index.tsx | 107 ++++++---- .../common/components/SelectModal/index.tsx | 194 ++++++++++++------ .../components/SelectModal/style/index.less | 3 +- .../friend-list/components/FriendList.tsx | 67 ++++-- .../src/contact/group-list/Container.tsx | 19 +- .../group-list/components/GroupList.tsx | 41 +++- .../contact/group-list/style/groupItem.less | 5 +- .../contact/group-list/style/groupList.less | 1 + .../im-kit-ui/src/search/search/Container.tsx | 51 ++--- .../search/components/SearchModal/index.tsx | 177 ++++++++++------ .../src/search/search/style/index.less | 2 +- react/src/components/IMApp/index.tsx | 53 +++-- 25 files changed, 641 insertions(+), 372 deletions(-) diff --git a/react/src/YXUIKit/im-kit-ui/package.json b/react/src/YXUIKit/im-kit-ui/package.json index 38ed125..e6e0205 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": "10.3.0", + "version": "10.3.1", "description": "云信即时通讯组件", "license": "MIT", "main": "lib/index.js", @@ -60,7 +60,8 @@ "mobx": "^6.6.1", "mobx-react": "^7.5.2", "nim-web-sdk-ng": "10.3.0", - "react-string-replace": "^1.1.0" + "react-string-replace": "^1.1.0", + "react-virtualized": "^9.22.5" }, - "gitHead": "5309f0b247ec3584301aa3ec5f8df0c3cbcd0966" + "gitHead": "45e04861b1ab80e65b14e6b57c9fd1de60a9a1b0" } 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 6f38ce6..07abce2 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/Container.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/Container.tsx @@ -115,7 +115,11 @@ export interface ChatContainerProps { 自定义渲染群组成员 item */ renderTeamMemberItem?: ( - params: GroupItemProps + params: GroupItemProps & { + renderKey: string + renderIndex: number + renderStyle: React.CSSProperties + } ) => JSX.Element | null | undefined /** 自定义渲染消息头像 diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatAddMembers/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatAddMembers/index.tsx index 4c4d53d..0448404 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatAddMembers/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatAddMembers/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { message, Modal } from 'antd' import { FriendSelect, useTranslation } from '../../../common' // import { SearchInput } from '@x-kit-react/search-kit' @@ -52,6 +52,10 @@ const ChatAddMemebers: React.FC = ({ setSelectedAccounts(defaultAccounts) } + const handleSelect = useCallback((selectedList: string[]) => { + setSelectedAccounts(selectedList) + }, []) + return ( = ({ setSelectedAccounts(selectedList)} + onSelect={handleSelect} selectedAccounts={selectedAccounts} disabledAccounts={defaultAccounts} /> diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCollectionList/CollectionItem.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCollectionList/CollectionItem.tsx index 2ecb395..137ea82 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCollectionList/CollectionItem.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatCollectionList/CollectionItem.tsx @@ -104,6 +104,8 @@ const CollectionItem: React.FC = ({ } + getPopupContainer={(triggerNode) => triggerNode} + overlayClassName={`${_prefix}-dropdown`} >
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 a83c7f5..7c025d3 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 @@ -317,9 +317,9 @@ const ChatForwardModal: React.FC = observer( } } - const handleSelectChange = (value: SelectModalItemProps[]) => { + const handleSelectChange = useCallback((value: SelectModalItemProps[]) => { setSelected(value.map((item) => item.key)) - } + }, []) const handleSelectDelete = (value: SelectModalItemProps) => { setSelected(selected.filter((item) => item !== value.key)) 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 dedc78c..a7cbff9 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 @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import { message } from 'antd' import { ComplexAvatarContainer, @@ -54,9 +54,9 @@ const GroupTransferModal: React.FC = observer( } } - const handleSelect = (value: SelectModalItemProps[]) => { + const handleSelect = useCallback((value: SelectModalItemProps[]) => { setSelectedMemberId(value[0].key) - } + }, []) const datasource: SelectModalItemProps[] = useMemo(() => { const aiUsers = store.aiUserStore.getAIUserList() @@ -81,16 +81,19 @@ const GroupTransferModal: React.FC = observer( return _showMembers }, [members, store.uiStore, store.aiUserStore]) - const itemAvatarRender = (data: SelectModalItemProps) => { - return ( - - ) - } + const itemAvatarRender = useCallback( + (data: SelectModalItemProps) => { + return ( + + ) + }, + [commonPrefix] + ) return ( = observer( const aiUsers = store.aiUserStore.getAIUserList() - const datasource = teamMembers - .filter((item) => aiUsers.every((ai) => ai.accountId !== item.accountId)) - .map((item) => ({ - key: item.accountId, - label: store.uiStore.getAppellation({ - account: item.accountId, - teamId: item.teamId, - }), - })) + const datasource = useMemo( + () => + teamMembers + .filter((item) => + aiUsers.every((ai) => ai.accountId !== item.accountId) + ) + .map((item) => ({ + key: item.accountId, + label: store.uiStore.getAppellation({ + account: item.accountId, + teamId: item.teamId, + }), + })), + [aiUsers, store.uiStore, teamMembers] + ) const teamManagerAccounts = teamMembers .filter( @@ -54,16 +60,19 @@ const ChatTeamMemberModal: React.FC = observer( ) .map((item) => item.accountId) - const itemAvatarRender = (data: SelectModalItemProps) => { - return ( - - ) - } + const itemAvatarRender = useCallback( + (data: SelectModalItemProps) => { + return ( + + ) + }, + [commonPrefix] + ) const handleOk = async (data: SelectModalItemProps[]) => { try { 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 270b9a6..ba33cba 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,10 +1,11 @@ -import React, { FC, useEffect, useMemo, useState } from 'react' +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react' import { GroupItem, GroupItemProps } from './GroupItem' 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' import { observer } from 'mobx-react' +import { AutoSizer, List } from 'react-virtualized' export interface GroupListProps { myMemberInfo: V2NIMTeamMember @@ -12,7 +13,11 @@ export interface GroupListProps { onRemoveTeamMemberClick: (member: V2NIMTeamMember) => void afterSendMsgClick?: () => void renderTeamMemberItem?: ( - params: GroupItemProps + params: GroupItemProps & { + renderKey: string + renderIndex: number + renderStyle: React.CSSProperties + } ) => JSX.Element | null | undefined prefix?: string commonPrefix?: string @@ -62,6 +67,44 @@ const GroupList: FC = observer( return _sortedMembers }, [members, groupSearchText, store.uiStore]) + const rowRenderer = useCallback( + ({ index, key, style }) => { + const item = showMembers[index] + const itemProps: GroupItemProps & { + renderKey: string + renderIndex: number + renderStyle: React.CSSProperties + } = { + member: item, + onRemoveTeamMemberClick, + afterSendMsgClick, + myMemberInfo, + prefix, + commonPrefix, + renderIndex: index, + renderKey: key, + renderStyle: style, + } + + return ( + renderTeamMemberItem?.(itemProps) ?? ( +
+ +
+ ) + ) + }, + [ + afterSendMsgClick, + commonPrefix, + myMemberInfo, + onRemoveTeamMemberClick, + prefix, + renderTeamMemberItem, + showMembers, + ] + ) + return (
= observer( placeholder={t('searchTeamMemberPlaceholder')} onChange={(e) => handleSearch(e.target.value)} /> - {showMembers.length ? ( - showMembers.map((item) => { - const itemProps = { - member: item, - onRemoveTeamMemberClick, - afterSendMsgClick, - myMemberInfo, - prefix, - commonPrefix, - } - - return ( - renderTeamMemberItem?.(itemProps) ?? ( - - ) - ) - }) - ) : ( -
{t('searchNoResText')}
- )} +
+ {showMembers.length ? ( + + {({ height, width }) => ( + + )} + + ) : ( +
{t('searchNoResText')}
+ )} +
) } diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/index.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/index.tsx index 9d8faf5..2dc79e7 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/index.tsx @@ -48,7 +48,11 @@ export interface ChatTeamSettingProps { afterSendMsgClick?: () => void setNavHistoryStack: (stack: HistoryStack[]) => void renderTeamMemberItem?: ( - params: GroupItemProps + params: GroupItemProps & { + renderKey: string + renderIndex: number + renderStyle: React.CSSProperties + } ) => JSX.Element | null | undefined prefix?: string diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/style/groupList.less b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/style/groupList.less index d1c43a6..e7b5163 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/style/groupList.less +++ b/react/src/YXUIKit/im-kit-ui/src/chat/components/ChatTeamSetting/style/groupList.less @@ -6,9 +6,15 @@ .@{prefix-cls}-wrap { width: 100%; height: 100%; - overflow-y: auto; padding: 0 10px; } + +.@{prefix-cls}-list { + width: 100%; + height: calc(100% - 57px); + overflow-y: auto; +} + .@{prefix-cls}-input { margin: 15px 0 10px 0; background-color: @yx-background-color-6!important; diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/containers/p2pChatContainer.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/containers/p2pChatContainer.tsx index a5c4116..ef5f190 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/containers/p2pChatContainer.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/containers/p2pChatContainer.tsx @@ -166,6 +166,23 @@ const P2pChatContainer: React.FC = observer( >(undefined) const [translateOpen, setTranslateOpen] = useState(false) + const resetSettingState = useCallback(() => { + setAction(undefined) + setGroupCreateVisible(false) + setSettingDrawerVisible(false) + }, []) + + const resetState = useCallback(() => { + resetSettingState() + setInputValue('') + setLoadingMore(false) + setNoMore(false) + setReceiveMsgBtnVisible(false) + setForwardMessage(undefined) + setTranslateOpen(false) + store.aiUserStore.resetAIProxy() + }, [store.aiUserStore, resetSettingState]) + const getHistory = useCallback( async (endTime: number, lastMsgId?: string) => { try { @@ -568,7 +585,6 @@ const P2pChatContainer: React.FC = observer( }, [ msgOperMenu, - mentionMembers, store.msgStore, store.userStore.users, store.uiStore, @@ -609,23 +625,6 @@ const P2pChatContainer: React.FC = observer( store.userStore.myUserInfo.accountId, ]) - const resetSettingState = () => { - setAction(undefined) - setGroupCreateVisible(false) - setSettingDrawerVisible(false) - } - - const resetState = useCallback(() => { - resetSettingState() - setInputValue('') - setLoadingMore(false) - setNoMore(false) - setReceiveMsgBtnVisible(false) - setForwardMessage(undefined) - setTranslateOpen(false) - store.aiUserStore.resetAIProxy() - }, [store.aiUserStore]) - const handleForwardModalSend = () => { scrollToBottom() setForwardMessage(undefined) diff --git a/react/src/YXUIKit/im-kit-ui/src/chat/containers/teamChatContainer.tsx b/react/src/YXUIKit/im-kit-ui/src/chat/containers/teamChatContainer.tsx index 9769349..921b655 100644 --- a/react/src/YXUIKit/im-kit-ui/src/chat/containers/teamChatContainer.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/chat/containers/teamChatContainer.tsx @@ -74,7 +74,11 @@ export interface TeamChatContainerProps { mute: boolean }) => string renderTeamMemberItem?: ( - params: GroupItemProps + params: GroupItemProps & { + renderKey: string + renderIndex: number + renderStyle: React.CSSProperties + } ) => JSX.Element | null | undefined renderMessageAvatar?: ( msg: V2NIMMessageForUI @@ -314,6 +318,24 @@ const TeamChatContainer: React.FC = observer( return {defaultTitle} }, [navHistoryStack, SETTING_NAV_TITLE_MAP, action]) + const resetSettingState = useCallback(() => { + setNavHistoryStack([]) + setAction(undefined) + setGroupAddMembersVisible(false) + setSettingDrawerVisible(false) + }, []) + + const resetState = useCallback(() => { + resetSettingState() + setInputValue('') + setLoadingMore(false) + setNoMore(false) + setReceiveMsgBtnVisible(false) + setForwardMessage(undefined) + setTranslateOpen(false) + store.aiUserStore.resetAIProxy() + }, [store.aiUserStore, resetSettingState]) + const getHistory = useCallback( async (endTime: number, lastMsgId?: string) => { try { @@ -909,7 +931,7 @@ const TeamChatContainer: React.FC = observer( } } }, - [store.teamMemberStore, team.teamId, t] + [store.teamMemberStore, team.teamId, t, resetSettingState] ) const onRemoveTeamMember = useCallback( @@ -1010,24 +1032,6 @@ const TeamChatContainer: React.FC = observer( [store.teamStore, team.teamId, t] ) - const resetSettingState = () => { - setNavHistoryStack([]) - setAction(undefined) - setGroupAddMembersVisible(false) - setSettingDrawerVisible(false) - } - - const resetState = useCallback(() => { - resetSettingState() - setInputValue('') - setLoadingMore(false) - setNoMore(false) - setReceiveMsgBtnVisible(false) - setForwardMessage(undefined) - setTranslateOpen(false) - store.aiUserStore.resetAIProxy() - }, [store.aiUserStore]) - const handleForwardModalSend = () => { scrollToBottom() setForwardMessage(undefined) diff --git a/react/src/YXUIKit/im-kit-ui/src/common/components/CreateTeamModal/index.tsx b/react/src/YXUIKit/im-kit-ui/src/common/components/CreateTeamModal/index.tsx index 7bcd894..e9a4000 100644 --- a/react/src/YXUIKit/im-kit-ui/src/common/components/CreateTeamModal/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/common/components/CreateTeamModal/index.tsx @@ -1,5 +1,5 @@ import { Button, message, Modal, Input } from 'antd' -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { urls, FriendSelect, @@ -86,6 +86,10 @@ export const CreateTeamModal: React.FC = observer( setCreating(false) } + const handleSelect = useCallback((accounts: string[]) => { + setSelectedList(accounts) + }, []) + const footer = (
@@ -146,9 +150,7 @@ export const CreateTeamModal: React.FC = observer(
{ - setSelectedList(accounts) - }} + onSelect={handleSelect} max={200} selectedAccounts={selectedList} prefix={prefix} diff --git a/react/src/YXUIKit/im-kit-ui/src/common/components/FriendSelect/index.tsx b/react/src/YXUIKit/im-kit-ui/src/common/components/FriendSelect/index.tsx index 623b948..aea2fcf 100644 --- a/react/src/YXUIKit/im-kit-ui/src/common/components/FriendSelect/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/common/components/FriendSelect/index.tsx @@ -1,10 +1,11 @@ -import React, { FC, useMemo, useState } from 'react' +import React, { FC, useCallback, useMemo, useState } from 'react' import { Divider, Spin, Tabs } from 'antd' import { FriendSelectItem } from './FriendSelectItem' import { groupByPy } from '../../../utils' import { useTranslation } from '../../hooks/useTranslation' import { useStateContext } from '../../hooks/useStateContext' import { observer } from 'mobx-react' +import { AutoSizer, List } from 'react-virtualized' const emptyArr = [] @@ -77,7 +78,10 @@ export const FriendSelect: FC = observer( firstKey: 'appellation', }, false - ), + ) + .map((item) => item.data) + .flat() + .filter((item) => item.visible), } }, [store.uiStore, store.relationStore.blacklist, store.aiUserStore, tab]) @@ -87,17 +91,22 @@ export const FriendSelect: FC = observer( ) }, [dataSource.arrData, selectedAccounts]) - const handleSelect = (account: string, selected: boolean) => { - let _selectedAccounts: string[] = [] - - if (selected && !selectedAccounts.includes(account)) { - _selectedAccounts = selectedAccounts.concat(account) - } else if (!selected && selectedAccounts.includes(account)) { - _selectedAccounts = selectedAccounts.filter((item) => item !== account) - } - - onSelect(_selectedAccounts) - } + const handleSelect = useCallback( + (account: string, selected: boolean) => { + let _selectedAccounts: string[] = [] + + if (selected && !selectedAccounts.includes(account)) { + _selectedAccounts = selectedAccounts.concat(account) + } else if (!selected && selectedAccounts.includes(account)) { + _selectedAccounts = selectedAccounts.filter( + (item) => item !== account + ) + } + + onSelect(_selectedAccounts) + }, + [onSelect, selectedAccounts] + ) const selectedList = useMemo(() => { return dataSource.arrData.filter((item) => @@ -132,6 +141,38 @@ export const FriendSelect: FC = observer( ) : null } + const rowRenderer = useCallback( + ({ index, key, style }) => { + const item = dataSource.groupByPyData[index] + + return ( +
+ = max && + !selectedAccounts.includes(item.account)) + } + prefix={prefix} + {...item} + /> +
+ ) + }, + [ + dataSource.groupByPyData, + disabledAccounts, + handleSelect, + max, + prefix, + selectedAccounts, + ] + ) + return (
{loading ? ( @@ -141,34 +182,18 @@ export const FriendSelect: FC = observer(
{renderTab()}
- {dataSource.groupByPyData.map(({ key, data }) => { - if (data.every((item) => !item.visible)) { - return null - } - - return ( -
-
{key}
- {data - .filter((item) => item.visible) - .map((item) => ( - = max && - !selectedAccounts.includes(item.account)) - } - prefix={prefix} - {...item} - /> - ))} -
- ) - })} + + {({ height, width }) => ( + + )} +
diff --git a/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/index.tsx b/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/index.tsx index 740b0bc..1b61b8a 100644 --- a/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/index.tsx @@ -1,7 +1,9 @@ -import React, { useEffect, useState } from 'react' -import { Modal, Input, Radio, Button, Checkbox } from 'antd' +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { Modal, Input, Radio, Button, Checkbox, RadioChangeEvent } from 'antd' import { CloseOutlined, SearchOutlined } from '@ant-design/icons' import { useTranslation } from '../../../common' +import { AutoSizer, List } from 'react-virtualized' +import { CheckboxChangeEvent } from 'antd/lib/checkbox' export interface SelectModalItemProps { key: string @@ -92,23 +94,28 @@ export const SelectModal: React.FC = ({ } }, [visible]) - // const finalDatasource = useMemo(() => { - // return datasource.filter((item) => item.label.includes(searchText)) - // }, [searchText, datasource]) + const visibleDatasource = useMemo(() => { + return datasource.filter( + (item) => !item.hide && item.label.includes(searchText) + ) + }, [searchText, datasource]) const _prefix = `${prefix}-select-modal` - const getItemsFromKeys = (keys: string[]) => { - return datasource - .filter((item) => keys.some((j) => j === item.key)) - .reduce((unique, item) => { - if (!unique.some((uniqueItem) => uniqueItem.key === item.key)) { - unique.push(item) - } + const getItemsFromKeys = useCallback( + (keys: string[]) => { + return datasource + .filter((item) => keys.some((j) => j === item.key)) + .reduce((unique, item) => { + if (!unique.some((uniqueItem) => uniqueItem.key === item.key)) { + unique.push(item) + } - return unique - }, [] as SelectModalItemProps[]) - } + return unique + }, [] as SelectModalItemProps[]) + }, + [datasource] + ) const handleSearchTextChange = (e: any) => { const value = e.target.value @@ -117,18 +124,28 @@ export const SelectModal: React.FC = ({ onSearchChange?.(value) } - const handleSelect = (e: any) => { - let value: string[] = [] + const handleRadioSelect = (e: RadioChangeEvent) => { + setSelected([e.target.value]) + onSelectChange?.(getItemsFromKeys([e.target.value])) + } - if (type === 'radio') { - value = [e.target.value] - } else { - value = e - } + const handleCheckboxSelect = useCallback( + (e: CheckboxChangeEvent) => { + const { value, checked } = e.target - setSelected(value) - onSelectChange?.(getItemsFromKeys(value)) - } + let newSelected = selected + + if (checked && !selected.includes(value)) { + newSelected = [...selected, value] + } else { + newSelected = selected.filter((i) => i !== value) + } + + setSelected(newSelected) + onSelectChange?.(getItemsFromKeys(newSelected)) + }, + [getItemsFromKeys, onSelectChange, selected] + ) const handleSelectDelete = (item: SelectModalItemProps) => { setSelected(selected.filter((i) => i !== item.key)) @@ -152,60 +169,101 @@ export const SelectModal: React.FC = ({ } } + const radioRowRenderer = useCallback( + ({ index, key, style }) => { + const item = visibleDatasource[index] + + return ( +
+
+ + {itemAvatarRender?.(item)} + {item.label} +
+
+ ) + }, + [_prefix, itemAvatarRender, selected, visibleDatasource] + ) + + const checkboxRowRenderer = useCallback( + ({ index, key, style }) => { + const item = visibleDatasource[index] + + return ( +
+
+ = max} + checked={selected.includes(item.key)} + onChange={handleCheckboxSelect} + /> + {itemAvatarRender?.(item)} + {item.label} +
+
+ ) + }, + [ + _prefix, + handleCheckboxSelect, + itemAvatarRender, + max, + selected, + visibleDatasource, + ] + ) + const renderRadio = () => { return ( - {datasource.map((item, index) => { - const isVisible = !item.hide && item.label.includes(searchText) - - return ( -
- - {itemAvatarRender?.(item)} - {item.label} -
- ) - })} + + {({ height, width }) => ( + + )} +
) } const renderCheckbox = () => { return ( - = max} - > - {datasource.map((item, index) => { - const isVisible = !item.hide && item.label.includes(searchText) - - return ( -
- - {itemAvatarRender?.(item)} - {item.label} -
- ) - })} -
+
+ + {({ height, width }) => ( + + )} + +
) } diff --git a/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/style/index.less b/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/style/index.less index 0867f1b..4dc309f 100644 --- a/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/style/index.less +++ b/react/src/YXUIKit/im-kit-ui/src/common/components/SelectModal/style/index.less @@ -44,7 +44,7 @@ &-l-list { flex: 1; - overflow-y: auto; + // overflow-y: auto; } &-r-list { @@ -64,6 +64,7 @@ } &-item { + display: flex; padding: 8px 12px; width: 100%; align-items: center; diff --git a/react/src/YXUIKit/im-kit-ui/src/contact/friend-list/components/FriendList.tsx b/react/src/YXUIKit/im-kit-ui/src/contact/friend-list/components/FriendList.tsx index 7959c3a..bab14d0 100644 --- a/react/src/YXUIKit/im-kit-ui/src/contact/friend-list/components/FriendList.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/contact/friend-list/components/FriendList.tsx @@ -1,7 +1,8 @@ -import React, { FC, useMemo } from 'react' +import React, { FC, useCallback, useMemo } from 'react' import { Utils, useStateContext, useTranslation } from '../../../common' import { FriendItem } from './FriendItem' import { Spin, Empty } from 'antd' +import { AutoSizer, List } from 'react-virtualized' export interface FriendListProps { accounts: string[] @@ -53,9 +54,41 @@ export const FriendList: FC = ({ res.push(...item.data) }) - return res + + return res.filter((item) => typeof item !== 'string') as { + account: string + appellation: string + }[] }, [accounts, store.uiStore]) + const rowRenderer = useCallback( + ({ index, key, style }) => { + const item = dataSource[index] + + // if (typeof item === 'string') { + // return ( + //
+ // {item} + //
+ // ) + // } + + return ( +
+ +
+ ) + }, + [afterSendMsgClick, commonPrefix, dataSource, onItemClick, prefix] + ) + return (
@@ -73,26 +106,18 @@ export const FriendList: FC = ({ ) ) : ( - dataSource.map((item) => { - if (typeof item === 'string') { - return ( -
- {item} -
- ) - } - - return ( - + {({ height, width }) => ( + - ) - }) + )} + )}
diff --git a/react/src/YXUIKit/im-kit-ui/src/contact/group-list/Container.tsx b/react/src/YXUIKit/im-kit-ui/src/contact/group-list/Container.tsx index 1853105..bfad987 100644 --- a/react/src/YXUIKit/im-kit-ui/src/contact/group-list/Container.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/contact/group-list/Container.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react' +import React, { FC, useCallback } from 'react' import { GroupList } from './components/GroupList' import { useEventTracking, useStateContext } from '../../common' import { V2NIMTeam } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' @@ -42,13 +42,16 @@ export const GroupListContainer: FC = observer( imVersion: sdkPkg.version, }) - const handleItemClick = async (team: V2NIMTeam) => { - await store.conversationStore.insertConversationActive( - V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM, - team.teamId - ) - onItemClick?.(team) - } + const handleItemClick = useCallback( + async (team: V2NIMTeam) => { + await store.conversationStore.insertConversationActive( + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM, + team.teamId + ) + onItemClick?.(team) + }, + [onItemClick, store.conversationStore] + ) return ( = ({ const { t } = useTranslation() + const rowRenderer = useCallback( + ({ index, key, style }) => { + const item = list[index] + + return ( +
+ +
+ ) + }, + [list, onItemClick, prefix] + ) + return (
@@ -40,14 +59,18 @@ export const GroupList: FC = ({ ) ) : ( - list.map((item) => ( - - )) + + {({ height, width }) => ( + + )} + )}
diff --git a/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupItem.less b/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupItem.less index f857dc1..87507de 100644 --- a/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupItem.less +++ b/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupItem.less @@ -10,10 +10,7 @@ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); width: 100%; border-radius: @yx-primary-border-radius; - - &:not(:last-child) { - border-bottom: 1px solid @yx-border-color-7; - } + border-bottom: 1px solid @yx-border-color-7; &:hover { background-color: @yx-background-color-4; diff --git a/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupList.less b/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupList.less index 2ccb058..d658d1c 100644 --- a/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupList.less +++ b/react/src/YXUIKit/im-kit-ui/src/contact/group-list/style/groupList.less @@ -21,4 +21,5 @@ .@{group-content-prefix-cls} { padding: 0 16px; + height: calc(100% - 71px); } diff --git a/react/src/YXUIKit/im-kit-ui/src/search/search/Container.tsx b/react/src/YXUIKit/im-kit-ui/src/search/search/Container.tsx index 4e84be4..062490c 100644 --- a/react/src/YXUIKit/im-kit-ui/src/search/search/Container.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/search/search/Container.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useCallback, useState } from 'react' import { useTranslation, useEventTracking, useStateContext } from '../../common' import { SearchOutlined } from '@ant-design/icons' import SearchModal, { SectionListItem } from './components/SearchModal' @@ -59,29 +59,32 @@ export const SearchContainer: React.FC = observer( const [visible, setVisible] = useState(false) - const handleChat = async (item: SectionListItem) => { - let conversationType: V2NIMConversationType - let receiverId = '' - - if ((item as V2NIMFriend & V2NIMUser).accountId) { - conversationType = - V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P - receiverId = (item as V2NIMFriend & V2NIMUser).accountId - } else if ((item as V2NIMTeam).teamId) { - conversationType = - V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM - receiverId = (item as V2NIMTeam).teamId - } else { - throw Error('unknow scene') - } - - await store.conversationStore.insertConversationActive( - conversationType, - receiverId - ) - setVisible(false) - onClickChat?.() - } + const handleChat = useCallback( + async (item: SectionListItem) => { + let conversationType: V2NIMConversationType + let receiverId = '' + + if ((item as V2NIMFriend & V2NIMUser).accountId) { + conversationType = + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P + receiverId = (item as V2NIMFriend & V2NIMUser).accountId + } else if ((item as V2NIMTeam).teamId) { + conversationType = + V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM + receiverId = (item as V2NIMTeam).teamId + } else { + throw Error('unknow scene') + } + + await store.conversationStore.insertConversationActive( + conversationType, + receiverId + ) + setVisible(false) + onClickChat?.() + }, + [onClickChat, store.conversationStore] + ) const handleOpenModal = async () => { setVisible(true) diff --git a/react/src/YXUIKit/im-kit-ui/src/search/search/components/SearchModal/index.tsx b/react/src/YXUIKit/im-kit-ui/src/search/search/components/SearchModal/index.tsx index 74476ec..dbf0777 100644 --- a/react/src/YXUIKit/im-kit-ui/src/search/search/components/SearchModal/index.tsx +++ b/react/src/YXUIKit/im-kit-ui/src/search/search/components/SearchModal/index.tsx @@ -1,10 +1,11 @@ -import React, { useState, useMemo } from 'react' +import React, { useState, useMemo, useCallback } from 'react' import { Modal } from 'antd' import { SearchInput, CrudeAvatar, useTranslation } from '../../../../common' import { CrudeAvatarProps } from '../../../../common/components/CrudeAvatar' import { V2NIMTeam } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMTeamService' import { V2NIMFriend } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMFriendService' import { V2NIMUser } from 'nim-web-sdk-ng/dist/v2/NIM_BROWSER_SDK/V2NIMUserService' +import { AutoSizer, List } from 'react-virtualized' export interface SearchItemProps extends CrudeAvatarProps { onClick: () => void @@ -32,8 +33,7 @@ const SearchItem: React.FC = ({ export type SectionListItem = (V2NIMFriend & V2NIMUser) | V2NIMTeam export type Section = { - id: string - title: string + id: 'friends' | 'groups' list: SectionListItem[] } @@ -70,67 +70,124 @@ const SearchModal: React.FC = ({ const sections: Section[] = useMemo(() => { return [ { - id: 'friends', - title: t('searchFriendTitle'), + id: 'friends' as Section['id'], list: friends, }, { - id: 'groups', - title: t('searchTeamTitle'), + id: 'groups' as Section['id'], list: teams, }, ].filter((item) => !!item.list.length) - }, [friends, teams, t]) - - const searchedSections: Section[] = useMemo(() => { - return sections - .map((item) => { - if (item.id === 'friends') { - return { - ...item, - list: item.list.filter((item) => { - return ( - (item as V2NIMFriend & V2NIMUser).alias || - (item as V2NIMFriend & V2NIMUser).name || - (item as V2NIMFriend & V2NIMUser).accountId - ).includes(searchText) - }), + }, [friends, teams]) + + const searchedSections: (SectionListItem | 'friends' | 'groups')[] = + useMemo(() => { + const finalSections = sections + .map((item) => { + if (item.id === 'friends') { + return { + ...item, + list: item.list.filter((item) => { + return ( + (item as V2NIMFriend & V2NIMUser).alias || + (item as V2NIMFriend & V2NIMUser).name || + (item as V2NIMFriend & V2NIMUser).accountId + ).includes(searchText) + }), + } } - } - if (item.id === 'groups') { - return { - ...item, - list: item.list.filter((item) => { - return ( - (item as V2NIMTeam).name || (item as V2NIMTeam).teamId - ).includes(searchText) - }), + if (item.id === 'groups') { + return { + ...item, + list: item.list.filter((item) => { + return ( + (item as V2NIMTeam).name || (item as V2NIMTeam).teamId + ).includes(searchText) + }), + } } - } - return { ...item } + return { ...item } + }) + .filter((item) => !!item.list.length) + + const res: (SectionListItem | 'friends' | 'groups')[] = [] + + finalSections.forEach((item) => { + if (item.id === 'friends') { + res.push('friends') + item.list.forEach((item) => { + res.push(item) + }) + } else if (item.id === 'groups') { + res.push('groups') + item.list.forEach((item) => { + res.push(item) + }) + } }) - .filter((item) => !!item.list.length) - }, [sections, searchText]) + + return res + }, [sections, searchText]) const handleSearchChange = (value: string) => { setSearchText(value) } - const handleItemClick = (data: SectionListItem) => { - onResultItemClick(data) - resetState() - } + const resetState = useCallback(() => { + setSearchText('') + }, []) + + const handleItemClick = useCallback( + (data: SectionListItem) => { + onResultItemClick(data) + resetState() + }, + [onResultItemClick, resetState] + ) const handleCancel = () => { onCancel() resetState() } - const resetState = () => { - setSearchText('') - } + const rowRenderer = useCallback( + ({ index, key, style }) => { + const item = searchedSections[index] + + if (typeof item === 'string') { + return ( +
+
+ {t(item === 'friends' ? 'searchFriendTitle' : 'searchTeamTitle')} +
+
+ ) + } + + return ( +
+ handleItemClick(item)} + prefix={prefix} + account={ + (item as V2NIMFriend & V2NIMUser).accountId || + (item as V2NIMTeam).teamId + } + avatar={item.avatar} + nick={item.name} + alias={(item as V2NIMFriend & V2NIMUser).alias || ''} + /> +
+ ) + }, + [_prefix, prefix, searchedSections, t, handleItemClick] + ) return ( = ({ ) ) : (
- {searchedSections.map((section) => ( -
-
- {section.title} -
- {section.list.map((item) => ( - handleItemClick(item)} - prefix={prefix} - account={ - (item as V2NIMFriend & V2NIMUser).accountId || - (item as V2NIMTeam).teamId - } - avatar={item.avatar} - nick={item.name} - alias={(item as V2NIMFriend & V2NIMUser).alias || ''} - /> - ))} -
- ))} + + {({ height, width }) => ( + + )} +
)}
diff --git a/react/src/YXUIKit/im-kit-ui/src/search/search/style/index.less b/react/src/YXUIKit/im-kit-ui/src/search/search/style/index.less index b4fa336..4b65975 100644 --- a/react/src/YXUIKit/im-kit-ui/src/search/search/style/index.less +++ b/react/src/YXUIKit/im-kit-ui/src/search/search/style/index.less @@ -49,7 +49,7 @@ &-content-section-title { color: @yx-border-color-7; border-bottom: 1px solid @yx-border-color-7; - margin-bottom: 8px; + padding-bottom: 8px; } &-content-section-item { diff --git a/react/src/components/IMApp/index.tsx b/react/src/components/IMApp/index.tsx index 2e15a9a..25504f0 100644 --- a/react/src/components/IMApp/index.tsx +++ b/react/src/components/IMApp/index.tsx @@ -295,15 +295,40 @@ const IMApp: React.FC = observer((props) => { ] ) + const goChat = useCallback(() => { + setModel('chat') + }, []) + + const afterAcceptApplyFriend = useCallback( + (data) => { + const textMsg = nim.V2NIMMessageCreator.createTextMessage( + t('passFriendAskText') + ) + + store.msgStore + .sendMessageActive({ + msg: textMsg, + conversationId: nim.V2NIMConversationIdUtil.p2pConversationId( + data.operatorAccountId + ), + }) + .then(() => { + setModel('chat') + }) + }, + [nim.V2NIMConversationIdUtil, nim.V2NIMMessageCreator, store.msgStore] + ) + + const renderContent = useCallback(() => { return ( <>
- setModel('chat')} /> +
- setModel('chat')} /> +
@@ -316,7 +341,7 @@ const IMApp: React.FC = observer((props) => { className={classNames('chat-icon', { active: model === 'chat', })} - onClick={() => setModel('chat')} + onClick={goChat} >
{t('session')}
@@ -382,25 +407,9 @@ const IMApp: React.FC = observer((props) => {
setModel('chat')} - onGroupItemClick={() => setModel('chat')} - afterAcceptApplyFriend={(data) => { - const textMsg = nim.V2NIMMessageCreator.createTextMessage( - t('passFriendAskText') - ) - - store.msgStore - .sendMessageActive({ - msg: textMsg, - conversationId: - nim.V2NIMConversationIdUtil.p2pConversationId( - data.operatorAccountId - ), - }) - .then(() => { - setModel('chat') - }) - }} + afterSendMsgClick={goChat} + onGroupItemClick={goChat} + afterAcceptApplyFriend={afterAcceptApplyFriend} />