fix(ai-chat): make the new-chat composer text-only so a sticker or file first move cannot strand the user

This commit is contained in:
heaven 2026-06-04 12:42:09 +03:00
parent 1de93f3c88
commit e66d8cf7bf
2 changed files with 14 additions and 5 deletions

View file

@ -63,6 +63,7 @@ function NewChatComposer({ preset, room }: { preset: BotPreset; room: Room }) {
roomId={room.roomId} roomId={room.roomId}
fileDropContainerRef={fileDropRef} fileDropContainerRef={fileDropRef}
onSend={handleSend} onSend={handleSend}
textOnly
/> />
</div> </div>
</div> </div>

View file

@ -205,9 +205,14 @@ interface RoomInputProps {
// freshly-rooted thread (the just-sent top-level event IS the thread root). // freshly-rooted thread (the just-sent top-level event IS the thread root).
// Channel / DM / thread composers omit it and stay fire-and-forget. // Channel / DM / thread composers omit it and stay fire-and-forget.
onSend?: (eventId: string) => void; onSend?: (eventId: string) => void;
// Text-only mode: hides the file/sticker affordances and no-ops file
// paste/drop. The AI "new chat" landing uses it because only a text send
// navigates into the freshly-rooted thread (onSend) — a sticker/file first
// move would otherwise strand the user on the landing.
textOnly?: boolean;
} }
export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>( export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
({ editor, fileDropContainerRef, roomId, room, threadId, onSend }, ref) => { ({ editor, fileDropContainerRef, roomId, room, threadId, onSend, textOnly }, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const mx = useMatrixClient(); const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication(); const useAuthentication = useMediaAuthentication();
@ -276,6 +281,9 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
const handleFiles = useCallback( const handleFiles = useCallback(
async (files: File[]) => { async (files: File[]) => {
// Text-only composer (AI new-chat landing) ignores every file vector —
// picker, paste and drop all funnel through here.
if (textOnly) return;
setUploadBoard(true); setUploadBoard(true);
const safeFiles = files.map(safeFile); const safeFiles = files.map(safeFile);
const fileItems: TUploadItem[] = []; const fileItems: TUploadItem[] = [];
@ -309,7 +317,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
item: fileItems, item: fileItems,
}); });
}, },
[setSelectedFiles, room] [setSelectedFiles, room, textOnly]
); );
const pickFile = useFilePicker(handleFiles, true); const pickFile = useFilePicker(handleFiles, true);
const handlePaste = useFilePasteHandler(handleFiles); const handlePaste = useFilePasteHandler(handleFiles);
@ -764,7 +772,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
</UploadBoard> </UploadBoard>
)} )}
<Overlay <Overlay
open={dropZoneVisible} open={dropZoneVisible && !textOnly}
backdrop={<OverlayBackdrop />} backdrop={<OverlayBackdrop />}
style={{ pointerEvents: 'none' }} style={{ pointerEvents: 'none' }}
> >
@ -873,9 +881,9 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
gap="200" gap="200"
style={{ padding: `${toRem(2)} ${toRem(8)} ${toRem(4)}` }} style={{ padding: `${toRem(2)} ${toRem(8)} ${toRem(4)}` }}
> >
{plusButton} {!textOnly && plusButton}
<Box grow="Yes" /> <Box grow="Yes" />
{emojiButton} {!textOnly && emojiButton}
{sendButton} {sendButton}
</Box> </Box>
} }