feat: add long-press context menus for mobile messages

Create useLongPress hook with 500ms delay, 10px movement cancellation
(Euclidean distance), and ghost click suppression. Integrate into
Message component to trigger the existing context menu bottom sheet
on touch devices.
This commit is contained in:
Jannis Braun
2026-03-23 14:22:38 +01:00
parent 876326e7b7
commit 80c32ae1e6
2 changed files with 139 additions and 1 deletions
+32 -1
View File
@@ -16,6 +16,7 @@ import { Username } from '../ui/Username';
import { EmojiPicker } from './EmojiPicker';
import { hasPermissionBit, PermissionBits } from '../../utils/permissions';
import { isSelf, resolveDisplayIdentity } from '../../utils/identity';
import { useLongPress } from '../../hooks/useLongPress';
interface MessageProps {
message: MessageWithUser;
@@ -201,6 +202,36 @@ export function Message({ message, isCompact, isFirstInGroup, previousMessageId
useContextMenuStore.getState().open({ x: e.clientX, y: e.clientY }, items);
};
const isMobile = useUIStore((s) => s.isMobile);
const longPressHandlers = useLongPress((position) => {
const selectedText = window.getSelection()?.toString() ?? '';
const items = buildMessageMenuItems({
message,
selectedText,
previousMessageId,
isAuthor,
isDm: isDmMessage,
canAddReactions,
canSendMessages,
canManageMessages,
onReply: () => setReplyTo(message),
onEdit: () => {
setEditContent(message.content ?? '');
setIsEditing(true);
},
onDelete: () => deleteMessage(message.id, channelKey),
onReaction: (emoji: string) => toggleReaction(emoji),
onOpenEmojiPicker: () => {
useContextMenuStore.getState().close();
setShowReactionPicker(true);
},
onMarkUnread: (msgId: string) => markUnread(channelKey, msgId),
});
if (items.length === 0) return;
useContextMenuStore.getState().open({ x: position.clientX, y: position.clientY }, items);
});
const handleEditSubmit = async (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
@@ -524,7 +555,7 @@ export function Message({ message, isCompact, isFirstInGroup, previousMessageId
);
return (
<div onContextMenu={handleContextMenu}>
<div onContextMenu={handleContextMenu} {...(isMobile ? longPressHandlers : {})}>
{content}
</div>
);