83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
"use client";
|
||
/**
|
||
* MessageContent 消息内容容器
|
||
*
|
||
* 决定渲染 ImageBubble 还是 TextBubble:
|
||
* - 有 imageUrl:渲染 ImageBubble(图片)
|
||
* - 有 content 且非 "[图片]" 占位符:渲染 TextBubble(文字)
|
||
* - 两者都有:图片在上,文字在下
|
||
*/
|
||
import { ImageBubble } from "./image-bubble";
|
||
import { PrivateMessageCard } from "./private-message-card";
|
||
import { TextBubble } from "./text-bubble";
|
||
|
||
export interface MessageContentProps {
|
||
messageId?: string;
|
||
content: string;
|
||
imageUrl?: string | null;
|
||
imagePaywalled?: boolean;
|
||
isFromAI: boolean;
|
||
privateLocked?: boolean | null;
|
||
privateHint?: string | null;
|
||
isUnlockingPrivate?: boolean;
|
||
onUnlockPrivateMessage?: (messageId: string) => void;
|
||
onUnlockImagePaywall?: () => void;
|
||
}
|
||
|
||
const IMAGE_PLACEHOLDER = "[图片]";
|
||
|
||
export function MessageContent({
|
||
messageId,
|
||
content,
|
||
imageUrl,
|
||
imagePaywalled,
|
||
isFromAI,
|
||
privateLocked,
|
||
privateHint,
|
||
isUnlockingPrivate = false,
|
||
onUnlockPrivateMessage,
|
||
onUnlockImagePaywall,
|
||
}: MessageContentProps) {
|
||
const hasImage = imageUrl != null && imageUrl.length > 0;
|
||
const hasText = content.length > 0 && content !== IMAGE_PLACEHOLDER;
|
||
const isLockedPrivateMessage = privateLocked === true;
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
// 关键:在 row flex 父中撑开(不依赖 content 循环)—— 让 TextBubble 能拿到真实宽度
|
||
flex: "1 1 0",
|
||
// 防超长 content(图片 / 超长 url)撑爆父
|
||
minWidth: 0,
|
||
alignItems: isFromAI ? "flex-start" : "flex-end",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
{isLockedPrivateMessage ? (
|
||
<PrivateMessageCard
|
||
hint={privateHint}
|
||
isUnlocking={isUnlockingPrivate}
|
||
onUnlock={
|
||
messageId
|
||
? () => onUnlockPrivateMessage?.(messageId)
|
||
: undefined
|
||
}
|
||
/>
|
||
) : (
|
||
<>
|
||
{hasImage && imageUrl && (
|
||
<ImageBubble
|
||
imageUrl={imageUrl}
|
||
imagePaywalled={imagePaywalled}
|
||
onUnlockImagePaywall={onUnlockImagePaywall}
|
||
/>
|
||
)}
|
||
{hasText && <TextBubble content={content} isFromAI={isFromAI} />}
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|