From e2bbc429149b4bc027788975a3dc97c1311c6eea Mon Sep 17 00:00:00 2001 From: Gigiaj Date: Sun, 15 Jun 2025 16:05:33 -0500 Subject: [PATCH] Pull out menu options and the dropdown into their own components for reuse --- src/app/features/room/MessageOptionsMenu.tsx | 273 +++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 src/app/features/room/MessageOptionsMenu.tsx diff --git a/src/app/features/room/MessageOptionsMenu.tsx b/src/app/features/room/MessageOptionsMenu.tsx new file mode 100644 index 00000000..43e5b751 --- /dev/null +++ b/src/app/features/room/MessageOptionsMenu.tsx @@ -0,0 +1,273 @@ +import { Box, Icon, IconButton, Icons, Line, Menu, MenuItem, PopOut, RectCords, Text } from 'folds'; +import React, { MouseEventHandler, useEffect, useState } from 'react'; + +import FocusTrap from 'focus-trap-react'; +import { EmojiBoard } from '../../components/emoji-board'; +import { stopPropagation } from '../../utils/keyboard'; +import * as css from './message/styles.css'; + +import { + MessageAllReactionItem, + MessageCopyLinkItem, + MessageDeleteItem, + MessagePinItem, + MessageQuickReactions, + MessageReadReceiptItem, + MessageReportItem, + MessageSourceCodeItem, +} from './message/Message'; + +export function MessageDropdownMenu({ + mEvent, + room, + mx, + relations, + eventId, + canSendReaction, + canEdit, + canDelete, + canPinEvent, + hideReadReceipts, + onReactionToggle, + onReplyClick, + onEditId, + handleAddReactions, + closeMenu, +}) { + return ( + + {canSendReaction && ( + { + onReactionToggle(eventId, key, shortcode); + closeMenu(); + }} + /> + )} + + {canSendReaction && ( + } + radii="300" + onClick={handleAddReactions} + > + + Add Reaction + + + )} + {relations && ( + + )} + } + radii="300" + data-event-id={eventId} + onClick={(evt) => { + onReplyClick(evt); + closeMenu(); + }} + > + + Reply + + + {canEdit && onEditId && ( + } + radii="300" + data-event-id={eventId} + onClick={() => { + onEditId(eventId); + closeMenu(); + }} + > + + Edit Message + + + )} + {!hideReadReceipts && ( + + )} + + + {canPinEvent && } + + {/* Redact and Report actions */} + {((!mEvent.isRedacted() && canDelete) || mEvent.getSender() !== mx.getUserId()) && ( + <> + + + {!mEvent.isRedacted() && canDelete && ( + + )} + {mEvent.getSender() !== mx.getUserId() && ( + + )} + + + )} + + ); +} + +export function MessageOptionsMenu({ + mEvent, + room, + mx, + relations, + imagePackRooms, + canSendReaction, + canEdit, + canDelete, + canPinEvent, + hideReadReceipts, + onReactionToggle, + onReplyClick, + onEditId, + onActiveStateChange, +}) { + const [menuAnchor, setMenuAnchor] = useState(); + const [emojiBoardAnchor, setEmojiBoardAnchor] = useState(); + + useEffect(() => { + onActiveStateChange?.(!!menuAnchor || !!emojiBoardAnchor); + }, [emojiBoardAnchor, menuAnchor, onActiveStateChange]); + + const eventId = mEvent.getId(); + if (!eventId) return null; + + const closeMenu = () => { + setMenuAnchor(undefined); + }; + + const handleOpenMenu: MouseEventHandler = (evt) => { + const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget; + setMenuAnchor(target.getBoundingClientRect()); + }; + + const handleOpenEmojiBoard: MouseEventHandler = (evt) => { + const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget; + setEmojiBoardAnchor(target.getBoundingClientRect()); + }; + + const handleAddReactions: MouseEventHandler = () => { + const rect = menuAnchor; + closeMenu(); + // Use a timeout to allow the first menu to close before opening the next + setTimeout(() => { + setEmojiBoardAnchor(rect); + }, 100); + }; + return ( +
+ + + {canSendReaction && ( + { + onReactionToggle(eventId, key); + setEmojiBoardAnchor(undefined); + }} + onCustomEmojiSelect={(mxc, shortcode) => { + onReactionToggle(eventId, mxc, shortcode); + setEmojiBoardAnchor(undefined); + }} + requestClose={() => setEmojiBoardAnchor(undefined)} + /> + } + > + + + + + )} + + + + {canEdit && onEditId && ( + onEditId(eventId)} + variant="SurfaceVariant" + size="300" + radii="300" + > + + + )} + setMenuAnchor(undefined), + clickOutsideDeactivates: true, + isKeyForward: (evt) => evt.key === 'ArrowDown', + isKeyBackward: (evt) => evt.key === 'ArrowUp', + escapeDeactivates: stopPropagation, + }} + > + + + } + > + + + + + + +
+ ); +}