From c84c92446c793919b67e6fe33b081e6ae634752d Mon Sep 17 00:00:00 2001 From: Keith Smith Date: Sun, 16 Aug 2026 11:03:01 -0600 Subject: [PATCH] Flip reaction emoji picker upward when it won't fit below Reacting to a message near the bottom of the scrolled list opened a picker that ran off-screen and couldn't be used -- placement was hardcoded to "below" regardless of the trigger's actual position. Now computed per-click from the trigger's bounding rect against available viewport space, matching the composer's own picker (which was already positioned dynamically, just always "above" since it's pinned to the bottom of the screen). Co-Authored-By: Claude Sonnet 5 --- frontend/src/components/EmojiPicker.tsx | 5 +++++ frontend/src/components/MessageList.tsx | 20 +++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/EmojiPicker.tsx b/frontend/src/components/EmojiPicker.tsx index 0bf9324..828b43d 100644 --- a/frontend/src/components/EmojiPicker.tsx +++ b/frontend/src/components/EmojiPicker.tsx @@ -11,6 +11,11 @@ interface EmojiPickerProps { align?: 'left' | 'right' } +// Kept in sync with .emoji-picker's max-height in EmojiPicker.css -- callers +// that compute placement dynamically (flipping above/below based on +// available viewport space) need this to know how much room to check for. +export const EMOJI_PICKER_MAX_HEIGHT = 380 + function searchEmoji(query: string): string[] { const q = query.trim().toLowerCase() if (!q) return [] diff --git a/frontend/src/components/MessageList.tsx b/frontend/src/components/MessageList.tsx index 7d37733..762e6c1 100644 --- a/frontend/src/components/MessageList.tsx +++ b/frontend/src/components/MessageList.tsx @@ -3,7 +3,7 @@ import { getRoomFileUrl, getRoomImageUrl } from '../api/rooms' import { useAuth } from '../context/AuthContext' import { avatarUrlFor, displayNameFor, senderColorIndex } from '../lib/messageGrouping' import type { ChatMessageEnvelope, Message, MessageFileInfo, RoomMember } from '../types' -import { EmojiPicker } from './EmojiPicker' +import { EMOJI_PICKER_MAX_HEIGHT, EmojiPicker } from './EmojiPicker' import { FilePreviewModal, getPreviewKind } from './FilePreviewModal' import { ImageLightbox } from './ImageLightbox' import { MessageContent } from './MessageContent' @@ -79,6 +79,7 @@ export function MessageList({ roomId, messages, members, onEdit, onReact }: Mess const [draft, setDraft] = useState('') const [lightboxSrc, setLightboxSrc] = useState(null) const [reactingId, setReactingId] = useState(null) + const [reactionPlacement, setReactionPlacement] = useState<'above' | 'below'>('below') const [previewFile, setPreviewFile] = useState(null) function displayNameForUserId(userId: string): string { @@ -200,7 +201,20 @@ export function MessageList({ roomId, messages, members, onEdit, onReact }: Mess