feat(message): add "Copy Text" context-menu action
The message menu had Copy Link (permalink) but no way to copy the message text itself. Add a Copy Text item that copies the plain-text body with the reply fallback stripped (trimReplyFromBody). It renders nothing when there is no usable text body (e.g. media without a caption), so the caller can list it unconditionally next to Copy Link. Uses Icons.Text. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -683,6 +683,10 @@ The indicator is hidden once the server confirms the event (when the internal st
|
|||||||
|
|
||||||
Context menu → **Forward** allows forwarding a message to any room the user is a member of.
|
Context menu → **Forward** allows forwarding a message to any room the user is a member of.
|
||||||
|
|
||||||
|
### Copy Message Text
|
||||||
|
|
||||||
|
Context menu → **Copy Text** copies a message's plain-text body to the clipboard (reply fallback stripped via `trimReplyFromBody`), complementing the existing **Copy Link** (permalink) action. It renders only when the event has a usable text body, so media without a caption doesn't show an empty action.
|
||||||
|
|
||||||
### Draft Persistence
|
### Draft Persistence
|
||||||
|
|
||||||
- Composer drafts are stored in `localStorage` keyed by `roomId`
|
- Composer drafts are stored in `localStorage` keyed by `roomId`
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
getMemberAvatarMxc,
|
getMemberAvatarMxc,
|
||||||
getMemberName,
|
getMemberName,
|
||||||
sendStateEvent,
|
sendStateEvent,
|
||||||
|
trimReplyFromBody,
|
||||||
} from '../../../utils/room';
|
} from '../../../utils/room';
|
||||||
import { mxcUrlToHttp } from '../../../utils/matrix';
|
import { mxcUrlToHttp } from '../../../utils/matrix';
|
||||||
import { messageAriaLabel } from '../../../utils/a11y';
|
import { messageAriaLabel } from '../../../utils/a11y';
|
||||||
@@ -411,6 +412,43 @@ export const MessageCopyLinkItem = as<
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Copies the message's plain-text body (reply fallback stripped) to the
|
||||||
|
// clipboard. Renders nothing for events without a usable text body (e.g. media
|
||||||
|
// without a caption), so the caller can list it unconditionally.
|
||||||
|
export const MessageCopyTextItem = as<
|
||||||
|
'button',
|
||||||
|
{
|
||||||
|
mEvent: MatrixEvent;
|
||||||
|
onClose?: () => void;
|
||||||
|
}
|
||||||
|
>(({ mEvent, onClose, ...props }, ref) => {
|
||||||
|
const content = mEvent.getContent();
|
||||||
|
const rawBody = typeof content.body === 'string' ? content.body : '';
|
||||||
|
const body = trimReplyFromBody(rawBody).trim();
|
||||||
|
|
||||||
|
if (!body) return null;
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
copyToClipboard(body);
|
||||||
|
onClose?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
size="300"
|
||||||
|
after={<Icon size="100" src={Icons.Text} />}
|
||||||
|
radii="300"
|
||||||
|
onClick={handleCopy}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<Text className={css.MessageMenuItemText} as="span" size="T300" truncate>
|
||||||
|
Copy Text
|
||||||
|
</Text>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export const MessagePinItem = as<
|
export const MessagePinItem = as<
|
||||||
'button',
|
'button',
|
||||||
{
|
{
|
||||||
@@ -1284,6 +1322,7 @@ export const Message = React.memo(
|
|||||||
onClose={closeMenu}
|
onClose={closeMenu}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<MessageCopyTextItem mEvent={mEvent} onClose={closeMenu} />
|
||||||
<MessageCopyLinkItem room={room} mEvent={mEvent} onClose={closeMenu} />
|
<MessageCopyLinkItem room={room} mEvent={mEvent} onClose={closeMenu} />
|
||||||
{canPinEvent && (
|
{canPinEvent && (
|
||||||
<MessagePinItem room={room} mEvent={mEvent} onClose={closeMenu} />
|
<MessagePinItem room={room} mEvent={mEvent} onClose={closeMenu} />
|
||||||
@@ -1513,6 +1552,7 @@ export const Event = React.memo(
|
|||||||
onClose={closeMenu}
|
onClose={closeMenu}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<MessageCopyTextItem mEvent={mEvent} onClose={closeMenu} />
|
||||||
<MessageCopyLinkItem room={room} mEvent={mEvent} onClose={closeMenu} />
|
<MessageCopyLinkItem room={room} mEvent={mEvent} onClose={closeMenu} />
|
||||||
</Box>
|
</Box>
|
||||||
{((!mEvent.isRedacted() && canDelete && !stateEvent) ||
|
{((!mEvent.isRedacted() && canDelete && !stateEvent) ||
|
||||||
|
|||||||
Reference in New Issue
Block a user