feat(captions): edit image/video captions after sending
Captions could be attached at upload but never changed — canEditEvent only allowed m.text/emote/notice, so a typo in an image caption meant delete + re-upload. Add caption editing for image/video messages. - utils/room.ts: canEditCaption (own image/video RoomMessage, no non-thread relation) + canEditEventOrCaption. canEditEvent unchanged. - Message.tsx: gate the Edit affordance (quick-actions + menu) on canEditEventOrCaption; label it "Edit caption" for media; keep the media rendered above the editor while editing. - MessageEditor.tsx: for a media message, seed the editor from the caption (not the filename), allow an empty caption (removes it), and build the m.replace so m.new_content spreads the original media content (url/info/encrypted file/filename/msgtype) and only sets body + format/formatted_body. Outer content is the full media (not a "* text" fallback) so non-edit-aware clients still render the media. No-op guard when the caption is unchanged. Placeholder "Add a caption…". Rendering + Edit History need no changes: getEditedEvent's m.new_content flows to renderCaption, and the word-diff already diffs body (the caption). Encrypted media keeps its file/key (no re-upload). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -50,7 +50,8 @@ import {
|
||||
UsernameBold,
|
||||
} from '../../../components/message';
|
||||
import {
|
||||
canEditEvent,
|
||||
canEditCaption,
|
||||
canEditEventOrCaption,
|
||||
getEventEdits,
|
||||
getMemberAvatarMxc,
|
||||
getMemberName,
|
||||
@@ -903,17 +904,22 @@ export const Message = React.memo(
|
||||
<Box direction="Column" alignSelf="Start" style={{ maxWidth: '100%' }}>
|
||||
{reply}
|
||||
{edit && onEditId ? (
|
||||
<MessageEditor
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: '100vw',
|
||||
}}
|
||||
roomId={room.roomId}
|
||||
room={room}
|
||||
mEvent={mEvent}
|
||||
imagePackRooms={imagePackRooms}
|
||||
onCancel={() => onEditId()}
|
||||
/>
|
||||
<>
|
||||
{/* Editing a media caption: keep the media visible for context; the
|
||||
editor below edits only the caption. */}
|
||||
{canEditCaption(mx, mEvent) && children}
|
||||
<MessageEditor
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: '100vw',
|
||||
}}
|
||||
roomId={room.roomId}
|
||||
room={room}
|
||||
mEvent={mEvent}
|
||||
imagePackRooms={imagePackRooms}
|
||||
onCancel={() => onEditId()}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
@@ -1072,13 +1078,13 @@ export const Message = React.memo(
|
||||
<Icon src={Icons.ThreadPlus} size="100" />
|
||||
</IconButton>
|
||||
)}
|
||||
{canEditEvent(mx, mEvent) && onEditId && (
|
||||
{canEditEventOrCaption(mx, mEvent) && onEditId && (
|
||||
<IconButton
|
||||
onClick={() => onEditId(mEvent.getId())}
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
aria-label="Edit message"
|
||||
aria-label={canEditCaption(mx, mEvent) ? 'Edit caption' : 'Edit message'}
|
||||
>
|
||||
<Icon src={Icons.Pencil} size="100" />
|
||||
</IconButton>
|
||||
@@ -1247,7 +1253,7 @@ export const Message = React.memo(
|
||||
</Text>
|
||||
</MenuItem>
|
||||
)}
|
||||
{canEditEvent(mx, mEvent) && onEditId && (
|
||||
{canEditEventOrCaption(mx, mEvent) && onEditId && (
|
||||
<MenuItem
|
||||
size="300"
|
||||
after={<Icon size="100" src={Icons.Pencil} />}
|
||||
@@ -1264,7 +1270,7 @@ export const Message = React.memo(
|
||||
size="T300"
|
||||
truncate
|
||||
>
|
||||
Edit Message
|
||||
{canEditCaption(mx, mEvent) ? 'Edit Caption' : 'Edit Message'}
|
||||
</Text>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user