diff --git a/res/css/_components.scss b/res/css/_components.scss index df5da0a158..74f5feedf3 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -84,7 +84,6 @@ @import "./views/avatars/_DecoratedRoomAvatar.scss"; @import "./views/avatars/_WidgetAvatar.scss"; @import "./views/beta/_BetaCard.scss"; -@import "./views/buttons/_Cancel.scss"; @import "./views/context_menus/_CallContextMenu.scss"; @import "./views/context_menus/_DeviceContextMenu.scss"; @import "./views/context_menus/_IconizedContextMenu.scss"; diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 63c2a61bc9..b19173a313 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -32,12 +32,6 @@ limitations under the License. position: relative; } -.mx_MainSplit_timeline { - .mx_MessageComposer_wrapper { - margin: $spacing-8 $spacing-16; - } -} - .mx_RoomView_auxPanel { min-width: 0px; width: 100%; @@ -161,7 +155,7 @@ limitations under the License. .mx_RoomView_messageListWrapper { justify-content: flex-start; - > .mx_RoomView_MessageList > li > ol { + >.mx_RoomView_MessageList > li > ol { list-style-type: none; } } diff --git a/res/css/views/buttons/_Cancel.scss b/res/css/views/buttons/_Cancel.scss deleted file mode 100644 index 50e2cd0931..0000000000 --- a/res/css/views/buttons/_Cancel.scss +++ /dev/null @@ -1,32 +0,0 @@ -/* -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. -*/ - -.mx_CancelButton { - width: var(--size); - height: var(--size); - - box-sizing: border-box; - padding: calc(var(--size) / 4); - border-radius: 50%; - - line-height: 0; - background: $quinary-content; - - svg { - width: calc(var(--size) / 2); - color: $secondary-content; - } -} diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 41faff2b03..9619bad3fe 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -20,50 +20,31 @@ limitations under the License. margin: auto; border-top: 1px solid $primary-hairline-color; position: relative; + padding-left: 42px; + padding-right: 16px; +} - display: grid; - grid-template: - "reply reply" auto - "composer controls" auto - / 1fr auto; +.mx_MessageComposer_replaced_wrapper { + margin-left: auto; + margin-right: auto; +} - /* the below is required, without this some unexpected scroll jump - will occur when erasing the composer content or jumping to voice note - recording */ - min-height: 45px; +.mx_MessageComposer_replaced_valign { + height: 60px; + display: table-cell; + vertical-align: middle; +} - .mx_ReplyPreview { - grid-area: reply; - } +.mx_MessageComposer_roomReplaced_icon { + float: left; + margin-right: 20px; + margin-top: 5px; + width: 31px; + height: 31px; +} - .mx_MessageComposer_row { - min-width: 0; - grid-area: composer; - } - - .mx_MessageComposer_controls { - grid-area: controls; - display: flex; - align-items: flex-end; - margin-bottom: 2px; - } - - >[role=button] { - margin-bottom: 7px; - } - - .mx_VoiceRecordComposerTile_delete { - margin-bottom: 9px; - } - - .mx_VoiceRecordComposerTile_stop, - .mx_MessageComposer_sendMessage { - margin-bottom: $spacing-4; - } - - .mx_VoiceMessagePrimaryContainer { - margin-right: $spacing-8; - } +.mx_MessageComposer_roomReplaced_header { + font-weight: bold; } .mx_MessageComposer_autocomplete_wrapper { @@ -75,36 +56,7 @@ limitations under the License. display: flex; flex-direction: row; align-items: center; - border: 1px solid $quaternary-content; - border-radius: 12px; - padding: $spacing-12 $spacing-8; - margin-right: $spacing-16; - - transition: border-color var(--transition-short); - - &[data-notice=true] { - border-color: transparent; - color: $secondary-content; - - p { - margin: 0; - line-height: 0; - } - - svg { - vertical-align: middle; - position: relative; - top: -2px; - } - } - - &:focus-within { - border-color: $tertiary-content; - } - - &[aria-disabled=true] { - cursor: not-allowed; - } + width: 100%; } .mx_MessageComposer .mx_MessageComposer_avatar { @@ -121,16 +73,22 @@ limitations under the License. } .mx_MessageComposer_e2eIcon.mx_E2EIcon { - margin: 0 0 2px; + position: absolute; + left: 20px; + margin-right: 0; // Counteract the E2EIcon class + margin-left: 3px; // Counteract the E2EIcon class width: 12px; height: 12px; - align-self: end; } .mx_MessageComposer_noperm_error { + width: 100%; + height: 60px; font-style: italic; - color: $tertiary-content; - font-size: $font-12px; + color: $info-plinth-fg-color; + display: flex; + align-items: center; + justify-content: center; } .mx_MessageComposer_input_wrapper { @@ -166,19 +124,13 @@ limitations under the License. .mx_MessageComposer_editor > :first-child { margin-top: 0 !important; } - .mx_MessageComposer_editor > :last-child { margin-bottom: 0 !important; } @keyframes visualbell { - from { - background-color: $visual-bell-bg-color; - } - - to { - background-color: $background; - } + from { background-color: $visual-bell-bg-color; } + to { background-color: $background; } } .mx_MessageComposer_input_error { @@ -214,14 +166,12 @@ limitations under the License. color: $accent; opacity: 1.0; } - .mx_MessageComposer_input textarea::-webkit-input-placeholder { color: $accent; } .mx_MessageComposer_button_highlight { background: rgba($accent, 0.25); - // make the icon the accent color too &::before { background-color: $accent !important; @@ -238,7 +188,6 @@ limitations under the License. padding-left: var(--size); border-radius: 50%; margin-right: 6px; - margin-bottom: 7px; &:last-child { margin-right: auto; @@ -312,30 +261,11 @@ limitations under the License. mask-image: url('$(res)/img/image-view/more.svg'); } -.mx_MessageComposer_sendMessageWrapper { - --sendMessageSize: 32px; - transition: all var(--transition-short); -} - -.mx_MessageComposer_sendMessageWrapper, -.mx_MessageComposer_sendMessageWrapper-enter, -.mx_MessageComposer_sendMessageWrapper-exit { - width: 0; - transform: scale(.6); - opacity: 0; -} - -.mx_MessageComposer_sendMessageWrapper-enter-active { - width: var(--sendMessageSize); - transform: scale(1); - opacity: 1; -} - .mx_MessageComposer_sendMessage { cursor: pointer; position: relative; - width: var(--sendMessageSize); - height: var(--sendMessageSize); + width: 32px; + height: 32px; border-radius: 100%; background-color: $accent; @@ -428,6 +358,10 @@ limitations under the License. .mx_MessageComposer_input { min-height: 50px; } + + .mx_MessageComposer_noperm_error { + height: 50px; + } } /** @@ -437,7 +371,21 @@ limitations under the License. .mx_MessageComposer.mx_MessageComposer--compact { margin-right: 0; + .mx_MessageComposer_wrapper { + padding: 0 0 0 25px; + } + + &:not(.mx_MessageComposer_e2eStatus) { + .mx_MessageComposer_wrapper { + padding: 0; + } + } + .mx_MessageComposer_button:last-child { margin-right: 0; } + + .mx_MessageComposer_e2eIcon { + left: 0; + } } diff --git a/res/css/views/rooms/_ReplyPreview.scss b/res/css/views/rooms/_ReplyPreview.scss index e303a22c02..50abcc738b 100644 --- a/res/css/views/rooms/_ReplyPreview.scss +++ b/res/css/views/rooms/_ReplyPreview.scss @@ -15,44 +15,48 @@ limitations under the License. */ .mx_ReplyPreview { - border: 1px solid $system; - - margin-left: calc(-1 * $spacing-16); - margin-right: calc(-1 * $spacing-16); - padding: $spacing-8 $spacing-16 0 $spacing-16; - - border-top-left-radius: 16px; - border-top-right-radius: 16px; - + border: 1px solid $primary-hairline-color; border-bottom: none; background: $background; max-height: 50vh; overflow: auto; -} -.mx_ReplyPreview_header { - display: flex; - font-size: $font-12px; - color: $secondary-content; - position: relative; + .mx_ReplyPreview_section { + border-bottom: 1px solid $primary-hairline-color; + display: flex; + flex-flow: column; + row-gap: $spacing-8; + padding: $spacing-8 $spacing-8 0 $spacing-8; - > svg { - width: 1em; - vertical-align: middle; - margin-right: $spacing-8; - } + .mx_ReplyPreview_header { + display: flex; + justify-content: space-between; + column-gap: 8px; - .mx_CancelButton { - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); + color: $primary-content; + font-weight: 400; + opacity: 0.4; + + .mx_ReplyPreview_header_cancel { + background-color: $primary-content; + mask: url('$(res)/img/cancel.svg'); + mask-repeat: no-repeat; + mask-position: center; + mask-size: 18px; + width: 18px; + height: 18px; + min-width: 18px; + min-height: 18px; + } + } } } -.mx_ReplyPreview_header_cancel { - position: absolute; - right: 0; - color: $primary-content; - width: 18px; +.mx_RoomView_body { + .mx_ReplyPreview { + // Add box-shadow to the reply preview on the main (left) panel only. + // It is not added to the preview on the (right) panel for threads and a chat with a maximized widget. + box-shadow: 0px -16px 32px $composer-shadow-color; + border-radius: 8px 8px 0 0; + } } diff --git a/res/css/views/rooms/_SendMessageComposer.scss b/res/css/views/rooms/_SendMessageComposer.scss index 6805f158ed..3e2cf68f1d 100644 --- a/res/css/views/rooms/_SendMessageComposer.scss +++ b/res/css/views/rooms/_SendMessageComposer.scss @@ -30,8 +30,14 @@ limitations under the License. flex: 1; 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_BasicMessageComposer_input { + padding: 3px 0; // this will center the contenteditable // in it's parent vertically // while keeping the autocomplete at the top diff --git a/res/img/cancel.svg b/res/img/cancel.svg index 82e3892561..e32060025e 100644 --- a/res/img/cancel.svg +++ b/res/img/cancel.svg @@ -5,6 +5,6 @@ Created with Sketch. - + - + \ No newline at end of file diff --git a/res/img/element-icons/room/message-bar/reply.svg b/res/img/element-icons/room/message-bar/reply.svg index c32848a0b0..9900d4d19d 100644 --- a/res/img/element-icons/room/message-bar/reply.svg +++ b/res/img/element-icons/room/message-bar/reply.svg @@ -1,4 +1,4 @@ - - + + diff --git a/res/img/element-icons/room/room-summary.svg b/res/img/element-icons/room/room-summary.svg index 1f43428ffc..b6ac258b18 100644 --- a/res/img/element-icons/room/room-summary.svg +++ b/res/img/element-icons/room/room-summary.svg @@ -1,3 +1,3 @@ - + diff --git a/res/img/element-icons/x-8px.svg b/res/img/element-icons/x-8px.svg index 8a706c3446..c9730ed619 100644 --- a/res/img/element-icons/x-8px.svg +++ b/res/img/element-icons/x-8px.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/components/views/buttons/Cancel.tsx b/src/components/views/buttons/Cancel.tsx deleted file mode 100644 index f0fbee56db..0000000000 --- a/src/components/views/buttons/Cancel.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* -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 React, { ComponentProps } from "react"; -import classnames from "classnames"; - -import AccessibleButton from "../elements/AccessibleButton"; -import { Icon as CancelIcon } from "../../../../res/img/cancel.svg"; - -export default function CancelButton(props: ComponentProps) { - const classNames = classnames("mx_CancelButton", props.className ?? ""); - const vars = { - "--size": `${props.size}px`, - } as React.CSSProperties; - - return - - ; -} - -CancelButton.defaultProps = { - size: "16", -}; diff --git a/src/components/views/messages/DisambiguatedProfile.tsx b/src/components/views/messages/DisambiguatedProfile.tsx index d634277bc6..36850e916e 100644 --- a/src/components/views/messages/DisambiguatedProfile.tsx +++ b/src/components/views/messages/DisambiguatedProfile.tsx @@ -28,14 +28,9 @@ interface IProps { onClick?(): void; colored?: boolean; emphasizeDisplayName?: boolean; - as?: string; } export default class DisambiguatedProfile extends React.Component { - public static defaultProps = { - as: "div", - }; - render() { const { fallbackName, member, colored, emphasizeDisplayName, onClick } = this.props; const rawDisplayName = member?.rawDisplayName || fallbackName; @@ -62,14 +57,13 @@ export default class DisambiguatedProfile extends React.Component { [colorClass]: true, }); - return React.createElement(this.props.as, { - className: "mx_DisambiguatedProfile", - onClick, - }, <> - - { rawDisplayName } - - { mxidElement } - ); + return ( +
+ + { rawDisplayName } + + { mxidElement } +
+ ); } } diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index a9c77f9ebe..da7d1206d1 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -27,17 +27,12 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; interface IProps { mxEvent: MatrixEvent; onClick?(): void; - as?: string; } export default class SenderProfile extends React.PureComponent { public static contextType = MatrixClientContext; public context!: React.ContextType; - public static defaultProps = { - as: "div", - }; - render() { const { mxEvent, onClick } = this.props; const msgtype = mxEvent.getContent().msgtype; @@ -65,7 +60,6 @@ export default class SenderProfile extends React.PureComponent { member={member} colored={true} emphasizeDisplayName={true} - as={this.props.as} /> ); } } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index b08262a4d4..0c7fb84da1 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -22,7 +22,6 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { EventType } from 'matrix-js-sdk/src/@types/event'; import { Optional } from "matrix-events-sdk"; import { THREAD_RELATION_TYPE } from 'matrix-js-sdk/src/models/thread'; -import { CSSTransition } from 'react-transition-group'; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; @@ -52,14 +51,12 @@ import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdat import MessageComposerButtons from './MessageComposerButtons'; import { ButtonEvent } from '../elements/AccessibleButton'; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; -import { Icon as InfoIcon } from "../../../../res/img/element-icons/room/room-summary.svg"; let instanceCount = 0; interface ISendButtonProps { onClick: (ev: ButtonEvent) => void; title?: string; // defaults to something generic - "aria-hidden"?: boolean; } function SendButton(props: ISendButtonProps) { @@ -68,7 +65,6 @@ function SendButton(props: ISendButtonProps) { className="mx_MessageComposer_sendMessage" onClick={props.onClick} title={props.title ?? _t('Send message')} - aria-hidden={props['aria-hidden'] ?? false} /> ); } @@ -267,15 +263,15 @@ export default class MessageComposer extends React.Component { } else if (replyingToThread) { return _t('Reply to thread…'); } else if (this.props.e2eStatus) { - return _t('Send encrypted reply…'); + return _t('Send an encrypted reply…'); } else { - return _t('Send reply…'); + return _t('Send a reply…'); } } else { if (this.props.e2eStatus) { - return _t('Send encrypted message…'); + return _t('Send an encrypted message…'); } else { - return _t('Send message…'); + return _t('Send a message…'); } } }; @@ -355,7 +351,11 @@ export default class MessageComposer extends React.Component { }; public render() { - const controls = []; + const controls = [ + this.props.e2eStatus ? + : + null, + ]; let menuPosition: AboveLeftOf | undefined; if (this.ref.current) { @@ -363,8 +363,6 @@ export default class MessageComposer extends React.Component { menuPosition = aboveLeftOf(contentRect); } - const roomReplaced = !!this.context.tombstone; - const canSendMessages = this.context.canSendMessages && !this.context.tombstone; if (canSendMessages) { controls.push( @@ -381,23 +379,34 @@ export default class MessageComposer extends React.Component { toggleStickerPickerOpen={this.toggleStickerPickerOpen} />, ); - } else if (roomReplaced) { + + controls.push(); + } else if (this.context.tombstone) { const replacementRoomId = this.context.tombstone.getContent()['replacement_room']; - controls.push(

- -   - { _t("This room has been replaced and is no longer active.") } -   - { replacementRoomId && ( - - { _t("The conversation continues here.") } - - ) } -

); + const continuesLink = replacementRoomId ? ( + + { _t("The conversation continues here.") } + + ) : ''; + + controls.push(
+
+ + + { _t("This room has been replaced and is no longer active.") } +
+ { continuesLink } +
+
); } else { controls.push(
@@ -432,12 +441,6 @@ export default class MessageComposer extends React.Component { const showSendButton = !this.state.isComposerEmpty || this.state.haveRecording; - if (this.props.e2eStatus) { - controls.push( - , - ); - } - const classes = classNames({ "mx_MessageComposer": true, "mx_MessageComposer--compact": this.props.compact, @@ -451,17 +454,8 @@ export default class MessageComposer extends React.Component { -
+
{ controls } -
-
- { canSendMessages && } { canSendMessages && { showStickersButton={this.state.showStickersButton} toggleButtonMenu={this.toggleButtonMenu} /> } - {}} - > -
- -
-
+ { showSendButton && ( + + ) }
diff --git a/src/components/views/rooms/ReplyPreview.tsx b/src/components/views/rooms/ReplyPreview.tsx index 345ce83c21..611c58f852 100644 --- a/src/components/views/rooms/ReplyPreview.tsx +++ b/src/components/views/rooms/ReplyPreview.tsx @@ -22,9 +22,7 @@ import { _t } from '../../../languageHandler'; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import ReplyTile from './ReplyTile'; import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext'; -import SenderProfile from '../messages/SenderProfile'; -import { Icon as ReplyIcon } from "../../../../res/img/element-icons/room/message-bar/reply.svg"; -import CancelButton from '../buttons/Cancel'; +import AccessibleButton from "../elements/AccessibleButton"; function cancelQuoting(context: TimelineRenderingType) { dis.dispatch({ @@ -46,19 +44,19 @@ export default class ReplyPreview extends React.Component { if (!this.props.replyToEvent) return null; return
-
- - { _t('Reply to ', {}, { - 'User': () => , - }) }   - - cancelQuoting(this.context.timelineRenderingType)} /> +
+
+ { _t('Replying') } + cancelQuoting(this.context.timelineRenderingType)} + /> +
+
-
; } } diff --git a/src/components/views/rooms/ReplyTile.tsx b/src/components/views/rooms/ReplyTile.tsx index bc26c02c23..2b973abfca 100644 --- a/src/components/views/rooms/ReplyTile.tsx +++ b/src/components/views/rooms/ReplyTile.tsx @@ -44,7 +44,6 @@ interface IProps { getRelationsForEvent?: ( (eventId: string, relationType: string, eventType: string) => Relations ); - showSenderProfile?: boolean; } export default class ReplyTile extends React.PureComponent { @@ -52,7 +51,6 @@ export default class ReplyTile extends React.PureComponent { static defaultProps = { onHeightChanged: () => {}, - showSenderProfile: true, }; componentDidMount() { @@ -138,8 +136,7 @@ export default class ReplyTile extends React.PureComponent { let sender; const needsSenderProfile = ( - this.props.showSenderProfile - && !isInfoMessage + !isInfoMessage && msgType !== MsgType.Image && evType !== EventType.Sticker && evType !== EventType.RoomCreate diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9982b50542..6a7e3f77f3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1700,12 +1700,12 @@ "Send message": "Send message", "Reply to encrypted thread…": "Reply to encrypted thread…", "Reply to thread…": "Reply to thread…", - "Send encrypted reply…": "Send encrypted reply…", - "Send reply…": "Send reply…", - "Send encrypted message…": "Send encrypted message…", - "Send message…": "Send message…", - "This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.", + "Send an encrypted reply…": "Send an encrypted reply…", + "Send a reply…": "Send a reply…", + "Send an encrypted message…": "Send an encrypted message…", + "Send a message…": "Send a message…", "The conversation continues here.": "The conversation continues here.", + "This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.", "You do not have permission to post to this room": "You do not have permission to post to this room", "%(seconds)ss left": "%(seconds)ss left", "Send voice message": "Send voice message", @@ -1758,7 +1758,7 @@ "Seen by %(count)s people|one": "Seen by %(count)s person", "Read receipts": "Read receipts", "Recently viewed": "Recently viewed", - "Reply to ": "Reply to ", + "Replying": "Replying", "Room %(name)s": "Room %(name)s", "Recently visited rooms": "Recently visited rooms", "No recently visited rooms": "No recently visited rooms", diff --git a/test/components/views/rooms/MessageComposer-test.tsx b/test/components/views/rooms/MessageComposer-test.tsx index d774aa8060..e756a7653c 100644 --- a/test/components/views/rooms/MessageComposer-test.tsx +++ b/test/components/views/rooms/MessageComposer-test.tsx @@ -61,7 +61,7 @@ describe("MessageComposer", () => { expect(wrapper.find("SendMessageComposer")).toHaveLength(0); expect(wrapper.find("MessageComposerButtons")).toHaveLength(0); - expect(wrapper.find("p").text()).toContain("room has been replaced"); + expect(wrapper.find(".mx_MessageComposer_roomReplaced_header")).toHaveLength(1); }); });