From be71931a815f39c0eb390b946a79917d27671a49 Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Sat, 17 Oct 2026 23:40:58 -0400 Subject: [PATCH] fix(forward): warn when forwarding out of an encrypted room into one that is not (#330) Forwarding from an encrypted room into an unencrypted one posts the text (and a re-uploaded plaintext attachment) where the server can read it, with the original author attached, and the dialog said nothing. Show a note above Send naming the unencrypted destination(s). Refs #330 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5 --- .../room/message/ForwardMessageDialog.tsx | 22 +++++++++++++++++-- .../room/message/forwardContent.test.ts | 15 ++++++++++++- .../features/room/message/forwardContent.ts | 20 +++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/app/features/room/message/ForwardMessageDialog.tsx b/src/app/features/room/message/ForwardMessageDialog.tsx index ef6f745e2..892a92ec2 100644 --- a/src/app/features/room/message/ForwardMessageDialog.tsx +++ b/src/app/features/room/message/ForwardMessageDialog.tsx @@ -47,8 +47,12 @@ import { recentForwardTargetsAtom, addRecentForwardTarget, } from '../../../state/recentForwardTargets'; -import { buildForwardContent, buildPlaintextAttachmentContent } from './forwardContent'; -import { useLocalRoomNames } from '../../../hooks/useRoomMeta'; +import { + buildForwardContent, + buildPlaintextAttachmentContent, + forwardEncryptionWarning, +} from './forwardContent'; +import { useLocalRoomNames, getLocalRoomName } from '../../../hooks/useRoomMeta'; type RoomRowProps = { room: Room; @@ -275,6 +279,14 @@ export function ForwardMessageDialog({ mEvent, onClose }: Props) { // Selection persists across query changes: a room selected then filtered out // of the rendered slice stays selected. const [selectedRoomIds, setSelectedRoomIds] = useState>(new Set()); + const encryptionWarning = forwardEncryptionWarning( + !!mx.getRoom(mEvent.getRoomId() ?? '')?.hasEncryptionStateEvent(), + [...selectedRoomIds] + .map((id) => mx.getRoom(id)) + .filter((r): r is Room => !!r && !r.hasEncryptionStateEvent()) + // The name the user sees (their own "Rename for me" name, if set). + .map((r) => getLocalRoomName(mx, r.roomId) ?? r.name), + ); const toggleRoom = useCallback((roomId: string) => { setSelectedRoomIds((prev) => { @@ -590,7 +602,13 @@ export function ForwardMessageDialog({ mEvent, onClose }: Props) { shrink="No" direction="Column" style={{ padding: `${config.space.S200} ${config.space.S400}` }} + gap="200" > + {encryptionWarning && ( + + {encryptionWarning} + + )}