新增 pc-client:畅联 PC 端工程(Electron+React,按确认效果图改造,B-59)
- 基于 openim-electron-demo 改造:工号+密码登录(自研账号服务)、会话列表/聊天窗/通讯录界面重做
- 文字/语音/文件/图片消息、文件拖拽发送、表情面板、一对一语音通话(LiveKit)
- RTC 令牌对接账号服务带鉴权版 /api/rtc_token(Bearer imToken + {room,identity})
- account-service: CORS Allow-Headers 补 authorization(浏览器/渲染进程跨域调 rtc_token 需要)
- 附本地 mock 账号服务(scripts/mock-account-server.js)与截图联调脚本
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import type {
|
||||
ConversationItem as ConversationItemType,
|
||||
MessageItem,
|
||||
} from "@openim/wasm-client-sdk/lib/types/entity";
|
||||
import { Badge } from "antd";
|
||||
import clsx from "clsx";
|
||||
import { t } from "i18next";
|
||||
import { memo, useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import OIMAvatar from "@/components/OIMAvatar";
|
||||
import { useConversationStore, useUserStore } from "@/store";
|
||||
import { formatConversionTime, getConversationContent } from "@/utils/imCommon";
|
||||
|
||||
import styles from "./conversation-item.module.scss";
|
||||
|
||||
interface IConversationProps {
|
||||
isActive: boolean;
|
||||
conversation: ConversationItemType;
|
||||
}
|
||||
|
||||
const ConversationItem = ({ isActive, conversation }: IConversationProps) => {
|
||||
const navigate = useNavigate();
|
||||
const updateCurrentConversation = useConversationStore(
|
||||
(state) => state.updateCurrentConversation,
|
||||
);
|
||||
const currentUser = useUserStore((state) => state.selfInfo.userID);
|
||||
|
||||
const toSpecifiedConversation = async () => {
|
||||
if (isActive) {
|
||||
return;
|
||||
}
|
||||
await updateCurrentConversation({ ...conversation });
|
||||
navigate(`/chat/${conversation.conversationID}`);
|
||||
};
|
||||
|
||||
const latestMessageContent = useMemo(() => {
|
||||
let content = "";
|
||||
if (!conversation.latestMsg) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
content = getConversationContent(
|
||||
JSON.parse(conversation.latestMsg) as MessageItem,
|
||||
);
|
||||
} catch (error) {
|
||||
content = t("messageDescription.catchMessage");
|
||||
}
|
||||
return content;
|
||||
}, [conversation.draftText, conversation.latestMsg, isActive, currentUser]);
|
||||
|
||||
const latestMessageTime = formatConversionTime(conversation.latestMsgSendTime);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
styles["conversation-item"],
|
||||
"border border-transparent",
|
||||
isActive ? `bg-[var(--primary-active)]` : "hover:bg-[#F3F5F7]",
|
||||
)}
|
||||
onClick={toSpecifiedConversation}
|
||||
>
|
||||
<OIMAvatar
|
||||
size={48}
|
||||
src={conversation.faceURL}
|
||||
isgroup={Boolean(conversation.groupID)}
|
||||
text={conversation.showName}
|
||||
/>
|
||||
|
||||
<div className="ml-3 flex h-12 flex-1 flex-col justify-between overflow-hidden py-0.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1 truncate text-sm font-medium">
|
||||
{conversation.showName}
|
||||
</div>
|
||||
<div className="ml-2 text-xs text-[var(--sub-text)]">{latestMessageTime}</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex min-h-[18px] flex-1 items-center overflow-hidden">
|
||||
<div
|
||||
className="truncate text-[13px] text-[rgba(81,94,112,0.5)]"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: latestMessageContent,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<Badge
|
||||
className="ml-2"
|
||||
size="small"
|
||||
count={conversation.unreadCount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ConversationItem);
|
||||
Reference in New Issue
Block a user