mirror of https://github.com/vector-im/riot-web
				
				
				
			Merge pull request #6682 from matrix-org/gsouquet/compact-composer-18533
						commit
						59de3c96a2
					
				|  | @ -237,6 +237,15 @@ limitations under the License. | |||
|     mask-image: url('$(res)/img/element-icons/room/composer/sticker.svg'); | ||||
| } | ||||
| 
 | ||||
| .mx_MessageComposer_buttonMenu::before { | ||||
|     mask-image: url('$(res)/img/image-view/more.svg'); | ||||
| } | ||||
| 
 | ||||
| .mx_MessageComposer_closeButtonMenu::before { | ||||
|     transform: rotate(90deg); | ||||
|     transform-origin: center; | ||||
| } | ||||
| 
 | ||||
| .mx_MessageComposer_sendMessage { | ||||
|     cursor: pointer; | ||||
|     position: relative; | ||||
|  | @ -356,3 +365,8 @@ limitations under the License. | |||
|         margin-right: 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .mx_MessageComposer_Menu .mx_CallContextMenu_item { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
| } | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| import React from 'react'; | ||||
| import React, { createRef } from 'react'; | ||||
| import classNames from 'classnames'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import { MatrixClientPeg } from '../../../MatrixClientPeg'; | ||||
|  | @ -27,7 +27,13 @@ import { makeRoomPermalink, RoomPermalinkCreator } from '../../../utils/permalin | |||
| import ContentMessages from '../../../ContentMessages'; | ||||
| import E2EIcon from './E2EIcon'; | ||||
| import SettingsStore from "../../../settings/SettingsStore"; | ||||
| import { aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu"; | ||||
| import { | ||||
|     aboveLeftOf, | ||||
|     ContextMenu, | ||||
|     ContextMenuTooltipButton, | ||||
|     useContextMenu, | ||||
|     MenuItem, | ||||
| } from "../../structures/ContextMenu"; | ||||
| import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; | ||||
| import ReplyPreview from "./ReplyPreview"; | ||||
| import { UIFeature } from "../../../settings/UIFeature"; | ||||
|  | @ -45,6 +51,9 @@ import { Action } from "../../../dispatcher/actions"; | |||
| import EditorModel from "../../../editor/model"; | ||||
| import EmojiPicker from '../emojipicker/EmojiPicker'; | ||||
| import MemberStatusMessageAvatar from "../avatars/MemberStatusMessageAvatar"; | ||||
| import UIStore, { UI_EVENTS } from '../../../stores/UIStore'; | ||||
| 
 | ||||
| const NARROW_MODE_BREAKPOINT = 500; | ||||
| 
 | ||||
| interface IComposerAvatarProps { | ||||
|     me: object; | ||||
|  | @ -71,13 +80,13 @@ function SendButton(props: ISendButtonProps) { | |||
|     ); | ||||
| } | ||||
| 
 | ||||
| const EmojiButton = ({ addEmoji }) => { | ||||
| const EmojiButton = ({ addEmoji, menuPosition }) => { | ||||
|     const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); | ||||
| 
 | ||||
|     let contextMenu; | ||||
|     if (menuDisplayed) { | ||||
|         const buttonRect = button.current.getBoundingClientRect(); | ||||
|         contextMenu = <ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false}> | ||||
|         const position = menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()); | ||||
|         contextMenu = <ContextMenu {...position} onFinished={closeMenu} managed={false}> | ||||
|             <EmojiPicker onChoose={addEmoji} showQuickReactions={true} /> | ||||
|         </ContextMenu>; | ||||
|     } | ||||
|  | @ -196,6 +205,9 @@ interface IState { | |||
|     haveRecording: boolean; | ||||
|     recordingTimeLeftSeconds?: number; | ||||
|     me?: RoomMember; | ||||
|     narrowMode?: boolean; | ||||
|     isMenuOpen: boolean; | ||||
|     showStickers: boolean; | ||||
| } | ||||
| 
 | ||||
| @replaceableComponent("views.rooms.MessageComposer") | ||||
|  | @ -203,6 +215,7 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|     private dispatcherRef: string; | ||||
|     private messageComposerInput: SendMessageComposer; | ||||
|     private voiceRecordingButton: VoiceRecordComposerTile; | ||||
|     private ref: React.RefObject<HTMLDivElement> = createRef(); | ||||
| 
 | ||||
|     static defaultProps = { | ||||
|         replyInThread: false, | ||||
|  | @ -220,6 +233,8 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|             isComposerEmpty: true, | ||||
|             haveRecording: false, | ||||
|             recordingTimeLeftSeconds: null, // when set to a number, shows a toast
 | ||||
|             isMenuOpen: false, | ||||
|             showStickers: false, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  | @ -227,8 +242,21 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|         MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents); | ||||
|         this.waitForOwnMember(); | ||||
|         UIStore.instance.trackElementDimensions("MessageComposer", this.ref.current); | ||||
|         UIStore.instance.on("MessageComposer", this.onResize); | ||||
|     } | ||||
| 
 | ||||
