Files
cozsweet-frontend-nextjs/src/app/chat/components/message-content.tsx
T

102 lines
3.0 KiB
TypeScript

"use client";
import { ImageBubble } from "./image-bubble";
import { PrivateMessageCard } from "./private-message-card";
import { TextBubble } from "./text-bubble";
import { VoiceBubble } from "./voice-bubble";
import styles from "./chat-area.module.css";
export interface MessageContentProps {
content: string;
imageUrl?: string | null;
imagePaywalled?: boolean;
audioUrl?: string | null;
messageId?: string;
isFromAI: boolean;
locked?: boolean | null;
lockReason?: string | null;
lockedPrivate?: boolean | null;
privateMessageHint?: string | null;
isUnlockingMessage?: boolean;
onUnlockPrivateMessage?: (messageId: string) => void;
onUnlockVoiceMessage?: (messageId: string) => void;
onOpenImage?: (messageId: string) => void;
}
const IMAGE_PLACEHOLDER = "[图片]";
export function MessageContent({
content,
imageUrl,
imagePaywalled,
audioUrl,
messageId,
isFromAI,
locked,
lockReason,
lockedPrivate,
privateMessageHint,
isUnlockingMessage,
onUnlockPrivateMessage,
onUnlockVoiceMessage,
onOpenImage,
}: MessageContentProps) {
const hasImage = imageUrl != null && imageUrl.length > 0;
const hasAudio = audioUrl != null && audioUrl.length > 0;
const hasText = content.length > 0 && content !== IMAGE_PLACEHOLDER;
const isLockedPrivateMessage =
lockedPrivate === true ||
(locked === true && lockReason === "private_message");
const isLockedVoiceMessage = locked === true && lockReason === "voice_message";
const shouldRenderVoiceMessage = hasAudio || isLockedVoiceMessage;
const handleUnlockPrivateMessage =
messageId && onUnlockPrivateMessage
? () => onUnlockPrivateMessage(messageId)
: undefined;
const handleUnlockVoiceMessage =
isLockedVoiceMessage && messageId && onUnlockVoiceMessage
? () => onUnlockVoiceMessage(messageId)
: undefined;
return (
<div
className={[
styles.messageContent,
isFromAI ? styles.messageContentAi : styles.messageContentUser,
].join(" ")}
>
{isLockedPrivateMessage ? (
<PrivateMessageCard
hint={privateMessageHint}
isUnlocking={isUnlockingMessage}
onUnlock={handleUnlockPrivateMessage}
/>
) : (
<>
{hasImage && imageUrl && (
<ImageBubble
messageId={messageId}
imageUrl={imageUrl}
imagePaywalled={imagePaywalled}
onOpenImage={onOpenImage}
/>
)}
{shouldRenderVoiceMessage ? (
<VoiceBubble
messageId={messageId}
audioUrl={audioUrl}
isFromAI={isFromAI}
locked={isLockedVoiceMessage}
hint={privateMessageHint}
isUnlocking={isUnlockingMessage}
onUnlock={handleUnlockVoiceMessage}
/>
) : null}
{hasText && !shouldRenderVoiceMessage ? (
<TextBubble content={content} isFromAI={isFromAI} />
) : null}
</>
)}
</div>
);
}