Refactor MessageComposerButtons (#7668)

pull/21833/head
Andy Balaam 2022-01-31 09:19:26 +00:00 committed by GitHub
parent 7c20eb9b74
commit b5a56698d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 82 deletions

View File

@ -17,7 +17,7 @@ limitations under the License.
import classNames from 'classnames'; import classNames from 'classnames';
import { IEventRelation } from "matrix-js-sdk/src/models/event"; import { IEventRelation } from "matrix-js-sdk/src/models/event";
import { M_POLL_START } from "matrix-events-sdk"; import { M_POLL_START } from "matrix-events-sdk";
import React, { useContext } from 'react'; import React, { ReactElement, useContext } from 'react';
import { Room } from 'matrix-js-sdk/src/models/room'; import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixClient } from 'matrix-js-sdk/src/client'; import { MatrixClient } from 'matrix-js-sdk/src/client';
@ -56,85 +56,55 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
const matrixClient: MatrixClient = useContext(MatrixClientContext); const matrixClient: MatrixClient = useContext(MatrixClientContext);
const { room, roomId } = useContext(RoomContext); const { room, roomId } = useContext(RoomContext);
if (props.haveRecording) { return (
return null; props.haveRecording
} ? null
: props.narrowMode
let uploadButtonIndex = 0; ? narrowMode(props, room, roomId, matrixClient)
const buttons: JSX.Element[] = []; : wideMode(props, room, roomId, matrixClient)
buttons.push(
<PollButton
key="polls"
room={room}
narrowMode={props.narrowMode}
/>,
); );
uploadButtonIndex = buttons.length; };
buttons.push(
<UploadButton key="controls_upload" roomId={roomId} relation={props.relation} />,
);
if (props.showLocationButton) {
const sender = room.getMember(matrixClient.getUserId());
buttons.push(
<LocationButton
key="location"
roomId={roomId}
sender={sender}
menuPosition={props.menuPosition}
narrowMode={props.narrowMode}
/>,
);
}
buttons.push(
<EmojiButton key="emoji_button" addEmoji={props.addEmoji} menuPosition={props.menuPosition} narrowMode={props.narrowMode} />,
);
if (props.showStickersButton) {
let title: string;
if (!props.narrowMode) {
title = props.isStickerPickerOpen ? _t("Hide Stickers") : _t("Show Stickers");
}
buttons.push( function wideMode(
<AccessibleTooltipButton props: IProps,
id='stickersButton' room: Room,
key="controls_stickers" roomId: string,
className="mx_MessageComposer_button mx_MessageComposer_stickers" matrixClient: MatrixClient,
onClick={() => props.setStickerPickerOpen(!props.isStickerPickerOpen)} ): ReactElement {
title={title} return <>
label={props.narrowMode ? _t("Send a sticker") : null} { pollButton(props, room) }
/>, { uploadButton(props, roomId) }
); { showLocationButton(props, room, roomId, matrixClient) }
} { emojiButton(props) }
{ showStickersButton(props) }
{ voiceRecordingButton(props) }
</>;
}
// XXX: the recording UI does not work well in narrow mode, so we hide this button for now function narrowMode(
if (!props.narrowMode) { props: IProps,
buttons.push( room: Room,
<CollapsibleButton roomId: string,
key="voice_message_send" matrixClient: MatrixClient,
className="mx_MessageComposer_button mx_MessageComposer_voiceMessage" ): ReactElement {
onClick={props.onRecordStartEndClick} const moreOptionsClasses = classNames({
title={_t("Send voice message")}
narrowMode={props.narrowMode}
/>,
);
}
if (!props.narrowMode) {
return <>{ buttons }</>;
}
const classnames = classNames({
mx_MessageComposer_button: true, mx_MessageComposer_button: true,
mx_MessageComposer_buttonMenu: true, mx_MessageComposer_buttonMenu: true,
mx_MessageComposer_closeButtonMenu: props.isMenuOpen, mx_MessageComposer_closeButtonMenu: props.isMenuOpen,
}); });
// we render the uploadButton at top level as it is a very common interaction, splice it out of the rest const moreButtons = [
const [uploadButton] = buttons.splice(uploadButtonIndex, 1); pollButton(props, room),
showLocationButton(props, room, roomId, matrixClient),
emojiButton(props),
showStickersButton(props),
voiceRecordingButton(props),
].filter(x => x);
return <> return <>
{ uploadButton } { uploadButton(props, roomId) }
<AccessibleTooltipButton <AccessibleTooltipButton
className={classnames} className={moreOptionsClasses}
onClick={props.toggleButtonMenu} onClick={props.toggleButtonMenu}
title={_t("More options")} title={_t("More options")}
tooltip={false} tooltip={false}
@ -145,28 +115,50 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
{...props.menuPosition} {...props.menuPosition}
wrapperClassName="mx_MessageComposer_Menu" wrapperClassName="mx_MessageComposer_Menu"
> >
{ buttons.map((button, index) => ( { moreButtons.map((button, index) => (
<MenuItem className="mx_CallContextMenu_item" key={index} onClick={props.toggleButtonMenu}> <MenuItem
className="mx_CallContextMenu_item"
key={index}
onClick={props.toggleButtonMenu}
>
{ button } { button }
</MenuItem> </MenuItem>
)) } )) }
</ContextMenu> </ContextMenu>
) } ) }
</>; </>;
}; }
function emojiButton(props: IProps): ReactElement {
return <EmojiButton
key="emoji_button"
addEmoji={props.addEmoji}
menuPosition={props.menuPosition}
narrowMode={props.narrowMode}
/>;
}
interface IEmojiButtonProps extends Pick<ICollapsibleButtonProps, "narrowMode"> { interface IEmojiButtonProps extends Pick<ICollapsibleButtonProps, "narrowMode"> {
addEmoji: (unicode: string) => boolean; addEmoji: (unicode: string) => boolean;
menuPosition: AboveLeftOf; menuPosition: AboveLeftOf;
} }
const EmojiButton: React.FC<IEmojiButtonProps> = ({ addEmoji, menuPosition, narrowMode }) => { const EmojiButton: React.FC<IEmojiButtonProps> = (
{ addEmoji, menuPosition, narrowMode },
) => {
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
let contextMenu: React.ReactElement | null = null; let contextMenu: React.ReactElement | null = null;
if (menuDisplayed) { if (menuDisplayed) {
const position = menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()); const position = (
contextMenu = <ContextMenu {...position} onFinished={closeMenu} managed={false}> menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect())
);
contextMenu = <ContextMenu
{...position}
onFinished={closeMenu}
managed={false}
>
<EmojiPicker onChoose={addEmoji} showQuickReactions={true} /> <EmojiPicker onChoose={addEmoji} showQuickReactions={true} />
</ContextMenu>; </ContextMenu>;
} }
@ -193,6 +185,14 @@ const EmojiButton: React.FC<IEmojiButtonProps> = ({ addEmoji, menuPosition, narr
</React.Fragment>; </React.Fragment>;
}; };
function uploadButton(props: IProps, roomId: string): ReactElement {
return <UploadButton
key="controls_upload"
roomId={roomId}
relation={props.relation}
/>;
}
interface IUploadButtonProps { interface IUploadButtonProps {
roomId: string; roomId: string;
relation?: IEventRelation | null; relation?: IEventRelation | null;
@ -270,6 +270,51 @@ class UploadButton extends React.Component<IUploadButtonProps> {
); );
} }
} }
function showStickersButton(props: IProps): ReactElement {
return (
props.showStickersButton
? <AccessibleTooltipButton
id='stickersButton'
key="controls_stickers"
className="mx_MessageComposer_button mx_MessageComposer_stickers"
onClick={() => props.setStickerPickerOpen(!props.isStickerPickerOpen)}
title={
props.narrowMode
? null
: props.isStickerPickerOpen
? _t("Hide Stickers")
: _t("Show Stickers")
}
label={props.narrowMode ? _t("Send a sticker") : null}
/>
: null
);
}
function voiceRecordingButton(props: IProps): ReactElement {
// XXX: recording UI does not work well in narrow mode, so hide for now
return (
props.narrowMode
? null
: <CollapsibleButton
key="voice_message_send"
className="mx_MessageComposer_button mx_MessageComposer_voiceMessage"
onClick={props.onRecordStartEndClick}
title={_t("Send voice message")}
narrowMode={props.narrowMode}
/>
);
}
function pollButton(props: IProps, room: Room): ReactElement {
return <PollButton
key="polls"
room={room}
narrowMode={props.narrowMode}
/>;
}
interface IPollButtonProps extends Pick<ICollapsibleButtonProps, "narrowMode"> { interface IPollButtonProps extends Pick<ICollapsibleButtonProps, "narrowMode"> {
room: Room; room: Room;
} }
@ -281,10 +326,17 @@ class PollButton extends React.PureComponent<IPollButtonProps> {
MatrixClientPeg.get().getUserId(), MatrixClientPeg.get().getUserId(),
); );
if (!canSend) { if (!canSend) {
Modal.createTrackedDialog('Polls', 'permissions error: cannot start', ErrorDialog, { Modal.createTrackedDialog(
title: _t("Permission Required"), 'Polls',
description: _t("You do not have permission to start polls in this room."), 'permissions error: cannot start',
}); ErrorDialog,
{
title: _t("Permission Required"),
description: _t(
"You do not have permission to start polls in this room.",
),
},
);
} else { } else {
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Polls', 'Polls',
@ -312,4 +364,23 @@ class PollButton extends React.PureComponent<IPollButtonProps> {
} }
} }
function showLocationButton(
props: IProps,
room: Room,
roomId: string,
matrixClient: MatrixClient,
): ReactElement {
return (
props.showLocationButton
? <LocationButton
key="location"
roomId={roomId}
sender={room.getMember(matrixClient.getUserId())}
menuPosition={props.menuPosition}
narrowMode={props.narrowMode}
/>
: null
);
}
export default MessageComposerButtons; export default MessageComposerButtons;

View File

@ -1694,11 +1694,11 @@
"You do not have permission to post to this room": "You do not have permission to post to this room", "You do not have permission to post to this room": "You do not have permission to post to this room",
"%(seconds)ss left": "%(seconds)ss left", "%(seconds)ss left": "%(seconds)ss left",
"Send voice message": "Send voice message", "Send voice message": "Send voice message",
"Add emoji": "Add emoji",
"Upload file": "Upload file",
"Hide Stickers": "Hide Stickers", "Hide Stickers": "Hide Stickers",
"Show Stickers": "Show Stickers", "Show Stickers": "Show Stickers",
"Send a sticker": "Send a sticker", "Send a sticker": "Send a sticker",
"Add emoji": "Add emoji",
"Upload file": "Upload file",
"You do not have permission to start polls in this room.": "You do not have permission to start polls in this room.", "You do not have permission to start polls in this room.": "You do not have permission to start polls in this room.",
"Create poll": "Create poll", "Create poll": "Create poll",
"Bold": "Bold", "Bold": "Bold",