fix(chat): voice bubble preview in action menu + popup positioning

- previewNode: add audio case → VoiceNoteBubble renders correctly in blur
  overlay when long-pressing a voice message (was rendering empty/null)
- MessageActionMenu: account for input bar (bottomInset=90) in positioning —
  menu no longer slides behind input bar on bottom messages; barTop clamped
  to never overlap the menu; both above/below paths respect usableH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-06-02 08:44:53 +02:00
parent 603ed9f392
commit 1dc4e4f9cd
2 changed files with 22 additions and 8 deletions

View File

@ -290,10 +290,13 @@ export function ChatBubble({
{ backgroundColor: bubbleBg },
!msg.isOwn && styles.bubbleOtherBorder,
isImageOnly && { padding: 4 },
isAudioMsg && { paddingHorizontal: 6, paddingTop: 6, paddingBottom: 4 },
]}
>
{msg.attachmentUrl && msg.attachmentType === 'image' ? (
<Image source={{ uri: msg.attachmentUrl }} style={styles.image} contentFit="cover" cachePolicy="memory-disk" />
) : msg.attachmentUrl && msg.attachmentType === 'audio' ? (
<VoiceNoteBubble url={msg.attachmentUrl} duration={msg.attachmentName ?? '0:00'} isOwn={msg.isOwn} />
) : msg.content !== '' ? (
<Text style={[styles.content, { color: bubbleText }]}>{msg.content}</Text>
) : null}

View File

@ -92,15 +92,26 @@ export function MessageActionMenu({
const barH = showReactions ? 60 : 0;
const estMenuH = actions.length * 52 + 12;
// Menü unter der Bubble, sonst darüber.
const belowSpace = screenH - (anchor.y + anchor.height);
const placeBelow = belowSpace > estMenuH + 40;
const menuTop = placeBelow
? anchor.y + anchor.height + 10
: Math.max(topSafe + barH + 10, anchor.y - estMenuH - 10);
// Input-Bar + Safe-Area am unteren Bildschirmrand (Popup darf nie darunter)
const bottomInset = 90;
const usableH = screenH - bottomInset;
// Reaktions-Leiste über der Bubble (geclampt unter die Safe-Area).
const barTop = Math.max(topSafe, anchor.y - barH - 4);
// Menü unter der Bubble wenn genug Platz, sonst darüber — Input-Bar berücksichtigt
const belowSpace = usableH - (anchor.y + anchor.height);
const placeBelow = belowSpace >= estMenuH + 8;
const menuTopIdeal = placeBelow
? anchor.y + anchor.height + 8
: anchor.y - estMenuH - 8;
// Clampen: nie über Safe-Area oben, nie unter Input-Bar unten
const menuTop = Math.min(
Math.max(topSafe + barH + 8, menuTopIdeal),
usableH - estMenuH,
);
// Reaktions-Leiste über der Bubble — nie über topSafe, nie das Menü überlappen
const barTopIdeal = Math.max(topSafe, anchor.y - barH - 4);
const barTop = Math.min(barTopIdeal, menuTop - barH - 8);
// Horizontale Ausrichtung an der Bubble-Seite.
const sideStyle = isOwn