From c4486b19debe70b8bc91899037c818821144dfe8 Mon Sep 17 00:00:00 2001 From: Shea Date: Sun, 21 Jun 2026 00:06:34 +0300 Subject: [PATCH] separate options into pages Signed-off-by: Shea --- .../feat_separate_options_onto_pages.md | 5 + src/app/components/message/modals/Options.tsx | 358 +++++++++--------- src/app/features/room/message/Message.tsx | 2 - 3 files changed, 187 insertions(+), 178 deletions(-) create mode 100644 .changeset/feat_separate_options_onto_pages.md diff --git a/.changeset/feat_separate_options_onto_pages.md b/.changeset/feat_separate_options_onto_pages.md new file mode 100644 index 000000000..799a39f06 --- /dev/null +++ b/.changeset/feat_separate_options_onto_pages.md @@ -0,0 +1,5 @@ +--- +default: minor +--- + +Add new page for when the menu is too full diff --git a/src/app/components/message/modals/Options.tsx b/src/app/components/message/modals/Options.tsx index 127306c8b..a7f2e798c 100644 --- a/src/app/components/message/modals/Options.tsx +++ b/src/app/components/message/modals/Options.tsx @@ -15,6 +15,7 @@ import { PushPin, PushPinSlash, Smiley, + SmileySticker, Star, } from '$components/icons/phosphor'; import { MessageAllReactionItem } from './MessageReactions'; @@ -28,8 +29,7 @@ import { MessageSourceCodeItem } from './MessageSource'; import { MessageForwardItem } from './MessageForward'; import * as css from '$features/room/message/styles.css'; -import { useAtom, useAtomValue, useSetAtom, useStore } from 'jotai'; -import { nicknamesAtom, setNicknameAtom } from '$state/nicknames'; +import { useAtom, useSetAtom, useStore } from 'jotai'; import type { Dispatch, MouseEventHandler, ReactNode, SetStateAction } from 'react'; import { useCallback, useEffect, useState } from 'react'; import { MessageDeleteItem } from './MessageDelete'; @@ -43,6 +43,8 @@ import { useRoomPinnedEvents } from '$hooks/useRoomPinnedEvents'; import { EmojiBoard } from '$components/emoji-board'; import { MemoizedBody, type ReactionHandler } from '$features/room/message'; import { useRecentEmoji } from '$hooks/useRecentEmoji'; +import { CaretDoubleRightIcon, CopySimpleIcon } from '@phosphor-icons/react'; +import { M_TEXT } from 'matrix-js-sdk'; function WrappedMessage({ isModal, @@ -66,9 +68,10 @@ function WrappedMessage({ type MessageQuickReactionsProps = { onReaction: ReactionHandler; count: number; + handleOpenEmojiBoard: MouseEventHandler; }; export const MessageQuickReactions = as<'div', MessageQuickReactionsProps>( - ({ onReaction, count, ...props }, ref) => { + ({ onReaction, count, handleOpenEmojiBoard, ...props }, ref) => { const mx = useMatrixClient(); const recentEmojis = useRecentEmoji(mx, count); @@ -83,6 +86,19 @@ export const MessageQuickReactions = as<'div', MessageQuickReactionsProps>( {...props} ref={ref} > + + {/*the simple Smiley crept me out to stare at me straight next to the cursor*/} + {menuIcon(SmileySticker)} + {recentEmojis.map((emoji) => ( ( ); } ); +const MessageCopyOriginalItem = as< + 'button', + { + room: Room; + mEvent: MatrixEvent; + onClose: () => void; + } +>(({ room, mEvent, onClose, ...props }, ref) => { + const handleCopy = () => { + const content = mEvent.getContent(); + if (!content) return; + const body = content.body ?? content[M_TEXT.name]; + if (!body) return; + copyToClipboard(body); + onClose(); + }; + + return ( + + + Copy Original + + + ); +}); + const MessageCopyLinkItem = as< 'button', { @@ -248,7 +297,6 @@ export function OptionQuickMenu({ hideReadReceipts, showDeveloperTools, canPinEvent, - cleanedDisplayName, canDelete, handleOpenMenu, menuAnchor, @@ -350,7 +398,6 @@ export function OptionQuickMenu({ hideReadReceipts={hideReadReceipts} showDeveloperTools={showDeveloperTools} canPinEvent={canPinEvent} - cleanedDisplayName={cleanedDisplayName} canDelete={canDelete} setIsEmoji={setIsEmoji} emojiBoardAnchor={menuAnchor} @@ -390,7 +437,6 @@ export type OptionMenuProps = { hideReadReceipts?: boolean; showDeveloperTools?: boolean; canPinEvent?: boolean; - cleanedDisplayName?: string; canDelete?: boolean; handleOpenMenu?: MouseEventHandler; menuAnchor?: RectCords | undefined; @@ -414,7 +460,6 @@ export function OptionMenu({ hideReadReceipts, showDeveloperTools, canPinEvent, - cleanedDisplayName, canDelete, imagePackRooms, setIsEmoji, @@ -433,16 +478,14 @@ export function OptionMenu({ getEventEdits(evtTimeline.getTimelineSet(), evtId, mEvent.getType())?.getRelations(); const isEdited = !!edits?.length; - const [nickEditOpen, setNickEditOpen] = useState(false); - const [nickDraft, setNickDraft] = useState(''); - const nicknames = useAtomValue(nicknamesAtom); - const setNickname = useSetAtom(setNicknameAtom); - const senderId = mEvent.getSender() ?? ''; - const onTotalClose = () => { setModal(null); closeMenu(); }; + const [showMore, setShowMore] = useState(false); + // Written like this to make it easier to change the amount of items required and which they should be + const overflow = + ((!!relations && 1) || 0) + ((canPinEvent && 1) || 0) + ((!hideReadReceipts && 1) || 0) >= 2; const handlePostDeactivate = useCallback(() => { const modal = store.get(modalAtom); @@ -511,194 +554,158 @@ export function OptionMenu({ onReactionToggle(mEvent.getId() ?? '', key, shortcode); onTotalClose(); }} - count={isModal ? 6 : 4} + handleOpenEmojiBoard={handleOpenEmojiBoard} + count={isModal ? 5 : 3} /> )} - {canSendReaction && onReactionToggle && handleOpenEmojiBoard && ( - - - Add Reaction - - - )} - {/* Only show "Add to User Sticker Pack" if the sticker isn't already in the default pack and isn't encrypted */} - {isStickerMessage && - mEvent.getContent().url && - !doesStickerExistInDefaultPack(mx, mEvent.getContent().url) && ( + {!showMore ? ( + <> + {/* Only show "Add to User Sticker Pack" if the sticker isn't already in the default pack and isn't encrypted */} + {isStickerMessage && + mEvent.getContent().url && + !doesStickerExistInDefaultPack(mx, mEvent.getContent().url) && ( + { + addStickerToDefaultPack( + mx, + `sticker-${mEvent.getId()}`, + mEvent.getContent().url ?? mEvent.getContent().file?.url ?? '', + mEvent.getContent().body, + mEvent.getContent().info + ); + onTotalClose(); + }} + > + + Steal Sticker + + + )} + {relations && !overflow && ( + + )} { - addStickerToDefaultPack( - mx, - `sticker-${mEvent.getId()}`, - mEvent.getContent().url ?? mEvent.getContent().file?.url ?? '', - mEvent.getContent().body, - mEvent.getContent().info - ); + data-event-id={mEvent.getId()} + onClick={(evt) => { + onReplyClick(evt); onTotalClose(); }} > - Add to User Sticker Pack + Reply - )} - {relations && } - { - onReplyClick(evt); - onTotalClose(); - }} - > - - Reply - - - {!isThreadedMessage && ( - { - onReplyClick(evt, true); - onTotalClose(); - }} - > - - Reply in Thread - - + {!isThreadedMessage && ( + { + onReplyClick(evt, true); + onTotalClose(); + }} + > + + Reply in Thread + + + )} + {canEditEvent(mx, mEvent) && onEditId && ( + { + onEditId(mEvent.getId()); + onTotalClose(); + }} + > + + Edit Message + + + )} + {!hideReadReceipts && !overflow && ( + + )} + {isEdited && ( + + )} + {showDeveloperTools && ( + + )} + + {!overflow && ( + + )} + {canForwardEvent(mEvent) && ( + + )} + {canPinEvent && !overflow && ( + + )} + + ) : ( + <> + {relations && } + + {!hideReadReceipts && ( + + )} + + + {canPinEvent && ( + + )} + )} - {canEditEvent(mx, mEvent) && onEditId && ( + + {overflow && ( { - onEditId(mEvent.getId()); - onTotalClose(); + setShowMore(!showMore); }} > - Edit Message + Next Page )} - {!hideReadReceipts && ( - - )} - {isEdited && ( - - )} - {showDeveloperTools && ( - - )} - - {canForwardEvent(mEvent) && ( - - )} - {canPinEvent && } - {cleanedDisplayName && - senderId !== mx.getUserId() && - (nickEditOpen ? ( - - Nickname - setNickDraft(e.target.value)} - placeholder={cleanedDisplayName} - onKeyDown={(e) => { - if (e.key === 'Enter') { - setNickname(senderId, nickDraft || undefined, mx); - closeMenu(); - } - if (e.key === 'Escape') closeMenu(); - }} - className={css.MessageNickEditor} - /> - - { - setNickname(senderId, nickDraft || undefined, mx); - closeMenu(); - }} - > - Save - - {nicknames[senderId] && ( - { - setNickname(senderId, undefined, mx); - onTotalClose(); - }} - > - Clear - - )} - + {((!mEvent.isRedacted() && canDelete) || mEvent.getSender() !== mx.getUserId()) && ( + <> + + + {!mEvent.isRedacted() && canDelete && ( + + )} + {mEvent.getSender() !== mx.getUserId() && ( + + )} - ) : ( - { - setNickDraft(nicknames[senderId] ?? ''); - setNickEditOpen(true); - }} - > - - {nicknames[senderId] ? 'Edit Nickname' : 'Set Nickname'} - - - ))} + + )} - {((!mEvent.isRedacted() && canDelete) || mEvent.getSender() !== mx.getUserId()) && ( - <> - - - {!mEvent.isRedacted() && canDelete && ( - - )} - {mEvent.getSender() !== mx.getUserId() && ( - - )} - - - )} @@ -731,7 +738,6 @@ export function MobileOptionsInternal({ options }: { options: OptionMenuProps }) hideReadReceipts={options.hideReadReceipts} showDeveloperTools={options.showDeveloperTools} canPinEvent={options.canPinEvent} - cleanedDisplayName={options.cleanedDisplayName} canDelete={options.canDelete} setIsEmoji={options.setIsEmoji} ActualMessage={options.ActualMessage} diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 89c4a867d..dccc714ee 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -818,7 +818,6 @@ function MessageInternal( hideReadReceipts: hideReadReceipts, showDeveloperTools: showDeveloperTools, canPinEvent: canPinEvent, - cleanedDisplayName: cleanedDisplayName, canDelete: canDelete, setIsEmoji: setIsEmoji, ActualMessage: ( @@ -914,7 +913,6 @@ function MessageInternal( hideReadReceipts={hideReadReceipts} showDeveloperTools={showDeveloperTools} canPinEvent={canPinEvent} - cleanedDisplayName={cleanedDisplayName} canDelete={canDelete} handleOpenMenu={handleOpenMenu} menuAnchor={menuAnchor}