Skip to content

Commit

Permalink
feat: add text panel message search right panel
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Nov 1, 2023
1 parent 0ff8f55 commit 176528b
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 133 deletions.
1 change: 0 additions & 1 deletion client/shared/hooks/useAsyncRequest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { DependencyList } from 'react';
import { isDevelopment, t } from '..';
import { showErrorToasts } from '../manager/ui';
import type { FunctionReturningPromise } from '../types';
import { useAsyncFn } from './useAsyncFn';
Expand Down
19 changes: 19 additions & 0 deletions client/shared/model/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ export async function deleteMessage(messageId: string): Promise<boolean> {
return data;
}

/**
* 搜索聊天记录
* @param converseId 会话id
* @param messageText 聊天文本
*/
export async function searchMessage(
text: string,
converseId: string,
groupId?: string
): Promise<ChatMessage[]> {
const { data } = await request.post('/api/chat/message/searchMessage', {
text,
converseId,
groupId,
});

return data;
}

/**
* 基于会话id获取会话最后一条消息的id
*/
Expand Down
2 changes: 1 addition & 1 deletion client/shared/utils/date-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function formatShortTime(date: dayjs.ConfigType): string {
}

/**
* 格式化为 小时:分钟
* 格式化为完整时间 YYYY-MM-DD HH:mm:ss
*/
export function formatFullTime(date: dayjs.ConfigType): string {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss');
Expand Down
267 changes: 136 additions & 131 deletions client/web/src/components/ChatBox/ChatMessageList/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,156 +61,160 @@ const MessageActionIcon: React.FC<{ icon: string }> = (props) => (
/**
* 普通消息
*/
const NormalMessage: React.FC<ChatMessageItemProps> = React.memo((props) => {
const { showAvatar, payload } = props;
const userInfo = useCachedUserInfo(payload.author ?? '');
const [isActionBtnActive, setIsActionBtnActive] = useState(false);
export const NormalMessage: React.FC<ChatMessageItemProps> = React.memo(
(props) => {
const { showAvatar, payload, hideAction = false } = props;
const userInfo = useCachedUserInfo(payload.author ?? '');
const [isActionBtnActive, setIsActionBtnActive] = useState(false);

const reactions = useMessageReactions(payload);
const reactions = useMessageReactions(payload);

const emojiAction = useChatMessageReactionAction(payload);
const moreActions = useChatMessageItemAction(payload, {
onClick: () => {
setIsActionBtnActive(false);
},
});
const emojiAction = useChatMessageReactionAction(payload);
const moreActions = useChatMessageItemAction(payload, {
onClick: () => {
setIsActionBtnActive(false);
},
});

// 禁止对消息进行操作,因为此时消息尚未发送到远程
const disableOperate =
payload.isLocal === true || payload.sendFailed === true;
// 禁止对消息进行操作,因为此时消息尚未发送到远程
const disableOperate =
hideAction === true ||
payload.isLocal === true ||
payload.sendFailed === true;

return (
<div
className={clsx(
'chat-message-item flex px-2 mobile:px-0 group relative select-text',
{
'bg-black bg-opacity-10': isActionBtnActive,
'hover:bg-black hover:bg-opacity-5': !isActionBtnActive,
}
)}
data-message-id={payload._id}
>
{/* 头像 */}
<div className="w-18 mobile:w-14 flex items-start justify-center pt-0.5">
{showAvatar ? (
<Popover
content={
!_isEmpty(userInfo) && (
<UserPopover userInfo={userInfo as UserBaseInfo} />
)
}
placement="top"
trigger="click"
>
<Avatar
className="cursor-pointer"
size={40}
src={userInfo.avatar}
name={userInfo.nickname}
/>
</Popover>
) : (
<div className="hidden group-hover:block opacity-40">
{formatShortTime(payload.createdAt)}
</div>
)}
</div>

{/* 主体 */}
return (
<div
className="flex flex-col flex-1 overflow-auto group"
onContextMenu={stopPropagation}
className={clsx(
'chat-message-item flex px-2 mobile:px-0 group relative select-text text-sm',
{
'bg-black bg-opacity-10': isActionBtnActive,
'hover:bg-black hover:bg-opacity-5': !isActionBtnActive,
}
)}
data-message-id={payload._id}
>
{showAvatar && (
<div className="flex items-center">
<div className="font-bold">
{userInfo.nickname ?? <span>&nbsp;</span>}
</div>
<div className="hidden group-hover:block opacity-40 ml-1 text-sm">
{/* 头像 */}
<div className="w-18 mobile:w-14 flex items-start justify-center pt-0.5">
{showAvatar ? (
<Popover
content={
!_isEmpty(userInfo) && (
<UserPopover userInfo={userInfo as UserBaseInfo} />
)
}
placement="top"
trigger="click"
>
<Avatar
className="cursor-pointer"
size={40}
src={userInfo.avatar}
name={userInfo.nickname}
/>
</Popover>
) : (
<div className="hidden group-hover:block opacity-40">
{formatShortTime(payload.createdAt)}
</div>
</div>
)}
)}
</div>

{/* 消息内容 */}
<AutoFolder
maxHeight={340}
backgroundColor="var(--tc-content-background-color)"
showFullText={
<div className="inline-block rounded-full bg-white dark:bg-black opacity-80 py-2 px-3 hover:opacity-100">
{t('点击展开更多')}
</div>
}
{/* 主体 */}
<div
className="flex flex-col flex-1 overflow-auto group"
onContextMenu={stopPropagation}
>
<div className="chat-message-item_body leading-6 break-words">
<MessageQuote payload={payload} />
{showAvatar && (
<div className="flex items-center">
<div className="font-bold">
{userInfo.nickname ?? <span>&nbsp;</span>}
</div>
<div className="hidden group-hover:block opacity-40 ml-1 text-sm">
{formatShortTime(payload.createdAt)}
</div>
</div>
)}

<span>{getMessageRender(payload.content)}</span>
{/* 消息内容 */}
<AutoFolder
maxHeight={340}
backgroundColor="var(--tc-content-background-color)"
showFullText={
<div className="inline-block rounded-full bg-white dark:bg-black opacity-80 py-2 px-3 hover:opacity-100">
{t('点击展开更多')}
</div>
}
>
<div className="chat-message-item_body leading-6 break-words">
<MessageQuote payload={payload} />

{payload.sendFailed === true && (
<Icon
className="inline-block ml-1"
icon="emojione:cross-mark-button"
/>
)}
<span>{getMessageRender(payload.content)}</span>

{payload.sendFailed === true && (
<Icon
className="inline-block ml-1"
icon="emojione:cross-mark-button"
/>
)}

{/* 解释器按钮 */}
{useRenderPluginMessageInterpreter(payload.content)}
</div>
</AutoFolder>

{/* 解释器按钮 */}
{useRenderPluginMessageInterpreter(payload.content)}
{/* 额外渲染 */}
<div>
{pluginMessageExtraParsers.map((parser) => (
<React.Fragment key={parser.name}>
{parser.render(payload)}
</React.Fragment>
))}
</div>
</AutoFolder>

{/* 额外渲染 */}
<div>
{pluginMessageExtraParsers.map((parser) => (
<React.Fragment key={parser.name}>
{parser.render(payload)}
</React.Fragment>
))}
{/* 消息反应 */}
{reactions}
</div>

{/* 消息反应 */}
{reactions}
</div>

{/* 操作 */}
{!disableOperate && (
<div
className={clsx(
'bg-white dark:bg-black rounded absolute right-2 cursor-pointer -top-3 shadow-sm flex',
{
'opacity-0 group-hover:opacity-100 bg-opacity-80 hover:bg-opacity-100':
!isActionBtnActive,
'opacity-100 bg-opacity-100': isActionBtnActive,
}
)}
>
<TcPopover
overlayClassName="chat-message-item_action-popover"
content={emojiAction}
placement="bottomLeft"
trigger={['click']}
onOpenChange={setIsActionBtnActive}
{/* 操作 */}
{!disableOperate && (
<div
className={clsx(
'bg-white dark:bg-black rounded absolute right-2 cursor-pointer -top-3 shadow-sm flex',
{
'opacity-0 group-hover:opacity-100 bg-opacity-80 hover:bg-opacity-100':
!isActionBtnActive,
'opacity-100 bg-opacity-100': isActionBtnActive,
}
)}
>
<div>
<MessageActionIcon icon="mdi:emoticon-happy-outline" />
</div>
</TcPopover>
<TcPopover
overlayClassName="chat-message-item_action-popover"
content={emojiAction}
placement="bottomLeft"
trigger={['click']}
onOpenChange={setIsActionBtnActive}
>
<div>
<MessageActionIcon icon="mdi:emoticon-happy-outline" />
</div>
</TcPopover>

<Dropdown
menu={moreActions}
placement="bottomLeft"
trigger={['click']}
onOpenChange={setIsActionBtnActive}
>
<div>
<MessageActionIcon icon="mdi:dots-horizontal" />
</div>
</Dropdown>
</div>
)}
</div>
);
});
<Dropdown
menu={moreActions}
placement="bottomLeft"
trigger={['click']}
onOpenChange={setIsActionBtnActive}
>
<div>
<MessageActionIcon icon="mdi:dots-horizontal" />
</div>
</Dropdown>
</div>
)}
</div>
);
}
);
NormalMessage.displayName = 'NormalMessage';

/**
Expand Down Expand Up @@ -250,6 +254,7 @@ SystemMessageWithNickname.displayName = 'SystemMessageWithNickname';
interface ChatMessageItemProps {
showAvatar: boolean;
payload: LocalChatMessage;
hideAction?: boolean;
}
const ChatMessageItem: React.FC<ChatMessageItemProps> = React.memo((props) => {
const payload = props.payload;
Expand Down
Loading

0 comments on commit 176528b

Please sign in to comment.