Files
tongxunruanjian/pc-client/src/pages/chat/ConversationSider/ConversationItem.tsx
T

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);