fix(chat): stabilize message identities

This commit is contained in:
2026-07-20 18:30:21 +08:00
parent 159e8bfd59
commit 1e13f94b5d
33 changed files with 718 additions and 212 deletions
+23 -14
View File
@@ -8,32 +8,39 @@ import styles from "./chat-area.module.css";
export interface MessageContentProps {
characterId: string;
displayMessageId: string;
remoteMessageId?: string;
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;
onUnlockImageMessage?: (messageId: string) => void;
onOpenImage?: (messageId: string) => void;
onUnlockPrivateMessage?: ChatMessageAction;
onUnlockVoiceMessage?: ChatMessageAction;
onUnlockImageMessage?: ChatMessageAction;
onOpenImage?: (displayMessageId: string) => void;
}
type ChatMessageAction = (
displayMessageId: string,
remoteMessageId?: string,
) => void;
const IMAGE_PLACEHOLDER = "[图片]";
export function MessageContent({
characterId,
displayMessageId,
remoteMessageId,
content,
imageUrl,
imagePaywalled,
audioUrl,
messageId,
isFromAI,
locked,
lockReason,
@@ -57,16 +64,17 @@ export function MessageContent({
(lockReason === "image_paywall" || lockReason === "image");
const shouldRenderVoiceMessage = hasAudio || isLockedVoiceMessage;
const handleUnlockPrivateMessage =
messageId && onUnlockPrivateMessage
? () => onUnlockPrivateMessage(messageId)
onUnlockPrivateMessage
? () =>
onUnlockPrivateMessage(displayMessageId, remoteMessageId)
: undefined;
const handleUnlockVoiceMessage =
isLockedVoiceMessage && messageId && onUnlockVoiceMessage
? () => onUnlockVoiceMessage(messageId)
isLockedVoiceMessage && onUnlockVoiceMessage
? () => onUnlockVoiceMessage(displayMessageId, remoteMessageId)
: undefined;
const handleUnlockImageMessage =
isLockedImageMessage && messageId && onUnlockImageMessage
? () => onUnlockImageMessage(messageId)
isLockedImageMessage && onUnlockImageMessage
? () => onUnlockImageMessage(displayMessageId, remoteMessageId)
: undefined;
return (
@@ -93,7 +101,8 @@ export function MessageContent({
{hasImage && imageUrl && (
<ImageBubble
characterId={characterId}
messageId={messageId}
displayMessageId={displayMessageId}
remoteMessageId={remoteMessageId}
imageUrl={imageUrl}
imagePaywalled={imagePaywalled}
onOpenImage={onOpenImage}
@@ -102,7 +111,7 @@ export function MessageContent({
{shouldRenderVoiceMessage ? (
<VoiceBubble
characterId={characterId}
messageId={messageId}
remoteMessageId={remoteMessageId}
audioUrl={audioUrl}
isFromAI={isFromAI}
locked={isLockedVoiceMessage}