diff --git a/src/components/views/rooms/MessageComposerButtons.tsx b/src/components/views/rooms/MessageComposerButtons.tsx index 6375519621e..0dbeacc7195 100644 --- a/src/components/views/rooms/MessageComposerButtons.tsx +++ b/src/components/views/rooms/MessageComposerButtons.tsx @@ -70,7 +70,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => { emojiButton(props), ]; moreButtons = [ - uploadButton(props, roomId), + uploadButton(), // props passed via UploadButtonContext showStickersButton(props), voiceRecordingButton(props, narrow), pollButton(room, props.relation), @@ -79,7 +79,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => { } else { mainButtons = [ emojiButton(props), - uploadButton(props, roomId), + uploadButton(), // props passed via UploadButtonContext ]; moreButtons = [ showStickersButton(props), @@ -98,7 +98,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => { mx_MessageComposer_closeButtonMenu: props.isMenuOpen, }); - return <> + return <UploadButtonContextProvider roomId={roomId} relation={props.relation}> { mainButtons } <AccessibleTooltipButton className={moreOptionsClasses} @@ -116,7 +116,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => { </OverflowMenuContext.Provider> </ContextMenu> ) } - </>; + </UploadButtonContextProvider>; }; function emojiButton(props: IProps): ReactElement { @@ -175,23 +175,22 @@ const EmojiButton: React.FC<IEmojiButtonProps> = ({ addEmoji, menuPosition }) => </React.Fragment>; }; -function uploadButton(props: IProps, roomId: string): ReactElement { - return <UploadButton - key="controls_upload" - roomId={roomId} - relation={props.relation} - />; +function uploadButton(): ReactElement { + return <UploadButton key="controls_upload" />; } +type UploadButtonFn = () => void; +export const UploadButtonContext = createContext<UploadButtonFn | null>(null); + interface IUploadButtonProps { roomId: string; relation?: IEventRelation | null; } -const UploadButton = ({ roomId, relation }: IUploadButtonProps) => { +// We put the file input outside the UploadButton component so that it doesn't get killed when the context menu closes. +const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, relation, children }) => { const cli = useContext(MatrixClientContext); const roomContext = useContext(RoomContext); - const overflowMenuCloser = useContext(OverflowMenuContext); const uploadInput = useRef<HTMLInputElement>(); const onUploadClick = () => { @@ -200,7 +199,6 @@ const UploadButton = ({ roomId, relation }: IUploadButtonProps) => { return; } uploadInput.current?.click(); - overflowMenuCloser?.(); // close overflow menu }; useDispatcher(dis, payload => { @@ -229,12 +227,9 @@ const UploadButton = ({ roomId, relation }: IUploadButtonProps) => { }; const uploadInputStyle = { display: 'none' }; - return <> - <CollapsibleButton - className="mx_MessageComposer_button mx_MessageComposer_upload" - onClick={onUploadClick} - title={_t('Attachment')} - /> + return <UploadButtonContext.Provider value={onUploadClick}> + { children } + <input ref={uploadInput} type="file" @@ -242,7 +237,24 @@ const UploadButton = ({ roomId, relation }: IUploadButtonProps) => { multiple onChange={onUploadFileInputChange} /> - </>; + </UploadButtonContext.Provider>; +}; + +// Must be rendered within an UploadButtonContextProvider +const UploadButton = () => { + const overflowMenuCloser = useContext(OverflowMenuContext); + const uploadButtonFn = useContext(UploadButtonContext); + + const onClick = () => { + uploadButtonFn?.(); + overflowMenuCloser?.(); // close overflow menu + }; + + return <CollapsibleButton + className="mx_MessageComposer_button mx_MessageComposer_upload" + onClick={onClick} + title={_t('Attachment')} + />; }; function showStickersButton(props: IProps): ReactElement {