From 2957e52bb9482e69443e73d028f04a599ab1cdf6 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 31 Oct 2022 16:01:55 +0100 Subject: [PATCH 01/31] WIP on new design --- res/css/_components.pcss | 1 + res/css/views/rooms/_EmojiButton.pcss | 13 ++++ res/css/views/rooms/_MessageComposer.pcss | 65 ++++------------ .../views/rooms/_MessageComposerButton.pcss | 42 +++++++++++ .../_EditWysiwygComposer.pcss | 2 +- .../_SendWysiwygComposer.pcss | 66 ++++++++++------ .../wysiwyg_composer/components/_Editor.pcss | 6 +- src/components/views/rooms/EmojiButton.tsx | 75 +++++++++++++++++++ .../views/rooms/MessageComposer.tsx | 37 ++++++--- .../views/rooms/MessageComposerButtons.tsx | 66 +++------------- .../wysiwyg_composer/SendWysiwygComposer.tsx | 17 ++++- .../wysiwyg_composer/components/Editor.tsx | 38 ++++++---- .../components/PlainTextComposer.tsx | 14 +++- .../components/WysiwygComposer.tsx | 15 +++- .../wysiwyg_composer/hooks/useIsExpended.ts | 35 +++++++++ 15 files changed, 332 insertions(+), 160 deletions(-) create mode 100644 res/css/views/rooms/_EmojiButton.pcss create mode 100644 res/css/views/rooms/_MessageComposerButton.pcss create mode 100644 src/components/views/rooms/EmojiButton.tsx create mode 100644 src/components/views/rooms/wysiwyg_composer/hooks/useIsExpended.ts diff --git a/res/css/_components.pcss b/res/css/_components.pcss index cc7c6a2e2a3..5a263aa1e96 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -260,6 +260,7 @@ @import "./views/rooms/_AuxPanel.pcss"; @import "./views/rooms/_BasicMessageComposer.pcss"; @import "./views/rooms/_E2EIcon.pcss"; +@import "./views/rooms/_EmojiButton.pcss"; @import "./views/rooms/_EditMessageComposer.pcss"; @import "./views/rooms/_EntityTile.pcss"; @import "./views/rooms/_EventBubbleTile.pcss"; diff --git a/res/css/views/rooms/_EmojiButton.pcss b/res/css/views/rooms/_EmojiButton.pcss new file mode 100644 index 00000000000..544ecdfa73c --- /dev/null +++ b/res/css/views/rooms/_EmojiButton.pcss @@ -0,0 +1,13 @@ +@import "./_MessageComposerButton.pcss"; + +.mx_EmojiButton { + @mixin composerButton; +} + +.mx_EmojiButton_highlight { + @mixin composerButtonHighLight; +} + +.mx_EmojiButton_icon::before { + mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg'); +} diff --git a/res/css/views/rooms/_MessageComposer.pcss b/res/css/views/rooms/_MessageComposer.pcss index 4d22f60a122..d5907126bfc 100644 --- a/res/css/views/rooms/_MessageComposer.pcss +++ b/res/css/views/rooms/_MessageComposer.pcss @@ -15,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +@import "./_MessageComposerButton.pcss"; + .mx_MessageComposer_wrapper { vertical-align: middle; margin: auto; @@ -171,52 +173,17 @@ limitations under the License. } .mx_MessageComposer_button_highlight { - background: rgba($accent, 0.25); - /* make the icon the accent color too */ - &::before { - background-color: $accent !important; - } + @mixin composerButtonHighLight; } .mx_MessageComposer_button { - --size: 26px; - position: relative; - cursor: pointer; - height: var(--size); - line-height: var(--size); - width: auto; - padding-left: var(--size); - border-radius: 50%; + @mixin composerButton; margin-right: 6px; &:last-child { margin-right: auto; } - &::before { - content: ''; - position: absolute; - top: 3px; - left: 3px; - height: 20px; - width: 20px; - background-color: $icon-button-color; - mask-repeat: no-repeat; - mask-size: contain; - mask-position: center; - } - - &::after { - content: ''; - position: absolute; - left: 0; - top: 0; - z-index: 0; - width: var(--size); - height: var(--size); - border-radius: 50%; - } - &:hover, &.mx_MessageComposer_closeButtonMenu { &::after { @@ -232,15 +199,19 @@ limitations under the License. background-color: $alert; } } - -/* - The wysisyg composer increase the size of the MessageComposer. We temporary move the buttons - Soon the dom structure of the MessageComposer will change with the next evolution of the wysiwyg composer - and this workaround will disappear -*/ .mx_MessageComposer_wysiwyg { - .mx_MessageComposer_e2eIcon.mx_E2EIcon,.mx_MessageComposer_button, .mx_MessageComposer_sendMessage { - margin-top: 28px; + .mx_MessageComposer_wrapper { + padding-left: 16px; + margin-top: 6px; + margin-bottom: 12px; + } + + .mx_MessageComposer_row { + align-items: flex-end; + } + + .mx_MessageComposer_button { + margin-bottom: 9px; } } @@ -260,10 +231,6 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/live.svg'); } -.mx_MessageComposer_emoji::before { - mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg'); -} - .mx_MessageComposer_plain_text::before { mask-image: url('$(res)/img/element-icons/room/composer/plain_text.svg'); } diff --git a/res/css/views/rooms/_MessageComposerButton.pcss b/res/css/views/rooms/_MessageComposerButton.pcss new file mode 100644 index 00000000000..81841165eb2 --- /dev/null +++ b/res/css/views/rooms/_MessageComposerButton.pcss @@ -0,0 +1,42 @@ +@define-mixin composerButtonHighLight { + background: rgba($accent, 0.25); + /* make the icon the accent color too */ + &::before { + background-color: $accent !important; + } +} + +@define-mixin composerButton { + --size: 26px; + position: relative; + cursor: pointer; + height: var(--size); + line-height: var(--size); + width: auto; + padding-left: var(--size); + border-radius: 50%; + + &::before { + content: ''; + position: absolute; + top: 3px; + left: 3px; + height: 20px; + width: 20px; + background-color: $icon-button-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } + + &::after { + content: ''; + position: absolute; + left: 0; + top: 0; + z-index: 0; + width: var(--size); + height: var(--size); + border-radius: 50%; + } +} diff --git a/res/css/views/rooms/wysiwyg_composer/_EditWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_EditWysiwygComposer.pcss index 73e5fef6e9a..b711a634d1e 100644 --- a/res/css/views/rooms/wysiwyg_composer/_EditWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_EditWysiwygComposer.pcss @@ -24,7 +24,7 @@ limitations under the License. gap: 8px; padding: 8px var(--EditWysiwygComposer-padding-inline); - .mx_WysiwygComposer_content { + .mx_WysiwygComposer_Editor_content { border-radius: 4px; border: solid 1px $primary-hairline-color; background-color: $background; diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index a00f8c7e113..b26527671b9 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -25,29 +25,53 @@ limitations under the License. margin-right: 6px; /* don't grow wider than available space */ min-width: 0; + gap: 8px; - .mx_WysiwygComposer_container { - flex: 1; + .mx_E2EIcon { + margin: 0px 14px 5px 14px; + // margin-bottom: 3px; + } + + .mx_E2EIcon_normal::after { + background-color: $quaternary-content; + } + + .mx_EmojiButton { + margin: 0px 14px 0px 14px; + } + + .mx_WysiwygComposer_Editor { + border: 1px solid; + border-color: $quinary-content; + padding: 8px 0px 8px; display: flex; - flex-direction: column; - /* min-height at this level so the mx_BasicMessageComposer_input */ - /* still stays vertically centered when less than 55px. */ - /* We also set this to ensure the voice message recording widget */ - /* doesn't cause a jump. */ - min-height: 55px; - - .mx_WysiwygComposer_content { - border: 1px solid; - border-radius: 20px; - padding: 8px 10px; - /* this will center the contenteditable */ - /* in it's parent vertically */ - /* while keeping the autocomplete at the top */ - /* of the composer. The parent needs to be a flex container for this to work. */ - margin: auto 0; - /* max-height at this level so autocomplete doesn't get scrolled too */ - max-height: 140px; - overflow-y: auto; + align-items: flex-end; + + &[data-is-expanded="true"] { + border-radius: 14px; + } + + &[data-is-expanded="false"] { + border-radius: 40px; + } + + .mx_WysiwygComposer_Editor_container { + flex: 1; + display: flex; + flex-direction: column; + min-height: 22px; + margin-bottom: 2px; + + .mx_WysiwygComposer_Editor_content { + /* this will center the contenteditable */ + /* in it's parent vertically */ + /* while keeping the autocomplete at the top */ + /* of the composer. The parent needs to be a flex container for this to work. */ + margin: auto 0; + /* max-height at this level so autocomplete doesn't get scrolled too */ + max-height: 140px; + overflow-y: auto; + } } } } diff --git a/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss b/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss index 6a6b68af7c6..00e5b220dfd 100644 --- a/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss +++ b/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss @@ -14,15 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_WysiwygComposer_container { - position: relative; - +.mx_WysiwygComposer_Editor_container { @keyframes visualbell { from { background-color: $visual-bell-bg-color; } to { background-color: $background; } } - .mx_WysiwygComposer_content { + .mx_WysiwygComposer_Editor_content { white-space: pre-wrap; word-wrap: break-word; outline: none; diff --git a/src/components/views/rooms/EmojiButton.tsx b/src/components/views/rooms/EmojiButton.tsx new file mode 100644 index 00000000000..8e7ca67bfe9 --- /dev/null +++ b/src/components/views/rooms/EmojiButton.tsx @@ -0,0 +1,75 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 classNames from "classnames"; +import React, { useContext } from "react"; + +import { _t } from "../../../languageHandler"; +import ContextMenu, { aboveLeftOf, AboveLeftOf, useContextMenu } from "../../structures/ContextMenu"; +import EmojiPicker from "../emojipicker/EmojiPicker"; +import { CollapsibleButton } from "./CollapsibleButton"; +import { OverflowMenuContext } from "./MessageComposerButtons"; + +interface IEmojiButtonProps { + addEmoji: (unicode: string) => boolean; + menuPosition: AboveLeftOf; + className?: string; +} + +export function EmojiButton({ addEmoji, menuPosition, className }: IEmojiButtonProps) { + const overflowMenuCloser = useContext(OverflowMenuContext); + const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); + + let contextMenu: React.ReactElement | null = null; + if (menuDisplayed) { + const position = ( + menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()) + ); + + contextMenu = { + closeMenu(); + overflowMenuCloser?.(); + }} + managed={false} + > + + ; + } + + const computedClassName = classNames( + "mx_EmojiButton", + className, + { + "mx_EmojiButton_highlight": menuDisplayed, + }, + ); + + // TODO: replace ContextMenuTooltipButton with a unified representation of + // the header buttons and the right panel buttons + return <> + + + { contextMenu } + ; +} diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 4b04b87daef..e5694d3a4c1 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -420,18 +420,32 @@ export class MessageComposer extends React.Component { return this.state.showStickersButton && !isLocalRoom(this.props.room); } + private getMenuPosition() { + if (this.ref.current) { + const hasFormattingButtons = this.state.isWysiwygLabEnabled && this.state.isRichTextEnabled; + const contentRect = this.ref.current.getBoundingClientRect(); + // Here we need to remove the all the extra space above the editor + // Instead of doing a querySelector or pass a ref to find the compute the height formatting buttons + // We are using an arbitrary value, the formatting buttons height doesn't change during the lifecycle of the component + // It's easier to just use a constant here instead of an over-engineering way to find the height + const heightToRemove = hasFormattingButtons ? 45 : 0; + const fixedRect = new DOMRect( + contentRect.x, + contentRect.y + heightToRemove, + contentRect.width, + contentRect.height - heightToRemove); + return aboveLeftOf(fixedRect); + } + } + public render() { + const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus); const controls = [ - this.props.e2eStatus ? - : - null, + hasE2EIcon && + , ]; - let menuPosition: AboveLeftOf | undefined; - if (this.ref.current) { - const contentRect = this.ref.current.getBoundingClientRect(); - menuPosition = aboveLeftOf(contentRect); - } + const menuPosition = this.getMenuPosition(); const canSendMessages = this.context.canSendMessages && !this.context.tombstone; if (canSendMessages) { @@ -443,6 +457,8 @@ export class MessageComposer extends React.Component { onSend={this.sendMessage} isRichTextEnabled={this.state.isRichTextEnabled} initialContent={this.state.initialComposerContent} + e2eStatus={this.props.e2eStatus} + menuPosition={menuPosition} />, ); } else { @@ -529,8 +545,8 @@ export class MessageComposer extends React.Component { const classes = classNames({ "mx_MessageComposer": true, "mx_MessageComposer--compact": this.props.compact, - "mx_MessageComposer_e2eStatus": this.props.e2eStatus != undefined, - "mx_MessageComposer_wysiwyg": this.state.isWysiwygLabEnabled && this.state.isRichTextEnabled, + "mx_MessageComposer_e2eStatus": hasE2EIcon, + "mx_MessageComposer_wysiwyg": this.state.isWysiwygLabEnabled, }); return ( @@ -559,7 +575,6 @@ export class MessageComposer extends React.Component { showLocationButton={!window.electron} showPollsButton={this.state.showPollsButton} showStickersButton={this.showStickersButton} - showComposerModeButton={this.state.isWysiwygLabEnabled} isRichTextEnabled={this.state.isRichTextEnabled} onComposerModeClick={this.onRichTextToggle} toggleButtonMenu={this.toggleButtonMenu} diff --git a/src/components/views/rooms/MessageComposerButtons.tsx b/src/components/views/rooms/MessageComposerButtons.tsx index d31f6fea27f..8ad660379d7 100644 --- a/src/components/views/rooms/MessageComposerButtons.tsx +++ b/src/components/views/rooms/MessageComposerButtons.tsx @@ -25,9 +25,8 @@ import { THREAD_RELATION_TYPE } from 'matrix-js-sdk/src/models/thread'; import { _t } from '../../../languageHandler'; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { CollapsibleButton } from './CollapsibleButton'; -import ContextMenu, { aboveLeftOf, AboveLeftOf, useContextMenu } from '../../structures/ContextMenu'; +import { AboveLeftOf } from '../../structures/ContextMenu'; import dis from '../../../dispatcher/dispatcher'; -import EmojiPicker from '../emojipicker/EmojiPicker'; import ErrorDialog from "../dialogs/ErrorDialog"; import LocationButton from '../location/LocationButton'; import Modal from "../../../Modal"; @@ -39,6 +38,8 @@ import RoomContext from '../../../contexts/RoomContext'; import { useDispatcher } from "../../../hooks/useDispatcher"; import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; import IconizedContextMenu, { IconizedContextMenuOptionList } from '../context_menus/IconizedContextMenu'; +import { EmojiButton } from './EmojiButton'; +import { useSettingValue } from '../../../hooks/useSettings'; interface IProps { addEmoji: (emoji: string) => boolean; @@ -56,7 +57,6 @@ interface IProps { showVoiceBroadcastButton: boolean; onStartVoiceBroadcastClick: () => void; isRichTextEnabled: boolean; - showComposerModeButton: boolean; onComposerModeClick: () => void; } @@ -67,6 +67,8 @@ const MessageComposerButtons: React.FC = (props: IProps) => { const matrixClient: MatrixClient = useContext(MatrixClientContext); const { room, roomId, narrow } = useContext(RoomContext); + const isWysiwygLabEnabled = useSettingValue('feature_wysiwyg_composer'); + if (props.haveRecording) { return null; } @@ -75,7 +77,9 @@ const MessageComposerButtons: React.FC = (props: IProps) => { let moreButtons: ReactElement[]; if (narrow) { mainButtons = [ - emojiButton(props), + !isWysiwygLabEnabled && emojiButton(props), + isWysiwygLabEnabled && + , ]; moreButtons = [ uploadButton(), // props passed via UploadButtonContext @@ -87,8 +91,8 @@ const MessageComposerButtons: React.FC = (props: IProps) => { ]; } else { mainButtons = [ - emojiButton(props), - props.showComposerModeButton && + !isWysiwygLabEnabled && emojiButton(props), + isWysiwygLabEnabled && , uploadButton(), // props passed via UploadButtonContext ]; @@ -139,58 +143,10 @@ function emojiButton(props: IProps): ReactElement { key="emoji_button" addEmoji={props.addEmoji} menuPosition={props.menuPosition} + className="mx_MessageComposer_button" />; } -interface IEmojiButtonProps { - addEmoji: (unicode: string) => boolean; - menuPosition: AboveLeftOf; -} - -const EmojiButton: React.FC = ({ addEmoji, menuPosition }) => { - const overflowMenuCloser = useContext(OverflowMenuContext); - const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); - - let contextMenu: React.ReactElement | null = null; - if (menuDisplayed) { - const position = ( - menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()) - ); - - contextMenu = { - closeMenu(); - overflowMenuCloser?.(); - }} - managed={false} - > - - ; - } - - const className = classNames( - "mx_MessageComposer_button", - { - "mx_MessageComposer_button_highlight": menuDisplayed, - }, - ); - - // TODO: replace ContextMenuTooltipButton with a unified representation of - // the header buttons and the right panel buttons - return - - - { contextMenu } - ; -}; - function uploadButton(): ReactElement { return ; } diff --git a/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx b/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx index 380b0430cef..725341faf02 100644 --- a/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx @@ -20,6 +20,10 @@ import { useWysiwygSendActionHandler } from './hooks/useWysiwygSendActionHandler import { WysiwygComposer } from './components/WysiwygComposer'; import { PlainTextComposer } from './components/PlainTextComposer'; import { ComposerFunctions } from './types'; +import { E2EStatus } from '../../../../utils/ShieldUtils'; +import E2EIcon from '../E2EIcon'; +import { EmojiButton } from '../EmojiButton'; +import { AboveLeftOf } from '../../../structures/ContextMenu'; interface ContentProps { disabled: boolean; @@ -37,14 +41,23 @@ interface SendWysiwygComposerProps { initialContent?: string; isRichTextEnabled: boolean; disabled?: boolean; + e2eStatus?: E2EStatus; onChange: (content: string) => void; onSend: () => void; + menuPosition: AboveLeftOf; } -export function SendWysiwygComposer({ isRichTextEnabled, ...props }: SendWysiwygComposerProps) { +export function SendWysiwygComposer( + { isRichTextEnabled, e2eStatus, menuPosition, ...props }: SendWysiwygComposerProps) { const Composer = isRichTextEnabled ? WysiwygComposer : PlainTextComposer; - return + return } + // TODO add emoji support + rightComponent={ void 0} />} + {...props} + > { (ref, composerFunctions) => ( ) } diff --git a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx index cca66f6c387..57e59132bb3 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx @@ -14,27 +14,39 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { forwardRef, memo } from 'react'; +import React, { forwardRef, memo, ReactNode, RefObject } from 'react'; + +import { useIsExpanded } from '../hooks/useIsExpended'; + +const HEIGHT_BREAKING_POINT = 20; interface EditorProps { disabled: boolean; + leftComponent?: ReactNode; + rightComponent?: ReactNode; } export const Editor = memo( forwardRef( - function Editor({ disabled }: EditorProps, ref, + function Editor({ disabled, leftComponent, rightComponent }: EditorProps, ref: RefObject, ) { - return
-
+ const isExpanded = useIsExpanded(ref, HEIGHT_BREAKING_POINT); + + return
+ { leftComponent } +
+
+
+ { rightComponent }
; }, ), diff --git a/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx b/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx index e15b5ef57f7..c3a7d867557 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx @@ -29,6 +29,8 @@ interface PlainTextComposerProps { onSend: () => void; initialContent?: string; className?: string; + leftComponent?: ReactNode; + rightComponent?: ReactNode; children?: ( ref: MutableRefObject, composerFunctions: ComposerFunctions, @@ -36,7 +38,15 @@ interface PlainTextComposerProps { } export function PlainTextComposer({ - className, disabled, onSend, onChange, children, initialContent }: PlainTextComposerProps, + className, + disabled, + onSend, + onChange, + children, + initialContent, + leftComponent, + rightComponent, +}: PlainTextComposerProps, ) { const { ref, onInput, onPaste, onKeyDown } = usePlainTextListeners(onChange, onSend); const composerFunctions = useComposerFunctions(ref); @@ -50,7 +60,7 @@ export function PlainTextComposer({ onPaste={onPaste} onKeyDown={onKeyDown} > - + { children?.(ref, composerFunctions) }
; } diff --git a/src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer.tsx b/src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer.tsx index 974e89f0cee..2bbce769060 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer.tsx @@ -28,6 +28,8 @@ interface WysiwygComposerProps { onSend: () => void; initialContent?: string; className?: string; + leftComponent?: ReactNode; + rightComponent?: ReactNode; children?: ( ref: MutableRefObject, wysiwyg: FormattingFunctions, @@ -35,7 +37,16 @@ interface WysiwygComposerProps { } export const WysiwygComposer = memo(function WysiwygComposer( - { disabled = false, onChange, onSend, initialContent, className, children }: WysiwygComposerProps, + { + disabled = false, + onChange, + onSend, + initialContent, + className, + leftComponent, + rightComponent, + children, + }: WysiwygComposerProps, ) { const inputEventProcessor = useInputEventProcessor(onSend); @@ -54,7 +65,7 @@ export const WysiwygComposer = memo(function WysiwygComposer( return (
- + { children?.(ref, wysiwyg) }
); diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpended.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpended.ts new file mode 100644 index 00000000000..e020d8f0e9e --- /dev/null +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpended.ts @@ -0,0 +1,35 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 { MutableRefObject, useEffect, useState } from "react"; + +export function useIsExpanded(ref: MutableRefObject, breakingPoint: number) { + const [isExpanded, setIsExpanded] = useState(false); + useEffect(() => { + if (ref.current) { + const editor = ref.current; + const resizeObserver = new ResizeObserver(entries => { + const height = entries[0]?.contentBoxSize?.[0].blockSize; + setIsExpanded(height >= breakingPoint); + }); + + resizeObserver.observe(editor); + return () => resizeObserver.unobserve(editor); + } + }, [ref, breakingPoint]); + + return isExpanded; +} From 1737c21012fe915bfef27de7046209b6885c91ea Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 10:23:10 +0100 Subject: [PATCH 02/31] Final design for composer --- .../rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index b26527671b9..a40022b88b7 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -28,24 +28,20 @@ limitations under the License. gap: 8px; .mx_E2EIcon { - margin: 0px 14px 5px 14px; - // margin-bottom: 3px; + margin-bottom: 5px; } .mx_E2EIcon_normal::after { background-color: $quaternary-content; } - .mx_EmojiButton { - margin: 0px 14px 0px 14px; - } - .mx_WysiwygComposer_Editor { border: 1px solid; - border-color: $quinary-content; - padding: 8px 0px 8px; + border-color: $quaternary-content; + padding: 8px 14px 8px; display: flex; align-items: flex-end; + gap: 14px; &[data-is-expanded="true"] { border-radius: 14px; From 479aa459ed2417565843610ff70815b904873ad2 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 12:01:33 +0100 Subject: [PATCH 03/31] Fix growing composer --- .../_SendWysiwygComposer.pcss | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index a40022b88b7..8b32bdfc388 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -23,28 +23,30 @@ limitations under the License. line-height: $font-18px; justify-content: center; margin-right: 6px; - /* don't grow wider than available space */ - min-width: 0; gap: 8px; - .mx_E2EIcon { - margin-bottom: 5px; - } - - .mx_E2EIcon_normal::after { - background-color: $quaternary-content; - } - .mx_WysiwygComposer_Editor { border: 1px solid; border-color: $quaternary-content; padding: 8px 14px 8px; display: flex; align-items: flex-end; - gap: 14px; + gap: 10px; + + .mx_E2EIcon { + margin: 0 0 5px 0; + } + + .mx_E2EIcon_normal::after { + background-color: $quaternary-content; + } &[data-is-expanded="true"] { border-radius: 14px; + + .mx_E2EIcon { + margin: 0 0 3px 0; + } } &[data-is-expanded="false"] { @@ -57,6 +59,8 @@ limitations under the License. flex-direction: column; min-height: 22px; margin-bottom: 2px; + /* don't grow wider than available space */ + min-width: 0; .mx_WysiwygComposer_Editor_content { /* this will center the contenteditable */ From 1055dd4e5699777d7f3d39a48404997259f2aa7f Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 13:59:52 +0100 Subject: [PATCH 04/31] Fix formatting buttons in edition --- .../rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 4 ++++ .../components/_FormattingButtons.pcss | 10 +--------- src/components/views/rooms/MessageComposer.tsx | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 8b32bdfc388..770be1012d4 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -25,6 +25,10 @@ limitations under the License. margin-right: 6px; gap: 8px; + .mx_FormattingButtons { + margin-left: 12px; + } + .mx_WysiwygComposer_Editor { border: 1px solid; border-color: $quaternary-content; diff --git a/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss b/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss index cd0ac38e0ed..76026ff9381 100644 --- a/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss +++ b/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss @@ -17,6 +17,7 @@ limitations under the License. .mx_FormattingButtons { display: flex; justify-content: flex-start; + gap: 8px; .mx_FormattingButtons_Button { --size: 28px; @@ -26,18 +27,9 @@ limitations under the License. line-height: var(--size); width: auto; padding-left: 22px; - margin-right: 8px; background-color: transparent; border: none; - &:first-child { - margin-left: 12px; - } - - &:last-child { - margin-right: auto; - } - &::before { content: ''; position: absolute; diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index e5694d3a4c1..ca3da807951 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -31,7 +31,7 @@ import Stickerpicker from './Stickerpicker'; import { makeRoomPermalink, RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import E2EIcon from './E2EIcon'; import SettingsStore from "../../../settings/SettingsStore"; -import { aboveLeftOf, AboveLeftOf } from "../../structures/ContextMenu"; +import { aboveLeftOf } from "../../structures/ContextMenu"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import ReplyPreview from "./ReplyPreview"; import { UPDATE_EVENT } from "../../../stores/AsyncStore"; @@ -428,7 +428,7 @@ export class MessageComposer extends React.Component { // Instead of doing a querySelector or pass a ref to find the compute the height formatting buttons // We are using an arbitrary value, the formatting buttons height doesn't change during the lifecycle of the component // It's easier to just use a constant here instead of an over-engineering way to find the height - const heightToRemove = hasFormattingButtons ? 45 : 0; + const heightToRemove = hasFormattingButtons ? 36 : 0; const fixedRect = new DOMRect( contentRect.x, contentRect.y + heightToRemove, From ef7f7f9a2061ca29a34068f2cebd8617bc5b652b Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 14:08:27 +0100 Subject: [PATCH 05/31] Fix copyright and hook name --- res/css/views/rooms/_EmojiButton.pcss | 16 ++++++++++++++++ res/css/views/rooms/_MessageComposerButton.pcss | 16 ++++++++++++++++ .../rooms/wysiwyg_composer/components/Editor.tsx | 2 +- .../hooks/{useIsExpended.ts => useIsExpanded.ts} | 0 4 files changed, 33 insertions(+), 1 deletion(-) rename src/components/views/rooms/wysiwyg_composer/hooks/{useIsExpended.ts => useIsExpanded.ts} (100%) diff --git a/res/css/views/rooms/_EmojiButton.pcss b/res/css/views/rooms/_EmojiButton.pcss index 544ecdfa73c..4d724d2d58a 100644 --- a/res/css/views/rooms/_EmojiButton.pcss +++ b/res/css/views/rooms/_EmojiButton.pcss @@ -1,3 +1,19 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 "./_MessageComposerButton.pcss"; .mx_EmojiButton { diff --git a/res/css/views/rooms/_MessageComposerButton.pcss b/res/css/views/rooms/_MessageComposerButton.pcss index 81841165eb2..271d1435534 100644 --- a/res/css/views/rooms/_MessageComposerButton.pcss +++ b/res/css/views/rooms/_MessageComposerButton.pcss @@ -1,3 +1,19 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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. +*/ + @define-mixin composerButtonHighLight { background: rgba($accent, 0.25); /* make the icon the accent color too */ diff --git a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx index 57e59132bb3..bb70571ea4f 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx @@ -16,7 +16,7 @@ limitations under the License. import React, { forwardRef, memo, ReactNode, RefObject } from 'react'; -import { useIsExpanded } from '../hooks/useIsExpended'; +import { useIsExpanded } from '../hooks/useIsExpanded'; const HEIGHT_BREAKING_POINT = 20; diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpended.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts similarity index 100% rename from src/components/views/rooms/wysiwyg_composer/hooks/useIsExpended.ts rename to src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts From 68a42167d18b2e05daaa38f4ab966ab3c7d5f5e8 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 14:13:00 +0100 Subject: [PATCH 06/31] Update i18n --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 966f3c04e32..a653d9d3561 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1829,6 +1829,7 @@ "This room is end-to-end encrypted": "This room is end-to-end encrypted", "Everyone in this room is verified": "Everyone in this room is verified", "Edit message": "Edit message", + "Emoji": "Emoji", "Mod": "Mod", "From a thread": "From a thread", "This event could not be displayed": "This event could not be displayed", @@ -1878,7 +1879,6 @@ "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", "Send voice message": "Send voice message", - "Emoji": "Emoji", "Hide stickers": "Hide stickers", "Sticker": "Sticker", "Voice Message": "Voice Message", From 275e3a0ca7d24f0471b5377342662fd110a5e46e Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 14:22:07 +0100 Subject: [PATCH 07/31] Fix types in test --- .../views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx index c85692d221a..3b5b8885d8f 100644 --- a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx @@ -28,6 +28,7 @@ import { createTestClient, flushPromises, getRoomContext, mkEvent, mkStubRoom } import { SendWysiwygComposer } from "../../../../../src/components/views/rooms/wysiwyg_composer"; import * as useComposerFunctions from "../../../../../src/components/views/rooms/wysiwyg_composer/hooks/useComposerFunctions"; +import { aboveLeftOf } from "../../../../../src/components/structures/ContextMenu"; const mockClear = jest.fn(); @@ -78,7 +79,7 @@ describe('SendWysiwygComposer', () => { return render( - + , ); From a535796a9d8dfbae75b34e14012ca5f5fe6239b0 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 14:35:57 +0100 Subject: [PATCH 08/31] Fix send button position --- res/css/views/rooms/_MessageComposer.pcss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/views/rooms/_MessageComposer.pcss b/res/css/views/rooms/_MessageComposer.pcss index d5907126bfc..5c0782922b2 100644 --- a/res/css/views/rooms/_MessageComposer.pcss +++ b/res/css/views/rooms/_MessageComposer.pcss @@ -213,6 +213,10 @@ limitations under the License. .mx_MessageComposer_button { margin-bottom: 9px; } + + .mx_MessageComposer_sendMessage { + margin-bottom: 7px; + } } .mx_MessageComposer_upload::before { From 10c8732c1eb4a908d6ede406791c3ef1f7f74b22 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 14:47:24 +0100 Subject: [PATCH 09/31] Cleaner button placement --- res/css/views/rooms/_MessageComposer.pcss | 15 ++-- .../views/rooms/MessageComposer.tsx | 74 ++++++++++--------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/res/css/views/rooms/_MessageComposer.pcss b/res/css/views/rooms/_MessageComposer.pcss index 5c0782922b2..10b0e8de6c4 100644 --- a/res/css/views/rooms/_MessageComposer.pcss +++ b/res/css/views/rooms/_MessageComposer.pcss @@ -61,6 +61,12 @@ limitations under the License. width: 100%; } +.mx_MessageComposer_actions { + display: flex; + align-items: center; + gap: 6px; +} + .mx_MessageComposer .mx_MessageComposer_avatar { position: absolute; left: 26px; @@ -178,7 +184,6 @@ limitations under the License. .mx_MessageComposer_button { @mixin composerButton; - margin-right: 6px; &:last-child { margin-right: auto; @@ -210,12 +215,8 @@ limitations under the License. align-items: flex-end; } - .mx_MessageComposer_button { - margin-bottom: 9px; - } - - .mx_MessageComposer_sendMessage { - margin-bottom: 7px; + .mx_MessageComposer_actions { + height: 44px; } } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index ca3da807951..287b4be11b1 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -558,43 +558,45 @@ export class MessageComposer extends React.Component { permalinkCreator={this.props.permalinkCreator} />
{ controls } - { canSendMessages && { - this.voiceRecordingButton.current?.onRecordStartEndClick(); - if (this.context.narrow) { +
+ { canSendMessages && { + this.voiceRecordingButton.current?.onRecordStartEndClick(); + if (this.context.narrow) { + this.toggleButtonMenu(); + } + }} + setStickerPickerOpen={this.setStickerPickerOpen} + showLocationButton={!window.electron} + showPollsButton={this.state.showPollsButton} + showStickersButton={this.showStickersButton} + isRichTextEnabled={this.state.isRichTextEnabled} + onComposerModeClick={this.onRichTextToggle} + toggleButtonMenu={this.toggleButtonMenu} + showVoiceBroadcastButton={this.state.showVoiceBroadcastButton} + onStartVoiceBroadcastClick={() => { + startNewVoiceBroadcastRecording( + this.props.room, + MatrixClientPeg.get(), + VoiceBroadcastRecordingsStore.instance(), + ); this.toggleButtonMenu(); - } - }} - setStickerPickerOpen={this.setStickerPickerOpen} - showLocationButton={!window.electron} - showPollsButton={this.state.showPollsButton} - showStickersButton={this.showStickersButton} - isRichTextEnabled={this.state.isRichTextEnabled} - onComposerModeClick={this.onRichTextToggle} - toggleButtonMenu={this.toggleButtonMenu} - showVoiceBroadcastButton={this.state.showVoiceBroadcastButton} - onStartVoiceBroadcastClick={() => { - startNewVoiceBroadcastRecording( - this.props.room, - MatrixClientPeg.get(), - VoiceBroadcastRecordingsStore.instance(), - ); - this.toggleButtonMenu(); - }} - /> } - { showSendButton && ( - - ) } + }} + /> } + { showSendButton && ( + + ) } +
From 4645fc7c8e6efb92b2dfee7f8701be1cdf5c80ee Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 15:33:58 +0100 Subject: [PATCH 10/31] Smaller editor --- res/css/views/rooms/_MessageComposer.pcss | 3 ++- .../views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 2 +- res/img/element-icons/room/composer/plain_text.svg | 7 ++++--- res/img/element-icons/room/composer/rich_text.svg | 7 +++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/res/css/views/rooms/_MessageComposer.pcss b/res/css/views/rooms/_MessageComposer.pcss index 10b0e8de6c4..ee4ecd011e6 100644 --- a/res/css/views/rooms/_MessageComposer.pcss +++ b/res/css/views/rooms/_MessageComposer.pcss @@ -216,7 +216,8 @@ limitations under the License. } .mx_MessageComposer_actions { - height: 44px; + /* Height of the composer editor */ + height: 40px; } } diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 770be1012d4..421cc23b32d 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -32,7 +32,7 @@ limitations under the License. .mx_WysiwygComposer_Editor { border: 1px solid; border-color: $quaternary-content; - padding: 8px 14px 8px; + padding: 6px 14px 6px; display: flex; align-items: flex-end; gap: 10px; diff --git a/res/img/element-icons/room/composer/plain_text.svg b/res/img/element-icons/room/composer/plain_text.svg index d2da9d25516..7ff47fe085c 100644 --- a/res/img/element-icons/room/composer/plain_text.svg +++ b/res/img/element-icons/room/composer/plain_text.svg @@ -1,9 +1,10 @@ - - + + + - + diff --git a/res/img/element-icons/room/composer/rich_text.svg b/res/img/element-icons/room/composer/rich_text.svg index 7ff47fe085c..d2da9d25516 100644 --- a/res/img/element-icons/room/composer/rich_text.svg +++ b/res/img/element-icons/room/composer/rich_text.svg @@ -1,10 +1,9 @@ - - - + + - + From b63f9d96a6f592e8397ea51ed4ae97c5875b4b65 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 15:58:03 +0100 Subject: [PATCH 11/31] Fix bottom input when editor is expanded --- .../views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 421cc23b32d..4e8700f7363 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -48,8 +48,8 @@ limitations under the License. &[data-is-expanded="true"] { border-radius: 14px; - .mx_E2EIcon { - margin: 0 0 3px 0; + .mx_WysiwygComposer_Editor_container { + margin-bottom: 4px; } } From 27463199daacb23452247571443043d977907258 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 16:40:17 +0100 Subject: [PATCH 12/31] Lot of visual fix --- res/css/views/rooms/_MessageComposer.pcss | 5 ++++ .../views/rooms/_VoiceRecordComposerTile.pcss | 14 ++++++++--- .../_SendWysiwygComposer.pcss | 10 ++++---- .../views/rooms/MessageComposer.tsx | 24 +++++++++---------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/res/css/views/rooms/_MessageComposer.pcss b/res/css/views/rooms/_MessageComposer.pcss index ee4ecd011e6..46718b7e7a7 100644 --- a/res/css/views/rooms/_MessageComposer.pcss +++ b/res/css/views/rooms/_MessageComposer.pcss @@ -219,6 +219,11 @@ limitations under the License. /* Height of the composer editor */ height: 40px; } + + .mx_MediaBody { + padding-top: 4px; + padding-bottom: 4px; + } } .mx_MessageComposer_upload::before { diff --git a/res/css/views/rooms/_VoiceRecordComposerTile.pcss b/res/css/views/rooms/_VoiceRecordComposerTile.pcss index 5443eca9275..26f206f9181 100644 --- a/res/css/views/rooms/_VoiceRecordComposerTile.pcss +++ b/res/css/views/rooms/_VoiceRecordComposerTile.pcss @@ -20,7 +20,7 @@ limitations under the License. height: 28px; border: 2px solid $voice-record-stop-border-color; border-radius: 32px; - margin-right: 8px; /* between us and the waveform component */ + margin-right: 2px; /* between us and the waveform component */ position: relative; &::after { @@ -39,7 +39,7 @@ limitations under the License. width: 24px; height: 24px; vertical-align: middle; - margin-right: 8px; /* distance from left edge of waveform container (container has some margin too) */ + margin-right: 2px; /* distance from left edge of waveform container (container has some margin too) */ background-color: $voice-record-icon-color; mask-repeat: no-repeat; mask-size: contain; @@ -69,7 +69,7 @@ limitations under the License. height: 32px; margin: 6px; /* force the composer area to put a gutter around us */ - margin-right: 12px; /* isolate from stop/send button */ + margin-right: 6px; /* isolate from stop/send button */ position: relative; /* important for the live circle */ @@ -93,6 +93,14 @@ limitations under the License. } } +.mx_MessageComposer_wysiwyg .mx_VoiceMessagePrimaryContainer { + &.mx_VoiceRecordComposerTile_recording { + &::before { + top: 15px; /* vertically center (middle align with clock) */ + } + } +} + /* The keyframes are slightly weird here to help make a ramping/punch effect */ /* for the recording dot. We start and end at 100% opacity to help make the */ /* dot feel a bit like a real lamp that is blinking: the animation ends up */ diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 4e8700f7363..1a11c8efa9c 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -32,17 +32,15 @@ limitations under the License. .mx_WysiwygComposer_Editor { border: 1px solid; border-color: $quaternary-content; - padding: 6px 14px 6px; + padding: 6px 11px 6px 12px; display: flex; align-items: flex-end; gap: 10px; .mx_E2EIcon { - margin: 0 0 5px 0; - } - - .mx_E2EIcon_normal::after { - background-color: $quaternary-content; + margin: 0 0 7px 0; + width: 12px; + height: 12px; } &[data-is-expanded="true"] { diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 287b4be11b1..1f6344a887e 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { createRef } from 'react'; +import React, { createRef, ReactNode } from 'react'; import classNames from 'classnames'; import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; @@ -440,17 +440,17 @@ export class MessageComposer extends React.Component { public render() { const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus); - const controls = [ - hasE2EIcon && - , - ]; + const e2eIcon = hasE2EIcon && + ; + const controls: ReactNode[] = []; const menuPosition = this.getMenuPosition(); const canSendMessages = this.context.canSendMessages && !this.context.tombstone; + let composer: ReactNode; if (canSendMessages) { if (this.state.isWysiwygLabEnabled) { - controls.push( + composer = { initialContent={this.state.initialComposerContent} e2eStatus={this.props.e2eStatus} menuPosition={menuPosition} - />, - ); + />; } else { - controls.push( + composer = { onChange={this.onChange} disabled={this.state.haveRecording} toggleStickerPickerOpen={this.toggleStickerPickerOpen} - />, - ); + />; } controls.push( { replyToEvent={this.props.replyToEvent} permalinkCreator={this.props.permalinkCreator} />
- { controls } + { e2eIcon } + { composer }
+ { controls } { canSendMessages && Date: Wed, 2 Nov 2022 17:09:37 +0100 Subject: [PATCH 13/31] Change outline color when editor has focused --- .../_SendWysiwygComposer.pcss | 8 +++++- .../components/PlainTextComposer.tsx | 7 ++++- .../components/WysiwygComposer.tsx | 6 ++++- .../wysiwyg_composer/hooks/useIsFocused.ts | 27 +++++++++++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 1a11c8efa9c..b136d49e01e 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -31,7 +31,7 @@ limitations under the License. .mx_WysiwygComposer_Editor { border: 1px solid; - border-color: $quaternary-content; + border-color: $quinary-content; padding: 6px 11px 6px 12px; display: flex; align-items: flex-end; @@ -77,3 +77,9 @@ limitations under the License. } } } + +.mx_SendWysiwygComposer-focused { + .mx_WysiwygComposer_Editor { + border-color: $quaternary-content; + } +} diff --git a/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx b/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx index c3a7d867557..c134de0f8dd 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer.tsx @@ -14,9 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +import classNames from 'classnames'; import React, { MutableRefObject, ReactNode } from 'react'; import { useComposerFunctions } from '../hooks/useComposerFunctions'; +import { useIsFocused } from '../hooks/useIsFocused'; import { usePlainTextInitialization } from '../hooks/usePlainTextInitialization'; import { usePlainTextListeners } from '../hooks/usePlainTextListeners'; import { useSetCursorPosition } from '../hooks/useSetCursorPosition'; @@ -52,10 +54,13 @@ export function PlainTextComposer({ const composerFunctions = useComposerFunctions(ref); usePlainTextInitialization(initialContent, ref); useSetCursorPosition(disabled, ref); + const { isFocused, onFocus } = useIsFocused(); return
+
{ children?.(ref, wysiwyg) } diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts new file mode 100644 index 00000000000..90eee53b788 --- /dev/null +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts @@ -0,0 +1,27 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 { FocusEvent, useCallback, useState } from "react"; + +export function useIsFocused() { + const [isFocused, setIsFocused] = useState(false); + + const onFocus = useCallback((event: FocusEvent) => { + setIsFocused(event.type === 'focus'); + }, [setIsFocused]); + + return { isFocused, onFocus }; +} From 7fbd4b7f0538724b2a490ccda93286597956dc9e Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 18:05:34 +0100 Subject: [PATCH 14/31] Rework the plain text icon --- .../room/composer/plain_text.svg | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/res/img/element-icons/room/composer/plain_text.svg b/res/img/element-icons/room/composer/plain_text.svg index 7ff47fe085c..874ae1a47da 100644 --- a/res/img/element-icons/room/composer/plain_text.svg +++ b/res/img/element-icons/room/composer/plain_text.svg @@ -1,11 +1,34 @@ - - - - - - - - - - + + + + + + + + + + + From 5092acd317d5f9db59090be4f969df6fd663a2ca Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 18:28:14 +0100 Subject: [PATCH 15/31] Fix blinking when switching mode --- .../rooms/wysiwyg_composer/hooks/useIsFocused.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts index 90eee53b788..0d84c66f649 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts @@ -14,12 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { FocusEvent, useCallback, useState } from "react"; +import { FocusEvent, useCallback, useEffect, useState } from "react"; export function useIsFocused() { const [isFocused, setIsFocused] = useState(false); + const [timeoutID, setTimeoutID] = useState(); + useEffect(() => { + return () => clearTimeout(timeoutID); + }, [timeoutID]); + const onFocus = useCallback((event: FocusEvent) => { + if (event.type === 'focus') { + setIsFocused(true); + } else { + // To avoid a blink when we switch mode between plain text and rich text mode + // We delay the unfocused action + setTimeoutID(setTimeout(() => setIsFocused(false), 500)); + } setIsFocused(event.type === 'focus'); }, [setIsFocused]); From d92bffc1162117f9494ec0310c206009d46da5c2 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 18:41:05 +0100 Subject: [PATCH 16/31] Increase timer in blur --- .../views/rooms/wysiwyg_composer/hooks/useIsFocused.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts index 0d84c66f649..b5676731d89 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts @@ -30,7 +30,7 @@ export function useIsFocused() { } else { // To avoid a blink when we switch mode between plain text and rich text mode // We delay the unfocused action - setTimeoutID(setTimeout(() => setIsFocused(false), 500)); + setTimeoutID(setTimeout(() => setIsFocused(false), 1000)); } setIsFocused(event.type === 'focus'); }, [setIsFocused]); From 8b9cf19fdba790a23f930c1c78859f92642a50d4 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 2 Nov 2022 18:55:03 +0100 Subject: [PATCH 17/31] Remove useless e2e icon key --- .../views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx b/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx index 725341faf02..b5d7f3dca27 100644 --- a/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx +++ b/src/components/views/rooms/wysiwyg_composer/SendWysiwygComposer.tsx @@ -53,7 +53,7 @@ export function SendWysiwygComposer( return } + leftComponent={e2eStatus && } // TODO add emoji support rightComponent={ void 0} />} {...props} From 2a7834fb11e6196de9d34897d4739a228b655b41 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 3 Nov 2022 09:43:04 +0100 Subject: [PATCH 18/31] fix isfocused timeout --- .../rooms/wysiwyg_composer/hooks/useIsFocused.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts index b5676731d89..2a3ca10e594 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts @@ -14,26 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { FocusEvent, useCallback, useEffect, useState } from "react"; +import { FocusEvent, useCallback, useRef, useState } from "react"; export function useIsFocused() { const [isFocused, setIsFocused] = useState(false); - - const [timeoutID, setTimeoutID] = useState(); - useEffect(() => { - return () => clearTimeout(timeoutID); - }, [timeoutID]); + const timeoutIDRef = useRef(); const onFocus = useCallback((event: FocusEvent) => { + clearTimeout(timeoutIDRef.current); if (event.type === 'focus') { setIsFocused(true); } else { + console.log('blur'); // To avoid a blink when we switch mode between plain text and rich text mode // We delay the unfocused action - setTimeoutID(setTimeout(() => setIsFocused(false), 1000)); + timeoutIDRef.current = setTimeout(() => setIsFocused(false), 100); } - setIsFocused(event.type === 'focus'); - }, [setIsFocused]); + }, [setIsFocused, timeoutIDRef]); return { isFocused, onFocus }; } From c5fd30244019cfa74c63a6a494bcdc928feac1b6 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 3 Nov 2022 09:58:51 +0100 Subject: [PATCH 19/31] Fix tests --- .../wysiwyg_composer/components/Editor.tsx | 6 ++- .../wysiwyg_composer/hooks/useIsFocused.ts | 1 - .../__snapshots__/RoomView-test.tsx.snap | 4 +- .../components/PlainTextComposer-test.tsx | 40 +++++++++++++++++-- .../components/WysiwygComposer-test.tsx | 4 -- test/setup/setupManualMocks.ts | 23 +++++++++++ 6 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx index bb70571ea4f..4f114762b8a 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx @@ -32,7 +32,11 @@ export const Editor = memo( ) { const isExpanded = useIsExpanded(ref, HEIGHT_BREAKING_POINT); - return
+ return
{ leftComponent }
setIsFocused(false), 100); diff --git a/test/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/components/structures/__snapshots__/RoomView-test.tsx.snap index 9b5c415f116..ddb5489a987 100644 --- a/test/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/test/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -4,6 +4,6 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1 exports[`RoomView for a local room in state ERROR should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.
    U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat

!
Some of your messages have not been sent
Retry
"`; -exports[`RoomView for a local room in state NEW should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.
    U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; +exports[`RoomView for a local room in state NEW should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.
    U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; -exports[`RoomView for a local room in state NEW that is encrypted should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
    Encryption enabled
    Messages in this chat will be end-to-end encrypted.
  1. U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; +exports[`RoomView for a local room in state NEW that is encrypted should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
    Encryption enabled
    Messages in this chat will be end-to-end encrypted.
  1. U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; diff --git a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx index 5d1b03020cf..5d53b0e4590 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx @@ -21,10 +21,6 @@ import userEvent from "@testing-library/user-event"; import { PlainTextComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer"; -// Work around missing ClipboardEvent type -class MyClipboardEvent {} -window.ClipboardEvent = MyClipboardEvent as any; - describe('PlainTextComposer', () => { const customRender = ( onChange = (_content: string) => void 0, @@ -91,4 +87,40 @@ describe('PlainTextComposer', () => { // Then expect(screen.getByRole('textbox').innerHTML).toBeFalsy(); }); + + it('Should have data-is-expanded when it has two lines', async () => { + let resizeHandler; + let editor; + const resizeObserver = ResizeObserver; + global.ResizeObserver = jest.fn((handler) => { + resizeHandler = handler; + return { + observe: (element) => { + editor = element; + }, + unobserve: jest.fn(), + disconnect: jest.fn(), + }; + }, + ); + + //When + render( + , + ); + + console.log(screen.getByTestId('WysiwygComposerEditor'), screen.getByTestId('WysiwygComposerEditor').dataset); + + // Then + expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('false'); + expect(editor).toBe(screen.getByRole('textbox')); + + // When + resizeHandler([{ contentBoxSize: [{ blockSize: 100 }] }]); + + // Then + expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('true'); + + global.ResizeObserver = resizeObserver; + }); }); diff --git a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx index 7e3db04abcf..64be2edfb36 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx @@ -23,10 +23,6 @@ import { WysiwygComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer"; import SettingsStore from "../../../../../../src/settings/SettingsStore"; -// Work around missing ClipboardEvent type -class MyClipboardEvent {} -window.ClipboardEvent = MyClipboardEvent as any; - let inputEventProcessor: InputEventProcessor | null = null; // The wysiwyg fetch wasm bytes and a specific workaround is needed to make it works in a node (jest) environnement diff --git a/test/setup/setupManualMocks.ts b/test/setup/setupManualMocks.ts index 2adda89e0f1..1594af4f5fc 100644 --- a/test/setup/setupManualMocks.ts +++ b/test/setup/setupManualMocks.ts @@ -31,6 +31,29 @@ class ResizeObserver { } window.ResizeObserver = ResizeObserver; +// Stub DOMRect +class DOMRect { + x = 0; + y = 0; + top = 0; + bottom = 0; + left = 0; + right = 0; + height = 0; + width = 0; + + static fromRect() { + return new DOMRect(); + } + toJSON() {} +} + +window.DOMRect = DOMRect; + +// Work around missing ClipboardEvent type +class MyClipboardEvent {} +window.ClipboardEvent = MyClipboardEvent as any; + // matchMedia is not included in jsdom const mockMatchMedia = jest.fn().mockImplementation(query => ({ matches: false, From 52adb2cd03462b846e72ff11befe918cb6917c00 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 3 Nov 2022 10:37:39 +0100 Subject: [PATCH 20/31] Fix margin in case of multiple lines --- res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index b136d49e01e..12f7c61195a 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -47,7 +47,8 @@ limitations under the License. border-radius: 14px; .mx_WysiwygComposer_Editor_container { - margin-bottom: 4px; + margin-top: 3px; + margin-bottom: 3px; } } From a3e5511870b69bd38846ee4a1204e1d4ed5347de Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 3 Nov 2022 14:53:13 +0100 Subject: [PATCH 21/31] Fix resize observer error --- cypress/support/composer.ts | 8 ++++++++ src/components/views/rooms/MessageComposerButtons.tsx | 2 +- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cypress/support/composer.ts b/cypress/support/composer.ts index ae6c8bef871..a611195f629 100644 --- a/cypress/support/composer.ts +++ b/cypress/support/composer.ts @@ -32,6 +32,14 @@ declare global { } } +const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/; +Cypress.on('uncaught:exception', (err) => { + /* returning false here prevents Cypress from failing the test */ + if (resizeObserverLoopErrRe.test(err.message)) { + return false; + } +}); + Cypress.Commands.add("getComposer", (isRightPanel?: boolean): Chainable => { const panelClass = isRightPanel ? '.mx_RightPanel' : '.mx_RoomView_body'; return cy.get(`${panelClass} .mx_MessageComposer`); diff --git a/src/components/views/rooms/MessageComposerButtons.tsx b/src/components/views/rooms/MessageComposerButtons.tsx index 8ad660379d7..48f40daef20 100644 --- a/src/components/views/rooms/MessageComposerButtons.tsx +++ b/src/components/views/rooms/MessageComposerButtons.tsx @@ -364,7 +364,7 @@ interface WysiwygToggleButtonProps { } function ComposerModeButton({ isRichTextEnabled, onClick }: WysiwygToggleButtonProps) { - const title = isRichTextEnabled ? _t("Show plain text") : _t("Show formatting"); + const title = isRichTextEnabled ? _t("Hide formatting") : _t("Show formatting"); return Date: Thu, 3 Nov 2022 17:00:47 +0100 Subject: [PATCH 22/31] improve rich text composer design --- res/css/views/rooms/_EmojiButton.pcss | 8 ++++++- res/css/views/rooms/_MessageComposer.pcss | 21 +++++++++++++++++-- .../views/rooms/_MessageComposerButton.pcss | 16 +++++++++++--- .../_SendWysiwygComposer.pcss | 2 +- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/res/css/views/rooms/_EmojiButton.pcss b/res/css/views/rooms/_EmojiButton.pcss index 4d724d2d58a..1720a9ce0d3 100644 --- a/res/css/views/rooms/_EmojiButton.pcss +++ b/res/css/views/rooms/_EmojiButton.pcss @@ -17,7 +17,7 @@ limitations under the License. @import "./_MessageComposerButton.pcss"; .mx_EmojiButton { - @mixin composerButton; + @mixin composerButton 50%,$accent; } .mx_EmojiButton_highlight { @@ -27,3 +27,9 @@ limitations under the License. .mx_EmojiButton_icon::before { mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg'); } + +.mx_MessageComposer_wysiwyg { + .mx_EmojiButton { + @mixin composerButton 5px,$tertiary-content; + } +} diff --git a/res/css/views/rooms/_MessageComposer.pcss b/res/css/views/rooms/_MessageComposer.pcss index 46718b7e7a7..95c7e2dd749 100644 --- a/res/css/views/rooms/_MessageComposer.pcss +++ b/res/css/views/rooms/_MessageComposer.pcss @@ -183,13 +183,12 @@ limitations under the License. } .mx_MessageComposer_button { - @mixin composerButton; + @mixin composerButton 50%,$accent; &:last-child { margin-right: auto; } - &:hover, &.mx_MessageComposer_closeButtonMenu { &::after { background: rgba($accent, 0.1); @@ -224,6 +223,24 @@ limitations under the License. padding-top: 4px; padding-bottom: 4px; } + + .mx_MessageComposer_button { + @mixin composerButton 5px,$tertiary-content; + + &.mx_MessageComposer_closeButtonMenu { + &::after { + background: rgba($accent, 0.1); + } + + &::before { + background-color: $accent; + } + } + + &.mx_MessageComposer_hangup:not(.mx_AccessibleButton_disabled)::before { + background-color: $alert; + } + } } .mx_MessageComposer_upload::before { diff --git a/res/css/views/rooms/_MessageComposerButton.pcss b/res/css/views/rooms/_MessageComposerButton.pcss index 271d1435534..e21c556207a 100644 --- a/res/css/views/rooms/_MessageComposerButton.pcss +++ b/res/css/views/rooms/_MessageComposerButton.pcss @@ -22,7 +22,7 @@ limitations under the License. } } -@define-mixin composerButton { +@define-mixin composerButton $border-radius,$hover-color { --size: 26px; position: relative; cursor: pointer; @@ -30,7 +30,7 @@ limitations under the License. line-height: var(--size); width: auto; padding-left: var(--size); - border-radius: 50%; + border-radius: $border-radius; &::before { content: ''; @@ -53,6 +53,16 @@ limitations under the License. z-index: 0; width: var(--size); height: var(--size); - border-radius: 50%; + border-radius: $border-radius; + } + + &:hover { + &::after { + background: rgba($hover-color, 0.1); + } + + &::before { + background-color: $hover-color; + } } } diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 12f7c61195a..2f3d19b7977 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -22,7 +22,7 @@ limitations under the License. /* fixed line height to prevent emoji from being taller than text */ line-height: $font-18px; justify-content: center; - margin-right: 6px; + margin-right: 15px; gap: 8px; .mx_FormattingButtons { From 4fdec530871f5581e831def0e62209661da53ac2 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 10:59:46 +0100 Subject: [PATCH 23/31] Review fixes --- .../views/rooms/MessageComposerButtons.tsx | 12 ++++++------ .../rooms/wysiwyg_composer/hooks/useIsFocused.ts | 3 ++- .../components/PlainTextComposer-test.tsx | 11 ++++++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/MessageComposerButtons.tsx b/src/components/views/rooms/MessageComposerButtons.tsx index 48f40daef20..49ac98b533f 100644 --- a/src/components/views/rooms/MessageComposerButtons.tsx +++ b/src/components/views/rooms/MessageComposerButtons.tsx @@ -77,9 +77,9 @@ const MessageComposerButtons: React.FC = (props: IProps) => { let moreButtons: ReactElement[]; if (narrow) { mainButtons = [ - !isWysiwygLabEnabled && emojiButton(props), - isWysiwygLabEnabled && - , + isWysiwygLabEnabled ? + : + emojiButton(props), ]; moreButtons = [ uploadButton(), // props passed via UploadButtonContext @@ -91,9 +91,9 @@ const MessageComposerButtons: React.FC = (props: IProps) => { ]; } else { mainButtons = [ - !isWysiwygLabEnabled && emojiButton(props), - isWysiwygLabEnabled && - , + isWysiwygLabEnabled ? + : + emojiButton(props), uploadButton(), // props passed via UploadButtonContext ]; moreButtons = [ diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts index c17c221f6c3..99e6dbd9c8a 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsFocused.ts @@ -14,12 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { FocusEvent, useCallback, useRef, useState } from "react"; +import { FocusEvent, useCallback, useEffect, useRef, useState } from "react"; export function useIsFocused() { const [isFocused, setIsFocused] = useState(false); const timeoutIDRef = useRef(); + useEffect(() => () => clearTimeout(timeoutIDRef.current), [timeoutIDRef]); const onFocus = useCallback((event: FocusEvent) => { clearTimeout(timeoutIDRef.current); if (event.type === 'focus') { diff --git a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx index 5d53b0e4590..b81eef7d935 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx @@ -89,8 +89,8 @@ describe('PlainTextComposer', () => { }); it('Should have data-is-expanded when it has two lines', async () => { - let resizeHandler; - let editor; + let resizeHandler: ResizeObserverCallback; + let editor: Element; const resizeObserver = ResizeObserver; global.ResizeObserver = jest.fn((handler) => { resizeHandler = handler; @@ -109,14 +109,15 @@ describe('PlainTextComposer', () => { , ); - console.log(screen.getByTestId('WysiwygComposerEditor'), screen.getByTestId('WysiwygComposerEditor').dataset); - // Then expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('false'); expect(editor).toBe(screen.getByRole('textbox')); // When - resizeHandler([{ contentBoxSize: [{ blockSize: 100 }] }]); + resizeHandler( + [{ contentBoxSize: [{ blockSize: 100 }] } as unknown as ResizeObserverEntry], + {} as ResizeObserver, + ); // Then expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('true'); From 3ae3ec78a6865166135f31b72d7fa25f6ec8461d Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 11:57:04 +0100 Subject: [PATCH 24/31] Reduce margin-right of the composer --- res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 2f3d19b7977..4a5b40f79af 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -22,7 +22,7 @@ limitations under the License. /* fixed line height to prevent emoji from being taller than text */ line-height: $font-18px; justify-content: center; - margin-right: 15px; + margin-right: 13px; gap: 8px; .mx_FormattingButtons { From d9e032aebd9b4f296616012a7a5bdd65f576933f Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 12:14:26 +0100 Subject: [PATCH 25/31] Fix ts strict error --- src/components/views/rooms/EmojiButton.tsx | 2 +- src/components/views/rooms/MessageComposer.tsx | 2 +- .../rooms/wysiwyg_composer/SendWysiwygComposer.tsx | 13 ++++++++----- .../rooms/wysiwyg_composer/components/Editor.tsx | 8 ++++---- .../components/PlainTextComposer.tsx | 4 ++-- .../rooms/wysiwyg_composer/hooks/useIsExpanded.ts | 2 +- .../hooks/usePlainTextInitialization.ts | 2 +- .../wysiwyg_composer/hooks/usePlainTextListeners.ts | 8 ++++---- .../hooks/useWysiwygSendActionHandler.ts | 8 ++++---- .../views/rooms/wysiwyg_composer/hooks/utils.ts | 6 ++++-- .../components/PlainTextComposer-test.tsx | 4 ++-- 11 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/components/views/rooms/EmojiButton.tsx b/src/components/views/rooms/EmojiButton.tsx index 8e7ca67bfe9..3c99c093fcd 100644 --- a/src/components/views/rooms/EmojiButton.tsx +++ b/src/components/views/rooms/EmojiButton.tsx @@ -34,7 +34,7 @@ export function EmojiButton({ addEmoji, menuPosition, className }: IEmojiButtonP const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); let contextMenu: React.ReactElement | null = null; - if (menuDisplayed) { + if (menuDisplayed && button.current) { const position = ( menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()) ); diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 1f6344a887e..7594b897e1f 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -449,7 +449,7 @@ export class MessageComposer extends React.Component { const canSendMessages = this.context.canSendMessages && !this.context.tombstone; let composer: ReactNode; if (canSendMessages) { - if (this.state.isWysiwygLabEnabled) { + if (this.state.isWysiwygLabEnabled && menuPosition) { composer = ( - function Content({ disabled, composerFunctions }: ContentProps, forwardRef: RefObject) { - useWysiwygSendActionHandler(disabled, forwardRef, composerFunctions); + function Content( + { disabled = false, composerFunctions }: ContentProps, + forwardRef: ForwardedRef, + ) { + useWysiwygSendActionHandler(disabled, forwardRef as MutableRefObject, composerFunctions); return null; }, ); @@ -55,7 +58,7 @@ export function SendWysiwygComposer( className="mx_SendWysiwygComposer" leftComponent={e2eStatus && } // TODO add emoji support - rightComponent={ void 0} />} + rightComponent={ false} />} {...props} > { (ref, composerFunctions) => ( diff --git a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx index 4f114762b8a..edfd679ee5b 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/Editor.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { forwardRef, memo, ReactNode, RefObject } from 'react'; +import React, { forwardRef, memo, MutableRefObject, ReactNode } from 'react'; import { useIsExpanded } from '../hooks/useIsExpanded'; @@ -28,9 +28,9 @@ interface EditorProps { export const Editor = memo( forwardRef( - function Editor({ disabled, leftComponent, rightComponent }: EditorProps, ref: RefObject, + function Editor({ disabled, leftComponent, rightComponent }: EditorProps, ref, ) { - const isExpanded = useIsExpanded(ref, HEIGHT_BREAKING_POINT); + const isExpanded = useIsExpanded(ref as MutableRefObject, HEIGHT_BREAKING_POINT); return
void; - onSend: () => void; + onSend?: () => void; initialContent?: string; className?: string; leftComponent?: ReactNode; @@ -41,7 +41,7 @@ interface PlainTextComposerProps { export function PlainTextComposer({ className, - disabled, + disabled = false, onSend, onChange, children, diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts index e020d8f0e9e..1c2d38a0b96 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts @@ -16,7 +16,7 @@ limitations under the License. import { MutableRefObject, useEffect, useState } from "react"; -export function useIsExpanded(ref: MutableRefObject, breakingPoint: number) { +export function useIsExpanded(ref: MutableRefObject, breakingPoint: number) { const [isExpanded, setIsExpanded] = useState(false); useEffect(() => { if (ref.current) { diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextInitialization.ts b/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextInitialization.ts index abf2a6a6d27..5353b9404de 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextInitialization.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextInitialization.ts @@ -16,7 +16,7 @@ limitations under the License. import { RefObject, useEffect } from "react"; -export function usePlainTextInitialization(initialContent: string, ref: RefObject) { +export function usePlainTextInitialization(initialContent = '', ref: RefObject) { useEffect(() => { if (ref.current) { ref.current.innerText = initialContent; diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts b/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts index 02063ddcfb0..b47da173687 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/usePlainTextListeners.ts @@ -22,18 +22,18 @@ function isDivElement(target: EventTarget): target is HTMLDivElement { return target instanceof HTMLDivElement; } -export function usePlainTextListeners(onChange: (content: string) => void, onSend: () => void) { - const ref = useRef(); +export function usePlainTextListeners(onChange?: (content: string) => void, onSend?: () => void) { + const ref = useRef(null); const send = useCallback((() => { if (ref.current) { ref.current.innerHTML = ''; } - onSend(); + onSend?.(); }), [ref, onSend]); const onInput = useCallback((event: SyntheticEvent) => { if (isDivElement(event.target)) { - onChange(event.target.innerHTML); + onChange?.(event.target.innerHTML); } }, [onChange]); diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts index 49c6302d5b3..cae126fd79d 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { RefObject, useCallback, useRef } from "react"; +import { MutableRefObject, RefObject, useCallback, useRef } from "react"; import defaultDispatcher from "../../../../../dispatcher/dispatcher"; import { Action } from "../../../../../dispatcher/actions"; @@ -26,16 +26,16 @@ import { ComposerFunctions } from "../types"; export function useWysiwygSendActionHandler( disabled: boolean, - composerElement: RefObject, + composerElement: MutableRefObject, composerFunctions: ComposerFunctions, ) { const roomContext = useRoomContext(); - const timeoutId = useRef(); + const timeoutId = useRef(null); const handler = useCallback((payload: ActionPayload) => { // don't let the user into the composer if it is disabled - all of these branches lead // to the cursor being in the composer - if (disabled || !composerElement.current) return; + if (disabled || !composerElement?.current) return; const context = payload.context ?? TimelineRenderingType.Room; diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/utils.ts b/src/components/views/rooms/wysiwyg_composer/hooks/utils.ts index bfaf526f72e..5b767038200 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/utils.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/utils.ts @@ -14,14 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MutableRefObject } from "react"; + import { TimelineRenderingType } from "../../../../../contexts/RoomContext"; import { IRoomState } from "../../../../structures/RoomView"; export function focusComposer( - composerElement: React.MutableRefObject, + composerElement: MutableRefObject, renderingType: TimelineRenderingType, roomContext: IRoomState, - timeoutId: React.MutableRefObject, + timeoutId: MutableRefObject, ) { if (renderingType === roomContext.timelineRenderingType) { // Immediately set the focus, so if you start typing it diff --git a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx index b81eef7d935..f0343bef2a6 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx @@ -89,8 +89,8 @@ describe('PlainTextComposer', () => { }); it('Should have data-is-expanded when it has two lines', async () => { - let resizeHandler: ResizeObserverCallback; - let editor: Element; + let resizeHandler: ResizeObserverCallback = jest.fn(); + let editor: Element | null = null; const resizeObserver = ResizeObserver; global.ResizeObserver = jest.fn((handler) => { resizeHandler = handler; From 5add6ab4a432bce76492259786892cd0f4fa22fb Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 14:44:14 +0100 Subject: [PATCH 26/31] Fix ts error --- .../rooms/wysiwyg_composer/hooks/useWysiwygEditActionHandler.ts | 2 +- .../rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygEditActionHandler.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygEditActionHandler.ts index b39fe18007b..e05e04d39d2 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygEditActionHandler.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygEditActionHandler.ts @@ -28,7 +28,7 @@ export function useWysiwygEditActionHandler( composerElement: RefObject, ) { const roomContext = useRoomContext(); - const timeoutId = useRef(); + const timeoutId = useRef(null); const handler = useCallback((payload: ActionPayload) => { // don't let the user into the composer if it is disabled - all of these branches lead diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts index cae126fd79d..500f0270491 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useWysiwygSendActionHandler.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MutableRefObject, RefObject, useCallback, useRef } from "react"; +import { MutableRefObject, useCallback, useRef } from "react"; import defaultDispatcher from "../../../../../dispatcher/dispatcher"; import { Action } from "../../../../../dispatcher/actions"; From 30015a5586fee76a4dad7db76a26b6874938a8ad Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 15:15:38 +0100 Subject: [PATCH 27/31] Update room view snapshot --- .../structures/__snapshots__/RoomView-test.tsx.snap | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/components/structures/__snapshots__/RoomView-test.tsx.snap index ddb5489a987..33f44f9a371 100644 --- a/test/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/test/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RoomView for a local room in state CREATING should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
We're creating a room with @user:example.com
"`; +exports[`RoomView for a local room in state CREATING should match the snapshot 1`] = `"
@user:example.com
We're creating a room with @user:example.com
"`; -exports[`RoomView for a local room in state ERROR should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.
    U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat

!
Some of your messages have not been sent
Retry
"`; +exports[`RoomView for a local room in state ERROR should match the snapshot 1`] = `"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.

    @user:example.com

    Send your first message to invite @user:example.com to chat

!
Some of your messages have not been sent
Retry
"`; -exports[`RoomView for a local room in state NEW should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.
    U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; +exports[`RoomView for a local room in state NEW should match the snapshot 1`] = `"
@user:example.com
  1. End-to-end encryption isn't enabled
    Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; -exports[`RoomView for a local room in state NEW that is encrypted should match the snapshot 1`] = `"
U\\"\\"
@user:example.com
    Encryption enabled
    Messages in this chat will be end-to-end encrypted.
  1. U\\"\\"

    @user:example.com

    Send your first message to invite @user:example.com to chat


"`; +exports[`RoomView for a local room in state NEW that is encrypted should match the snapshot 1`] = `"
@user:example.com
    Encryption enabled
    Messages in this chat will be end-to-end encrypted.
  1. @user:example.com

    Send your first message to invite @user:example.com to chat


"`; From b8bcd7cca3a9b5a0e5be7a1ba168701ef224413d Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 15:40:29 +0100 Subject: [PATCH 28/31] Fix composer growing --- res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss index 4a5b40f79af..2eee815c3fc 100644 --- a/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss +++ b/res/css/views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss @@ -63,7 +63,7 @@ limitations under the License. min-height: 22px; margin-bottom: 2px; /* don't grow wider than available space */ - min-width: 0; + width: 0; .mx_WysiwygComposer_Editor_content { /* this will center the contenteditable */ From df6238b7c903901ab8a06103c32b9ebbb3f0e1d8 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 15:47:53 +0100 Subject: [PATCH 29/31] Fix remaining ts error --- test/setup/setupManualMocks.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/setup/setupManualMocks.ts b/test/setup/setupManualMocks.ts index 1594af4f5fc..3510ee1e8c5 100644 --- a/test/setup/setupManualMocks.ts +++ b/test/setup/setupManualMocks.ts @@ -74,6 +74,7 @@ global.URL.revokeObjectURL = jest.fn(); // polyfilling TextEncoder as it is not available on JSDOM // view https://github.com/facebook/jest/issues/9983 global.TextEncoder = TextEncoder; +// @ts-ignore global.TextDecoder = TextDecoder; // prevent errors whenever a component tries to manually scroll. @@ -83,4 +84,5 @@ window.HTMLElement.prototype.scrollIntoView = jest.fn(); fetchMock.config.overwriteRoutes = false; fetchMock.catch(""); fetchMock.get("/image-file-stub", "image file stub"); +// @ts-ignore window.fetch = fetchMock.sandbox(); From 8a68450bf0ea184ace9c159176b30d0b7949494b Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 15:58:23 +0100 Subject: [PATCH 30/31] Add requestionAnimationFrame to avoid resizer loop --- cypress/support/composer.ts | 8 -------- .../views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts | 6 ++++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cypress/support/composer.ts b/cypress/support/composer.ts index a611195f629..ae6c8bef871 100644 --- a/cypress/support/composer.ts +++ b/cypress/support/composer.ts @@ -32,14 +32,6 @@ declare global { } } -const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/; -Cypress.on('uncaught:exception', (err) => { - /* returning false here prevents Cypress from failing the test */ - if (resizeObserverLoopErrRe.test(err.message)) { - return false; - } -}); - Cypress.Commands.add("getComposer", (isRightPanel?: boolean): Chainable => { const panelClass = isRightPanel ? '.mx_RightPanel' : '.mx_RoomView_body'; return cy.get(`${panelClass} .mx_MessageComposer`); diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts index 1c2d38a0b96..c7758917b1d 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useIsExpanded.ts @@ -22,8 +22,10 @@ export function useIsExpanded(ref: MutableRefObject, breakin if (ref.current) { const editor = ref.current; const resizeObserver = new ResizeObserver(entries => { - const height = entries[0]?.contentBoxSize?.[0].blockSize; - setIsExpanded(height >= breakingPoint); + requestAnimationFrame(() => { + const height = entries[0]?.contentBoxSize?.[0].blockSize; + setIsExpanded(height >= breakingPoint); + }); }); resizeObserver.observe(editor); From cc7901e0c29003e15db3fd50e3dac57554f7bb65 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 4 Nov 2022 16:19:14 +0100 Subject: [PATCH 31/31] Fix is expanded test --- .../components/PlainTextComposer-test.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx index f0343bef2a6..9c2e10100fe 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx @@ -91,8 +91,7 @@ describe('PlainTextComposer', () => { it('Should have data-is-expanded when it has two lines', async () => { let resizeHandler: ResizeObserverCallback = jest.fn(); let editor: Element | null = null; - const resizeObserver = ResizeObserver; - global.ResizeObserver = jest.fn((handler) => { + jest.spyOn(global, 'ResizeObserver').mockImplementation((handler) => { resizeHandler = handler; return { observe: (element) => { @@ -103,6 +102,10 @@ describe('PlainTextComposer', () => { }; }, ); + jest.spyOn(global, 'requestAnimationFrame').mockImplementation(cb => { + cb(0); + return 0; + }); //When render( @@ -118,10 +121,12 @@ describe('PlainTextComposer', () => { [{ contentBoxSize: [{ blockSize: 100 }] } as unknown as ResizeObserverEntry], {} as ResizeObserver, ); + jest.runAllTimers(); // Then expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('true'); - global.ResizeObserver = resizeObserver; + (global.ResizeObserver as jest.Mock).mockRestore(); + (global.requestAnimationFrame as jest.Mock).mockRestore(); }); });