|     private onResize = (type: UI_EVENTS, entry: ResizeObserverEntry) => { | ||||
|         if (type === UI_EVENTS.Resize) { | ||||
|             const narrowMode = entry.contentRect.width <= NARROW_MODE_BREAKPOINT; | ||||
|             this.setState({ | ||||
|                 narrowMode, | ||||
|                 isMenuOpen: !narrowMode ? false : this.state.isMenuOpen, | ||||
|                 showStickers: false, | ||||
|             }); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     private onAction = (payload: ActionPayload) => { | ||||
|         if (payload.action === 'reply_to_event') { | ||||
|             // add a timeout for the reply preview to be rendered, so
 | ||||
|  | @ -263,6 +291,8 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|         } | ||||
|         VoiceRecordingStore.instance.off(UPDATE_EVENT, this.onVoiceStoreUpdate); | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|         UIStore.instance.stopTrackingElementDimensions("MessageComposer"); | ||||
|         UIStore.instance.removeListener("MessageComposer", this.onResize); | ||||
|     } | ||||
| 
 | ||||
|     private onRoomStateEvents = (ev, state) => { | ||||
|  | @ -369,6 +399,96 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     private shouldShowStickerPicker = (): boolean => { | ||||
|         return SettingsStore.getValue(UIFeature.Widgets) | ||||
|         && SettingsStore.getValue("MessageComposerInput.showStickersButton") | ||||
|         && !this.state.haveRecording; | ||||
|     }; | ||||
| 
 | ||||
|     private showStickers = (showStickers: boolean) => { | ||||
|         this.setState({ showStickers }); | ||||
|     }; | ||||
| 
 | ||||
|     private toggleButtonMenu = (): void => { | ||||
|         this.setState({ | ||||
|             isMenuOpen: !this.state.isMenuOpen, | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     private renderButtons(menuPosition): JSX.Element | JSX.Element[] { | ||||
|         const buttons = new Map<string, JSX.Element>(); | ||||
|         if (!this.state.haveRecording) { | ||||
|             buttons.set( | ||||
|                 _t("Send File"), | ||||
|                 <UploadButton key="controls_upload" roomId={this.props.room.roomId} />, | ||||
|             ); | ||||
|             buttons.set( | ||||
|                 _t("Show Emojis"), | ||||
|                 <EmojiButton key="emoji_button" addEmoji={this.addEmoji} menuPosition={menuPosition} />, | ||||
|             ); | ||||
|         } | ||||
|         if (this.shouldShowStickerPicker()) { | ||||
|             buttons.set( | ||||
|                 _t("Show Stickers"), | ||||
|                 <AccessibleTooltipButton | ||||
|                     id='stickersButton' | ||||
|                     key="controls_stickers" | ||||
|                     className="mx_MessageComposer_button mx_MessageComposer_stickers" | ||||
|                     onClick={() => this.showStickers(!this.state.showStickers)} | ||||
|                     title={this.state.showStickers ? _t("Hide Stickers") : _t("Show Stickers")} | ||||
|                 />, | ||||
|             ); | ||||
|         } | ||||
|         if (!this.state.haveRecording && !this.state.narrowMode) { | ||||
|             buttons.set( | ||||
|                 _t("Send voice message"), | ||||
|                 <AccessibleTooltipButton | ||||
|                     className="mx_MessageComposer_button mx_MessageComposer_voiceMessage" | ||||
|                     onClick={() => this.voiceRecordingButton?.onRecordStartEndClick()} | ||||
|                     title={_t("Send voice message")} | ||||
|                 />, | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (!this.state.narrowMode) { | ||||
|             return Array.from(buttons.values()); | ||||
|         } else { | ||||
|             const classnames = classNames({ | ||||
|                 mx_MessageComposer_button: true, | ||||
|                 mx_MessageComposer_buttonMenu: true, | ||||
|                 mx_MessageComposer_closeButtonMenu: this.state.isMenuOpen, | ||||
|             }); | ||||
| 
 | ||||
|             return <> | ||||
|                 { buttons[0] } | ||||
|                 <AccessibleTooltipButton | ||||
|                     className={classnames} | ||||
|                     onClick={this.toggleButtonMenu} | ||||
|                     title={_t("Composer menu")} | ||||
|                     tooltip={false} | ||||
|                 /> | ||||
|                 { this.state.isMenuOpen && ( | ||||
|                     <ContextMenu | ||||
|                         onFinished={this.toggleButtonMenu} | ||||
|                         {...menuPosition} | ||||
|                         menuPaddingRight={10} | ||||
|                         menuPaddingTop={5} | ||||
|                         menuPaddingBottom={5} | ||||
|                         menuWidth={150} | ||||
|                         wrapperClassName="mx_MessageComposer_Menu" | ||||
|                     > | ||||
|                         { Array.from(buttons).slice(1).map(([label, button]) => ( | ||||
|                             <MenuItem className="mx_CallContextMenu_item" key={label} onClick={this.toggleButtonMenu}> | ||||
|                                 { button } | ||||
|                                 { label } | ||||
|                             </MenuItem> | ||||
|                         )) } | ||||
|                     </ContextMenu> | ||||
|                 ) } | ||||
|             </>; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const controls = [ | ||||
|             this.state.me && !this.props.compact ? <ComposerAvatar key="controls_avatar" me={this.state.me} /> : null, | ||||
|  | @ -377,6 +497,12 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|                 null, | ||||
|         ]; | ||||
| 
 | ||||
|         let menuPosition; | ||||
|         if (this.ref.current) { | ||||
|             const contentRect = this.ref.current.getBoundingClientRect(); | ||||
|             menuPosition = aboveLeftOf(contentRect); | ||||
|         } | ||||
| 
 | ||||
|         if (!this.state.tombstone && this.state.canSendMessages) { | ||||
|             controls.push( | ||||
|                 <SendMessageComposer | ||||
|  | @ -392,33 +518,10 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|                 />, | ||||
|             ); | ||||
| 
 | ||||
|             if (!this.state.haveRecording) { | ||||
|                 controls.push( | ||||
|                     <UploadButton key="controls_upload" roomId={this.props.room.roomId} />, | ||||
|                     <EmojiButton key="emoji_button" addEmoji={this.addEmoji} />, | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             if (SettingsStore.getValue(UIFeature.Widgets) && | ||||
|                 SettingsStore.getValue("MessageComposerInput.showStickersButton") && | ||||
|                 !this.state.haveRecording) { | ||||
|                 controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />); | ||||
|             } | ||||
| 
 | ||||
|             controls.push(<VoiceRecordComposerTile | ||||
|                 key="controls_voice_record" | ||||
|                 ref={c => this.voiceRecordingButton = c} | ||||
|                 room={this.props.room} />); | ||||
| 
 | ||||
|             if (!this.state.isComposerEmpty || this.state.haveRecording) { | ||||
|                 controls.push( | ||||
|                     <SendButton | ||||
|                         key="controls_send" | ||||
|                         onClick={this.sendMessage} | ||||
|                         title={this.state.haveRecording ? _t("Send voice message") : undefined} | ||||
|                     />, | ||||
|                 ); | ||||
|             } | ||||
|         } else if (this.state.tombstone) { | ||||
|             const replacementRoomId = this.state.tombstone.getContent()['replacement_room']; | ||||
| 
 | ||||
|  | @ -459,6 +562,15 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|                 yOffset={-50} | ||||
|             />; | ||||
|         } | ||||
|         controls.push( | ||||
|             <Stickerpicker | ||||
|                 room={this.props.room} | ||||
|                 showStickers={this.state.showStickers} | ||||
|                 setShowStickers={this.showStickers} | ||||
|                 menuPosition={menuPosition} />, | ||||
|         ); | ||||
| 
 | ||||
|         const showSendButton = !this.state.isComposerEmpty || this.state.haveRecording; | ||||
| 
 | ||||
|         const classes = classNames({ | ||||
|             "mx_MessageComposer": true, | ||||
|  | @ -467,7 +579,7 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|         }); | ||||
| 
 | ||||
|         return ( | ||||
|             <div className={classes}> | ||||
|             <div className={classes} ref={this.ref}> | ||||
|                 { recordingTooltip } | ||||
|                 <div className="mx_MessageComposer_wrapper"> | ||||
|                     { this.props.showReplyPreview && ( | ||||
|  | @ -475,6 +587,14 @@ export default class MessageComposer extends React.Component<IProps, IState> { | |||
|                     ) } | ||||
|                     <div className="mx_MessageComposer_row"> | ||||
|                         { controls } | ||||
|                         { this.renderButtons(menuPosition) } | ||||
|                         { showSendButton && ( | ||||
|                             <SendButton | ||||
|                                 key="controls_send" | ||||
|                                 onClick={this.sendMessage} | ||||
|                                 title={this.state.haveRecording ? _t("Send voice message") : undefined} | ||||
|                             /> | ||||
|                         ) } | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| import React from 'react'; | ||||
| import classNames from 'classnames'; | ||||
| import { Room } from 'matrix-js-sdk/src/models/room'; | ||||
| import { _t, _td } from '../../../languageHandler'; | ||||
| import AppTile from '../elements/AppTile'; | ||||
|  | @ -27,7 +26,6 @@ import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; | |||
| import SettingsStore from "../../../settings/SettingsStore"; | ||||
| import { ChevronFace, ContextMenu } from "../../structures/ContextMenu"; | ||||
| import { WidgetType } from "../../../widgets/WidgetType"; | ||||
| import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; | ||||
| import { Action } from "../../../dispatcher/actions"; | ||||
| import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore"; | ||||
| import { replaceableComponent } from "../../../utils/replaceableComponent"; | ||||
|  | @ -44,10 +42,12 @@ const PERSISTED_ELEMENT_KEY = "stickerPicker"; | |||
| 
 | ||||
| interface IProps { | ||||
|     room: Room; | ||||
|     showStickers: boolean; | ||||
|     menuPosition?: any; | ||||
|     setShowStickers: (showStickers: boolean) => void; | ||||
| } | ||||
| 
 | ||||
| interface IState { | ||||
|     showStickers: boolean; | ||||
|     imError: string; | ||||
|     stickerpickerX: number; | ||||
|     stickerpickerY: number; | ||||
|  | @ -72,7 +72,6 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|     constructor(props: IProps) { | ||||
|         super(props); | ||||
|         this.state = { | ||||
|             showStickers: false, | ||||
|             imError: null, | ||||
|             stickerpickerX: null, | ||||
|             stickerpickerY: null, | ||||
|  | @ -114,7 +113,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|             console.warn('No widget ID specified, not disabling assets'); | ||||
|         } | ||||
| 
 | ||||
|         this.setState({ showStickers: false }); | ||||
|         this.props.setShowStickers(false); | ||||
|         WidgetUtils.removeStickerpickerWidgets().then(() => { | ||||
|             this.forceUpdate(); | ||||
|         }).catch((e) => { | ||||
|  | @ -146,15 +145,15 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|     } | ||||
| 
 | ||||
|     public componentDidUpdate(prevProps: IProps, prevState: IState): void { | ||||
|         this.sendVisibilityToWidget(this.state.showStickers); | ||||
|         this.sendVisibilityToWidget(this.props.showStickers); | ||||
|     } | ||||
| 
 | ||||
|     private imError(errorMsg: string, e: Error): void { | ||||
|         console.error(errorMsg, e); | ||||
|         this.setState({ | ||||
|             showStickers: false, | ||||
|             imError: _t(errorMsg), | ||||
|         }); | ||||
|         this.props.setShowStickers(false); | ||||
|     } | ||||
| 
 | ||||
|     private updateWidget = (): void => { | ||||
|  | @ -194,12 +193,12 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|                 this.forceUpdate(); | ||||
|                 break; | ||||
|             case "stickerpicker_close": | ||||
|                 this.setState({ showStickers: false }); | ||||
|                 this.props.setShowStickers(false); | ||||
|                 break; | ||||
|             case Action.AfterRightPanelPhaseChange: | ||||
|             case "show_left_panel": | ||||
|             case "hide_left_panel": | ||||
|                 this.setState({ showStickers: false }); | ||||
|                 this.props.setShowStickers(false); | ||||
|                 break; | ||||
|         } | ||||
|     }; | ||||
|  | @ -338,8 +337,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
| 
 | ||||
|         const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19; | ||||
| 
 | ||||
|         this.props.setShowStickers(true); | ||||
|         this.setState({ | ||||
|             showStickers: true, | ||||
|             stickerpickerX: x, | ||||
|             stickerpickerY: y, | ||||
|             stickerpickerChevronOffset, | ||||
|  | @ -351,8 +350,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|      * @param  {Event} ev Event that triggered the function call | ||||
|      */ | ||||
|     private onHideStickersClick = (ev: React.MouseEvent): void => { | ||||
|         if (this.state.showStickers) { | ||||
|             this.setState({ showStickers: false }); | ||||
|         if (this.props.showStickers) { | ||||
|             this.props.setShowStickers(false); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|  | @ -360,8 +359,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|      * Called when the window is resized | ||||
|      */ | ||||
|     private onResize = (): void => { | ||||
|         if (this.state.showStickers) { | ||||
|             this.setState({ showStickers: false }); | ||||
|         if (this.props.showStickers) { | ||||
|             this.props.setShowStickers(false); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|  | @ -369,8 +368,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|      * The stickers picker was hidden | ||||
|      */ | ||||
|     private onFinished = (): void => { | ||||
|         if (this.state.showStickers) { | ||||
|             this.setState({ showStickers: false }); | ||||
|         if (this.props.showStickers) { | ||||
|             this.props.setShowStickers(false); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|  | @ -395,54 +394,23 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> { | |||
|     }; | ||||
| 
 | ||||
|     public render(): JSX.Element { | ||||
|         let stickerPicker; | ||||
|         let stickersButton; | ||||
|         const className = classNames( | ||||
|             "mx_MessageComposer_button", | ||||
|             "mx_MessageComposer_stickers", | ||||
|             "mx_Stickers_hideStickers", | ||||
|             "mx_MessageComposer_button_highlight", | ||||
|         ); | ||||
|         if (this.state.showStickers) { | ||||
|             // Show hide-stickers button
 | ||||
|             stickersButton = | ||||
|                 <AccessibleButton | ||||
|                     id='stickersButton' | ||||
|                     key="controls_hide_stickers" | ||||
|                     className={className} | ||||
|                     onClick={this.onHideStickersClick} | ||||
|                     title={_t("Hide Stickers")} | ||||
|                 />; | ||||
|         if (!this.props.showStickers) return null; | ||||
| 
 | ||||
|             stickerPicker = <ContextMenu | ||||
|                 chevronOffset={this.state.stickerpickerChevronOffset} | ||||
|                 chevronFace={ChevronFace.Bottom} | ||||
|                 left={this.state.stickerpickerX} | ||||
|                 top={this.state.stickerpickerY} | ||||
|                 menuWidth={this.popoverWidth} | ||||
|                 menuHeight={this.popoverHeight} | ||||
|                 onFinished={this.onFinished} | ||||
|                 menuPaddingTop={0} | ||||
|                 menuPaddingLeft={0} | ||||
|                 menuPaddingRight={0} | ||||
|                 zIndex={STICKERPICKER_Z_INDEX} | ||||
|             > | ||||
|                 <GenericElementContextMenu element={this.getStickerpickerContent()} onResize={this.onFinished} /> | ||||
|             </ContextMenu>; | ||||
|         } else { | ||||
|             // Show show-stickers button
 | ||||
|             stickersButton = | ||||
|                 <AccessibleTooltipButton | ||||
|                     id='stickersButton' | ||||
|                     key="controls_show_stickers" | ||||
|                     className="mx_MessageComposer_button mx_MessageComposer_stickers" | ||||
|                     onClick={this.onShowStickersClick} | ||||
|                     title={_t("Show Stickers")} | ||||
|                 />; | ||||
|         } | ||||
|         return <React.Fragment> | ||||
|             { stickersButton } | ||||
|             { stickerPicker } | ||||
|         </React.Fragment>; | ||||
|         return <ContextMenu | ||||
|             chevronOffset={this.state.stickerpickerChevronOffset} | ||||
|             chevronFace={ChevronFace.Bottom} | ||||
|             left={this.state.stickerpickerX} | ||||
|             top={this.state.stickerpickerY} | ||||
|             menuWidth={this.popoverWidth} | ||||
|             menuHeight={this.popoverHeight} | ||||
|             onFinished={this.onFinished} | ||||
|             menuPaddingTop={0} | ||||
|             menuPaddingLeft={0} | ||||
|             menuPaddingRight={0} | ||||
|             zIndex={STICKERPICKER_Z_INDEX} | ||||
|             {...this.props.menuPosition} | ||||
|         > | ||||
|             <GenericElementContextMenu element={this.getStickerpickerContent()} onResize={this.onFinished} /> | ||||
|         </ContextMenu>; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ import React, { ReactNode } from "react"; | |||
| import { IUpload, RecordingState, VoiceRecording } from "../../../audio/VoiceRecording"; | ||||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { MatrixClientPeg } from "../../../MatrixClientPeg"; | ||||
| import classNames from "classnames"; | ||||
| import LiveRecordingWaveform from "../audio_messages/LiveRecordingWaveform"; | ||||
| import { replaceableComponent } from "../../../utils/replaceableComponent"; | ||||
| import LiveRecordingClock from "../audio_messages/LiveRecordingClock"; | ||||
|  | @ -137,7 +136,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps, | |||
|         await this.disposeRecording(); | ||||
|     }; | ||||
| 
 | ||||
|     private onRecordStartEndClick = async () => { | ||||
|     public onRecordStartEndClick = async () => { | ||||
|         if (this.state.recorder) { | ||||
|             await this.state.recorder.stop(); | ||||
|             return; | ||||
|  | @ -215,27 +214,23 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps, | |||
|     } | ||||
| 
 | ||||
|     public render(): ReactNode { | ||||
|         let stopOrRecordBtn; | ||||
|         let deleteButton; | ||||
|         if (!this.state.recordingPhase || this.state.recordingPhase === RecordingState.Started) { | ||||
|             const classes = classNames({ | ||||
|                 'mx_MessageComposer_button': !this.state.recorder, | ||||
|                 'mx_MessageComposer_voiceMessage': !this.state.recorder, | ||||
|                 'mx_VoiceRecordComposerTile_stop': this.state.recorder?.isRecording, | ||||
|             }); | ||||
|         if (!this.state.recordingPhase) return null; | ||||
| 
 | ||||
|         let stopBtn; | ||||
|         let deleteButton; | ||||
|         if (this.state.recordingPhase === RecordingState.Started) { | ||||
|             let tooltip = _t("Send voice message"); | ||||
|             if (!!this.state.recorder) { | ||||
|                 tooltip = _t("Stop recording"); | ||||
|             } | ||||
| 
 | ||||
|             stopOrRecordBtn = <AccessibleTooltipButton | ||||
|                 className={classes} | ||||
|             stopBtn = <AccessibleTooltipButton | ||||
|                 className="mx_VoiceRecordComposerTile_stop" | ||||
|                 onClick={this.onRecordStartEndClick} | ||||
|                 title={tooltip} | ||||
|             />; | ||||
|             if (this.state.recorder && !this.state.recorder?.isRecording) { | ||||
|                 stopOrRecordBtn = null; | ||||
|                 stopBtn = null; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -264,13 +259,10 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps, | |||
|             </span>; | ||||
|         } | ||||
| 
 | ||||
|         // The record button (mic icon) is meant to be on the right edge, but we also want the
 | ||||
|         // stop button to be left of the waveform area. Luckily, none of the surrounding UI is
 | ||||
|         // rendered when we're not recording, so the record button ends up in the correct spot.
 | ||||
|         return (<> | ||||
|             { uploadIndicator } | ||||
|             { deleteButton } | ||||
|             { stopOrRecordBtn } | ||||
|             { stopBtn } | ||||
|             { this.renderWaveformArea() } | ||||
|         </>); | ||||
|     } | ||||
|  |  | |||
|  | @ -1564,7 +1564,12 @@ | |||
|     "Send a reply…": "Send a reply…", | ||||
|     "Send an encrypted message…": "Send an encrypted message…", | ||||
|     "Send a message…": "Send a message…", | ||||
|     "Send File": "Send File", | ||||
|     "Show Emojis": "Show Emojis", | ||||
|     "Show Stickers": "Show Stickers", | ||||
|     "Hide Stickers": "Hide Stickers", | ||||
|     "Send voice message": "Send voice message", | ||||
|     "Composer menu": "Composer menu", | ||||
|     "The conversation continues here.": "The conversation continues here.", | ||||
|     "This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.", | ||||
|     "You do not have permission to post to this room": "You do not have permission to post to this room", | ||||
|  | @ -1725,8 +1730,6 @@ | |||
|     "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled", | ||||
|     "Add some now": "Add some now", | ||||
|     "Stickerpack": "Stickerpack", | ||||
|     "Hide Stickers": "Hide Stickers", | ||||
|     "Show Stickers": "Show Stickers", | ||||
|     "Failed to revoke invite": "Failed to revoke invite", | ||||
|     "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.", | ||||
|     "Admin Tools": "Admin Tools", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Germain
						Germain