Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
143 lines
4.3 KiB
TypeScript
143 lines
4.3 KiB
TypeScript
import type {
|
|
ConversationItem as ConversationItemType,
|
|
MessageItem,
|
|
} from "@openim/wasm-client-sdk/lib/types/entity";
|
|
import { Badge, Dropdown } 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 { IMSDK } from "@/layout/MainContentWrap";
|
|
import { useConversationStore, useUserStore } from "@/store";
|
|
import { feedbackToast } from "@/utils/common";
|
|
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);
|
|
|
|
const onMenuClick = async ({ key }: { key: string }) => {
|
|
try {
|
|
if (key === "pin") {
|
|
await IMSDK.pinConversation({
|
|
conversationID: conversation.conversationID,
|
|
isPinned: !conversation.isPinned,
|
|
});
|
|
return;
|
|
}
|
|
if (key === "delete") {
|
|
await IMSDK.deleteConversationAndDeleteAllMsg(conversation.conversationID);
|
|
if (isActive) {
|
|
navigate("/chat");
|
|
}
|
|
}
|
|
} catch (error) {
|
|
feedbackToast({
|
|
error,
|
|
msg:
|
|
key === "pin"
|
|
? t("toast.pinConversationFailed")
|
|
: t("toast.deleteConversationFailed"),
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dropdown
|
|
trigger={["contextMenu"]}
|
|
menu={{
|
|
items: [
|
|
{
|
|
key: "pin",
|
|
label: conversation.isPinned
|
|
? t("placeholder.removeSticky")
|
|
: t("placeholder.sticky"),
|
|
},
|
|
{ key: "delete", label: t("placeholder.delete"), danger: true },
|
|
],
|
|
onClick: onMenuClick,
|
|
}}
|
|
>
|
|
<div
|
|
className={clsx(
|
|
styles["conversation-item"],
|
|
"h-16 border border-transparent transition-colors duration-100",
|
|
isActive
|
|
? "bg-[var(--primary-active)]"
|
|
: "hover:bg-[var(--chat-bg)]",
|
|
)}
|
|
style={{ transitionTimingFunction: "ease-out" }}
|
|
onClick={toSpecifiedConversation}
|
|
>
|
|
<OIMAvatar
|
|
size={44}
|
|
src={conversation.faceURL}
|
|
isgroup={Boolean(conversation.groupID)}
|
|
text={conversation.showName}
|
|
/>
|
|
|
|
<div className="ml-3 flex min-w-0 flex-1 flex-col justify-center overflow-hidden">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex-1 truncate text-base font-bold text-[var(--base-black)]">
|
|
{conversation.showName}
|
|
</div>
|
|
<div className="ml-2 shrink-0 text-xs text-[var(--sub-text)]">
|
|
{latestMessageTime}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-1 flex items-center justify-between">
|
|
<div className="flex min-h-[18px] min-w-0 flex-1 items-center overflow-hidden">
|
|
<div
|
|
className="truncate text-sm text-[var(--sub-text)]"
|
|
dangerouslySetInnerHTML={{
|
|
__html: latestMessageContent,
|
|
}}
|
|
></div>
|
|
</div>
|
|
<Badge className="ml-2 shrink-0" size="small" count={conversation.unreadCount} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Dropdown>
|
|
);
|
|
};
|
|
|
|
export default memo(ConversationItem);
|