From 311c229d844b4245ecc949831fa145493bbeffa7 Mon Sep 17 00:00:00 2001 From: su-ex Date: Thu, 10 Jun 2021 22:56:33 +0200 Subject: [PATCH 01/23] Initial implementation of messages bubbles for Element --- res/css/_components.scss | 1 + res/css/views/rooms/_BubbleLayout.scss | 723 ++++++++++++++++++ res/themes/dark/css/_dark.scss | 3 + res/themes/legacy-dark/css/_legacy-dark.scss | 3 + .../legacy-light/css/_legacy-light.scss | 3 + res/themes/light-custom/css/_custom.scss | 5 + res/themes/light/css/_light.scss | 3 + src/components/structures/MessagePanel.js | 4 + src/components/structures/RoomView.tsx | 45 +- src/components/structures/TimelinePanel.js | 4 + .../views/elements/EventListSummary.tsx | 49 +- .../views/elements/EventTilePreview.tsx | 1 + src/components/views/messages/MAudioBody.js | 6 + src/components/views/messages/MFileBody.js | 6 + src/components/views/messages/MImageBody.js | 16 +- src/components/views/messages/MStickerBody.js | 3 +- src/components/views/messages/MVideoBody.tsx | 39 +- src/components/views/messages/MessageEvent.js | 7 + src/components/views/messages/TextualBody.js | 14 +- src/components/views/rooms/EventTile.tsx | 277 +++++-- .../views/rooms/MessageComposer.tsx | 15 +- .../views/rooms/SearchResultTile.js | 2 + .../tabs/user/AppearanceUserSettingsTab.tsx | 104 +++ src/contexts/RoomContext.ts | 2 + src/settings/Layout.ts | 4 +- src/settings/Settings.tsx | 10 + 26 files changed, 1227 insertions(+), 122 deletions(-) create mode 100644 res/css/views/rooms/_BubbleLayout.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 56403ea190e..8a6b1c202a8 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -195,6 +195,7 @@ @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; @import "./views/rooms/_GroupLayout.scss"; +@import "./views/rooms/_BubbleLayout.scss"; @import "./views/rooms/_IRCLayout.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; diff --git a/res/css/views/rooms/_BubbleLayout.scss b/res/css/views/rooms/_BubbleLayout.scss new file mode 100644 index 00000000000..99876c0f290 --- /dev/null +++ b/res/css/views/rooms/_BubbleLayout.scss @@ -0,0 +1,723 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020 Tobias Büttner +Copyright 2020-2021 Quirin Götz + +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. +*/ + +$left-gutter: 56px; + +.sc_BubbleLayout { + // ---- Overrides ---- + + .mx_RoomView_MessageList { + padding-bottom: 8px; + padding-right: 10px; + } + + .mx_RoomTile { + .mx_RoomTile_nameContainer { + .mx_RoomTile_name, + .mx_RoomTile_messagePreview { + margin: 2px 2px; + } + } + } + + li > .mx_DateSeparator { + margin: 7px 0px 1px 0px; + } + + .mx_LinkPreviewWidget { + margin-bottom: 0px; + } + + .mx_EventTile { + // no reserved space for read receipts required. + padding-top: 6px; + + &.mx_EventTile_info { + padding-top: 0px; + + .mx_EventTile_avatar { + left: $left-gutter; + } + } + + > .mx_SenderProfile { + line-height: $font-17px; + padding-left: $left-gutter; + max-width: unset !important; + } + + > .mx_EventTile_line { + padding-left: $left-gutter !important; + border-left: none !important; + background: transparent !important; + margin-right: unset; + } + + > .mx_EventTile_avatar { + position: absolute; + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 3px; + padding-bottom: 3px; + padding-left: $left-gutter; + line-height: $font-22px; + min-height: $font-22px; + } + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-left: $left-gutter; + } + + .mx_EventListSummary { + // For toggle + position: relative; + + // Consistence with other bubbles + .mx_EventTile { + padding-top: 0; + } + .sc_EventTile_bubbleLine_info { + margin-top: 6px; + } + + .mx_EventTile_line { + padding-top: 3px; + padding-bottom: 3px; + } + + .sc_EventTile_bubbleArea_info { + // For toggle + max-width: 72rem; + } + } + + .mx_EventListSummary_toggle { + // ToDo: Find better way to not crash bubble on small width screens + float: unset; + position: absolute; + right: 10px; + margin-right: 0; + z-index: 1; + } + + .mx_EventTile_info .mx_EventTile_line { + padding-left: calc($left-gutter + 18px) !important; + max-width: max-content; + + .mx_MessageActionBar { + top: -21px; // counteract padding: 24-3 + } + } + + .mx_SenderProfile { + text-align: left; + } + + .mx_SenderProfile_name { + color: $accent-color !important; + } + + .mx_EventTile_selected > div > a > .mx_MessageTimestamp { + left: 0px; + width: 46px; + } + + .mx_EventTile_msgOption { + float: unset; + text-align: unset; + position: unset; + + margin-left: 8px; + margin-right: unset; + + /* Hack to stop the height of this pushing the messages apart. + Replaces margin-top: -6px. This interacts better with a read + marker being in between. Content overflows. */ + // SC: Reserve our space + height: 14px; + margin-top: 6px; + margin-bottom: 6px; + + // stop read avatars overflowing + width: 100%; + + &.sc_readReceipts_empty { + height: 0; + margin-top: 0; + margin-bottom: 0; + } + } + + .mx_EventTile_bubbleContainer .mx_EventTile_msgOption { + grid-column: unset; // show read avatara like always + } + + .mx_EventTile_readAvatars { + // SC-TODO align left below msg area + //top: 29px; + top: 0; + + // stop read avatars overflowing + width: 100%; + height: 22px; + overflow-x: scroll; + overflow-y: hidden; + z-index: 0; // let message action bar not jump away + } + + .mx_EventTile_readAvatarRemainder { + height: 14px; + } + + .mx_EventTile_content { + // Handled by bubble + margin-right: unset; + } + + // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) + .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp, + .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp, + .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp { + left: 5px; + } + + .mx_EventTile_isEditing { + background-color: transparent; + } + + .mx_EditMessageComposer { + margin: 0; + padding: 0; + + .mx_EditMessageComposer_buttons { + position: unset; + margin: 0; + margin-left: auto; + padding: 0; + padding-top: 10px; + max-width: 100%; + background: transparent; + flex-wrap: wrap; + + .mx_AccessibleButton { + padding: 5px 0; + width: 11em; + max-width: 100%; + } + } + } + + .mx_BasicMessageComposer { + max-width: 100%; + } + + // blockquote.mx_ReplyThread { + // margin-bottom: 10px; + // } + + .mx_ReplyThread, .mx_ReplyPreview { + // padding-top: 2px; + + .mx_EventTile_avatar { + top: 4px !important; + } + + .mx_MessageTimestamp { + top: 4px !important; + } + + .mx_MImageBody_thumbnail, span.mx_MVideoBody video.mx_MVideoBody { + border-color: $event-timestamp-color !important; + } + } + + .mx_MNoticeBody { + opacity: unset; + } + + .mx_MessageActionBar { + left: unset; + right: 0px; + top: -36px; + } + + .mx_MFileBody { + display: flow-root; + } + + .mx_MFileBody_download { + display: inline; + } + + + .mx_MessageTimestamp { + position: absolute; + width: 46px; /* 8 + 30 (avatar) + 8 */ + } + + .mx_ReactionsRow { + margin-right: -6px; + display: flex; + justify-content: left; + flex-direction: row; + + .mx_ReactionsRow_addReactionButton { + margin-left: unset; + + &::before { + left: 0; + } + } + } + + a.mx_Pill { + max-width: 100%; + } + + .mx_MImageBody { + margin-right: 0; + position: relative; + + .mx_MFileBody_download { + display: none; + } + + .sc_PlaceholderTimestamp { + display: none; + } + + .mx_MessageTimestamp { + visibility: visible !important; + text-align: center; + background: rgba(0, 0, 0, 0.5); + color: white; + left: unset; + right: 1px; + top: unset; + bottom: 1px; + border-radius: 8px 0; + font-size: 0.85em; + padding: 6px 9px; + width: unset; + line-height: 1; + } + } + + .mx_MImageBody_thumbnail, span.mx_MVideoBody video.mx_MVideoBody { + box-sizing: border-box; + border: 1px solid $message-bubble-background; + } + + .mx_MStickerBody_wrapper { + padding: 0; + } + + span.mx_MVideoBody { + display: block; + max-width: max-content; + position: relative; + + video.mx_MVideoBody { + display: block; + width: auto; + max-height: 600px; + } + + .mx_MFileBody { + display: flex; + position: relative; + justify-content: space-between; + align-items: center; + } + + .mx_MFileBody_download { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .sc_PlaceholderTimestamp { + display: none; + } + + .sc_LinkedTimestamp { + margin-left: 1rem; + + .mx_MessageTimestamp { + visibility: visible !important; + position: unset !important; + width: unset !important; + font-size: 0.85em; + line-height: 1; + } + } + } + + // ---- Bubble specific ---- + + .mx_EventTile > .sc_EventTile_bubbleLine_info { + padding-left: 0 !important; + } + + .mx_EventTile .sc_EventTile_bubbleLine_info { + line-height: unset; + } + + .mx_EventListSummary .sc_EventTile_bubbleLine_info { + // ToDo: Find better way to not crash bubble with toggle on small width screens + // padding-left: 110px; + padding-left: 0; + margin-right: 0; + } + + .sc_EventTile_bubbleContainer { + > .mx_EventTile_avatar { + top: 9px; + } + } + + .mx_EventTile_e2eIcon { + display: none !important; + } + + // .sc_EventTile_bubbleLine { + // .mx_EventTile_e2eIcon { + // left: 16px; + + // .sc_EventTile_bubbleTailLeftContainer & { + // top: 35px; + // } + // } + // } + + .sc_EventTile_bubbleArea { + // No full width for both-side bubbles only + width: 84%; + max-width: 88rem; + padding: 0px; + margin-bottom: 0; + } + + .sc_EventTile_bubbleArea_left { + margin-left: 0px; + margin-right: auto; + text-align: left; + } + + .sc_EventTile_bubble { + background-color: $message-bubble-background; + padding: 7px 10px; + border-radius: 8px; + //margin: 10px auto; + max-width: max-content; + // Min width: respect/"hide" bubble tail + min-width: 20px; + position: relative; + //box-sizing: content-box; + //display: flex; + //flex-wrap: wrap; + + // Don't inherit bubbleArea alignment + text-align: start; + + &.sc_EventTile_bubble_tail::before { + content: ''; + border: 16px solid transparent; + border-top-color: $message-bubble-background; + border-bottom: 0; + position: absolute; + top: 0; + } + + > .mx_SenderProfile { + // Sender-profile within bubble + max-width: 100%; + margin-bottom: 4px; + display: block; + } + + .mx_ReplyThread_wrapper { + margin-top: 3px; + } + + > *:not(.mx_ReplyThread_wrapper) { + .markdown-body > blockquote { + margin-top: 3px !important; + } + + .sc_PlaceholderTimestamp { + display: inline-flex; + margin-inline-start: .7em; + opacity: 0; + line-height: 1; // don't add additional height + + .mx_MessageTimestamp { + visibility: visible !important; + position: unset; + width: unset; + text-align: unset; + display: inline; + font-size: 0.85em; + } + + &:dir(rtl) { + display: block; + margin-inline-start: unset; + } + } + + .sc_LinkedTimestamp { + position: absolute; + inset-block-end: 4px; + inset-inline-end: 10px; + + // Get rid of underline with custom themes + text-decoration: none; + + .mx_MessageTimestamp { + visibility: visible !important; + position: unset; + text-align: unset; + display: inline; + font-size: 0.85em; + } + + &:dir(rtl) { + position: absolute; + display: block; + inset-inline-end: unset; + right: 10px; + } + } + } + } + + .sc_EventTile_bubble_left { + margin-left: 0px; + margin-right: auto; + + &.sc_EventTile_bubble_tail::before { + left: -8px; + } + } + + .sc_EventTile_bubbleArea_right { + margin-right: 16px; + margin-left: auto; + text-align: right; + + .mx_ReactionsRow { + justify-content: right; + flex-direction: row-reverse; + + .mx_ReactionsRow_addReactionButton { + margin-left: unset; + margin-right: 6px; + } + } + } + + .sc_EventTile_bubble_right { + margin-right: 0px; + margin-left: auto; + + &.sc_EventTile_bubble_tail::before { + right: -8px; + } + } + + .sc_EventTile_bubble_self { + &:not(.sc_EventTile_bubble_media) { + background-color: $message-bubble-background-self; + + &.sc_EventTile_bubble_tail::before { + border-top-color: $message-bubble-background-self; + } + } + + .mx_MImageBody_thumbnail, span.mx_MVideoBody video.mx_MVideoBody { + border-color: $message-bubble-background-self; + } + } + + .sc_EventTile_bubbleArea_center { + margin-left: auto; + margin-right: auto; + } + + .sc_EventTile_bubble_center { + margin-left: auto; + margin-right: auto; + } + + .sc_EventTile_bubbleArea_info { + width: 76%; + } + + .sc_EventTile_bubble_info { + text-align: center; + background: transparent; + box-shadow: inset 0px 0px 0px 1px rgba($roomtopic-color, 0.15); + + .mx_EventTileBubble { + background-color: transparent; + padding: 0; + margin: 0; + max-width: unset; + text-align: start; + + &::before, &::after { + margin-top: 1px; + } + + .mx_EventTileBubble_title { + opacity: 0.8; + } + } + + .mx_EventTile_avatar { + display: inline; + position: unset; + + &:after { + content: "\00a0"; + } + } + + .mx_BaseAvatar { + display: inline-flex; + vertical-align: middle; + margin-top: -2px; + } + + .mx_EventListSummary_avatars { + padding-top: unset; + } + + .mx_TextualEvent, .mx_RoomAvatarEvent { + display: inline; + opacity: 0.8; + } + + .mx_RoomAvatarEvent_avatar { + top: unset; + } + } + + .sc_EventTile_bubble_notice { + opacity: 0.6; + } + + .sc_EventTile_bubble_media { + max-width: 600px; + position: relative; + + &.sc_EventTile_bubble_right { + margin-left: auto; + + > * { + margin-left: auto; + } + + .mx_MImageBody_thumbnail { + left: unset; + right: 0; + } + } + + // maybe: + // & + .mx_ReactionsRow { + // margin-top: 0; + // margin-bottom: 0; + // } + } + + .sc_EventTile_bubble_sticker { + .mx_MImageBody_thumbnail { + border: none; + } + } + + .mx_EventTile_selected { + .sc_EventTile_bubble { + background: $message-bubble-background-selected; + + &.sc_EventTile_bubble_tail::before { + border-top-color: $message-bubble-background-selected; + } + } + + .sc_EventTile_bubble_media { + > .mx_MImageBody { + .mx_MImageBody_thumbnail_container { + background: $accent-color; + border-radius: 8px; + } + .mx_MImageBody_thumbnail { + opacity: 0.7; + } + } + + > span.mx_MVideoBody { + .sc_MVideoBody_video_container { + background: $accent-color; + border-radius: 8px; + display: block; + } + + video.mx_MVideoBody { + opacity: 0.7; + } + } + } + } + + // single side bubbles overrides + &.sc_BubbleLayout_singleSide { + .sc_EventTile_bubbleArea { + width: calc(100% - 16px); + max-width: unset; + } + + .sc_EventTile_bubbleArea_info { + width: 80%; + max-width: 72rem; + } + + .sc_EventTile_bubbleLine { + max-width: 104rem; + } + + .sc_EventTile_bubbleLine_info, .mx_DateSeparator, .mx_EventListSummary { + max-width: calc($left-gutter + 104rem); + } + } +} + +/* Compact layout overrides */ + +// .mx_MatrixChat_useCompactLayout { +// .sc_BubbleLayout { +// // nothing (yet?) +// // ToDo: figure out if there is a useful way to make it more compact +// // maybe reduce text line-height and some paddings and margins +// } +// } diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 2d0e3d2a8b0..6e0c62df8f9 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -181,6 +181,9 @@ $visual-bell-bg-color: #800; $room-warning-bg-color: $header-panel-bg-color; $dark-panel-bg-color: $header-panel-bg-color; +$message-bubble-background: #424242; +$message-bubble-background-self: #303030; +$message-bubble-background-selected: #3F4931; $panel-gradient: rgba(34, 38, 46, 0), rgba(34, 38, 46, 1); $message-action-bar-bg-color: $header-panel-bg-color; diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index a852ad94e95..04264a06952 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -175,6 +175,9 @@ $visual-bell-bg-color: #800; $room-warning-bg-color: $header-panel-bg-color; $dark-panel-bg-color: $header-panel-bg-color; +$message-bubble-background: #424242; +$message-bubble-background-self: #303030; +$message-bubble-background-selected: #3F4931; $panel-gradient: rgba(34, 38, 46, 0), rgba(34, 38, 46, 1); $message-action-bar-bg-color: $header-panel-bg-color; diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index 84666bc662c..2936f865269 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -298,6 +298,9 @@ $authpage-primary-color: #232f32; $authpage-secondary-color: #61708b; $dark-panel-bg-color: $secondary-accent-color; +$message-bubble-background: #eeeeee; +$message-bubble-background-self: #F1F8E9; +$message-bubble-background-selected: #DBEDC6; $panel-gradient: rgba(242, 245, 248, 0), rgba(242, 245, 248, 1); $message-action-bar-bg-color: $primary-bg-color; diff --git a/res/themes/light-custom/css/_custom.scss b/res/themes/light-custom/css/_custom.scss index 1b9254d1005..91c9805c736 100644 --- a/res/themes/light-custom/css/_custom.scss +++ b/res/themes/light-custom/css/_custom.scss @@ -140,3 +140,8 @@ $event-highlight-bg-color: var(--timeline-highlights-color); // // redirect some variables away from their hardcoded values in the light theme $settings-grey-fg-color: $primary-fg-color; + +// bubble layout variables +$message-bubble-background: var(--timeline-bubble-background-incoming); +$message-bubble-background-self: var(--timeline-bubble-background-outgoing); +$message-bubble-background-selected: var(--timeline-bubble-background-selected); diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index c889f43d0b9..d5bf37b5dda 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -296,6 +296,9 @@ $authpage-primary-color: #232f32; $authpage-secondary-color: #61708b; $dark-panel-bg-color: $secondary-accent-color; +$message-bubble-background: #eeeeee; +$message-bubble-background-self: #F1F8E9; +$message-bubble-background-selected: #DBEDC6; $panel-gradient: rgba(242, 245, 248, 0), rgba(242, 245, 248, 1); $message-action-bar-bg-color: $primary-bg-color; diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index eb9611a6fc6..021e9190c29 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -148,6 +148,9 @@ export default class MessagePanel extends React.Component { // which layout to use layout: LayoutPropType, + // whether to use single side bubbles + singleSideBubbles: PropTypes.bool, + // whether or not to show flair at all enableFlair: PropTypes.bool, }; @@ -672,6 +675,7 @@ export default class MessagePanel extends React.Component { getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} layout={this.props.layout} + singleSideBubbles={this.props.singleSideBubbles} enableFlair={this.props.enableFlair} showReadReceipts={this.props.showReadReceipts} /> diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index fe90d2f8730..91f372cc210 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -180,6 +180,8 @@ export interface IState { canReact: boolean; canReply: boolean; layout: Layout; + singleSideBubbles: boolean; + adaptiveSideBubbles: boolean; lowBandwidth: boolean; showReadReceipts: boolean; showRedactions: boolean; @@ -243,6 +245,8 @@ export default class RoomView extends React.Component { canReact: false, canReply: false, layout: SettingsStore.getValue("layout"), + singleSideBubbles: SettingsStore.getValue("singleSideBubbles"), + adaptiveSideBubbles: SettingsStore.getValue("adaptiveSideBubbles"), lowBandwidth: SettingsStore.getValue("lowBandwidth"), showReadReceipts: true, showRedactions: true, @@ -279,6 +283,15 @@ export default class RoomView extends React.Component { SettingsStore.watchSetting("layout", null, () => this.setState({ layout: SettingsStore.getValue("layout") }), ), + SettingsStore.watchSetting("singleSideBubbles", null, () => + this.setState({ singleSideBubbles: SettingsStore.getValue("singleSideBubbles") }), + ), + SettingsStore.watchSetting("adaptiveSideBubbles", null, () => + this.setState({ + adaptiveSideBubbles: SettingsStore.getValue("adaptiveSideBubbles"), + singleSideBubbles: SettingsStore.getValue("singleSideBubbles"), // restore default + }), + ), SettingsStore.watchSetting("lowBandwidth", null, () => this.setState({ lowBandwidth: SettingsStore.getValue("lowBandwidth") }), ), @@ -608,6 +621,8 @@ export default class RoomView extends React.Component { atEndOfLiveTimeline: this.messagePanel.isAtEndOfLiveTimeline(), }); } + + this.onResize(); } componentWillUnmount() { @@ -705,12 +720,6 @@ export default class RoomView extends React.Component { } } - private onLayoutChange = () => { - this.setState({ - layout: SettingsStore.getValue("layout"), - }); - }; - private onRightPanelStoreUpdate = () => { this.setState({ showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom, @@ -1435,6 +1444,8 @@ export default class RoomView extends React.Component { resultLink={resultLink} permalinkCreator={this.getPermalinkCreatorForRoom(room)} onHeightChanged={onHeightChanged} + layout={this.state.layout} + singleSideBubbles={this.state.singleSideBubbles} />); } return ret; @@ -1992,6 +2003,13 @@ export default class RoomView extends React.Component { }; } + const layout = { + "mx_IRCLayout": this.state.layout == Layout.IRC, + "mx_GroupLayout": this.state.layout == Layout.Group, + "sc_BubbleLayout": this.state.layout == Layout.Bubble, + "sc_BubbleLayout_singleSide": this.state.layout == Layout.Bubble && this.state.singleSideBubbles, + }; + // if we have search results, we keep the messagepanel (so that it preserves its // scroll state), but hide it. let searchResultsPanel; @@ -2004,10 +2022,16 @@ export default class RoomView extends React.Component {
); } else { + const searchResultsPanelClassNames = classNames( + "mx_RoomView_messagePanel", + "mx_RoomView_searchResultsPanel", + layout, + ); + searchResultsPanel = ( @@ -2026,10 +2050,8 @@ export default class RoomView extends React.Component { const messagePanelClassNames = classNames( "mx_RoomView_messagePanel", - { - "mx_IRCLayout": this.state.layout == Layout.IRC, - "mx_GroupLayout": this.state.layout == Layout.Group, - }); + layout, + ); // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); const messagePanel = ( @@ -2054,6 +2076,7 @@ export default class RoomView extends React.Component { resizeNotifier={this.props.resizeNotifier} showReactions={true} layout={this.state.layout} + singleSideBubbles={this.state.singleSideBubbles} />); let topUnreadMessagesBar = null; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index bb62745d986..4bee16cb400 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -122,6 +122,9 @@ class TimelinePanel extends React.Component { // which layout to use layout: LayoutPropType, + // whether to use single side bubbles + singleSideBubbles: PropTypes.bool, + // whether to always show timestamps for an event alwaysShowTimestamps: PropTypes.bool, } @@ -1454,6 +1457,7 @@ class TimelinePanel extends React.Component { editState={this.state.editState} showReactions={this.props.showReactions} layout={this.props.layout} + singleSideBubbles={this.props.singleSideBubbles} enableFlair={SettingsStore.getValue(UIFeature.Flair)} /> ); diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index 86d3e082ad3..59e161c4f6e 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -22,6 +22,7 @@ import MemberAvatar from '../avatars/MemberAvatar'; import { _t } from '../../../languageHandler'; import {useStateToggle} from "../../../hooks/useStateToggle"; import AccessibleButton from "./AccessibleButton"; +import { Layout } from '../../../settings/Layout'; interface IProps { // An array of member events to summarise @@ -37,7 +38,9 @@ interface IProps { // An array of EventTiles to render when expanded children: ReactChildren, // Called when the event list expansion is toggled - onToggle?(): void; + onToggle?(): void, + // which layout to use + layout: Layout, } const EventListSummary: React.FC = ({ @@ -48,6 +51,7 @@ const EventListSummary: React.FC = ({ startExpanded, summaryMembers = [], summaryText, + layout, }) => { const [expanded, toggleExpanded] = useStateToggle(startExpanded); @@ -63,9 +67,9 @@ const EventListSummary: React.FC = ({ // If we are only given few events then just pass them through if (events.length < threshold) { return ( -
  • +
    { children } -
  • +
    ); } @@ -77,18 +81,35 @@ const EventListSummary: React.FC = ({ ; } else { const avatars = summaryMembers.map((m) => ); - body = ( -
    -
    - - { avatars } - - - { summaryText } - + if (layout == Layout.Bubble) { + body = ( +
    +
    +
    + + { avatars } + + + { summaryText } + +
    +
    -
    - ); + ); + } else { + body = ( +
    +
    + + { avatars } + + + { summaryText } + +
    +
    + ); + } } return ( diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index b15fbbed2b8..e4849db6009 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -121,6 +121,7 @@ export default class EventTilePreview extends React.Component { const className = classnames(this.props.className, { "mx_IRCLayout": this.props.layout == Layout.IRC, "mx_GroupLayout": this.props.layout == Layout.Group, + "sc_BubbleLayout": this.props.layout == Layout.Bubble, }); return
    diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js index 0d5e449fc00..1292d6d3039 100644 --- a/src/components/views/messages/MAudioBody.js +++ b/src/components/views/messages/MAudioBody.js @@ -15,6 +15,8 @@ */ import React from 'react'; +import PropTypes from 'prop-types'; + import MFileBody from './MFileBody'; import { decryptFile } from '../../../utils/DecryptFile'; @@ -25,6 +27,10 @@ import {mediaFromContent} from "../../../customisations/Media"; @replaceableComponent("views.messages.MAudioBody") export default class MAudioBody extends React.Component { + static propTypes = { + scBubbleGroupTimestamp: PropTypes.object, + }; + constructor(props) { super(props); this.state = { diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index 8f464e08bde..c104fadab8e 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -102,6 +102,7 @@ export default class MFileBody extends React.Component { tileShape: PropTypes.string, /* whether or not to show the default placeholder for the file. Defaults to true. */ showGenericPlaceholder: PropTypes.bool, + scBubbleGroupTimestamp: PropTypes.object, }; static defaultProps = { @@ -214,6 +215,7 @@ export default class MFileBody extends React.Component { { _t("Decrypt %(text)s", { text: text }) }
    + { this.props.scBubbleGroupTimestamp } ); } @@ -256,6 +258,7 @@ export default class MFileBody extends React.Component { ref={this._iframe} sandbox="allow-scripts allow-downloads allow-downloads-without-user-activation" />
    + { this.props.scBubbleGroupTimestamp } ); } else if (contentUrl) { @@ -318,6 +321,7 @@ export default class MFileBody extends React.Component { { content.info && content.info.size ? filesize(content.info.size) : "" } + { this.props.scBubbleGroupTimestamp } ); } else { @@ -330,6 +334,7 @@ export default class MFileBody extends React.Component { { _t("Download %(text)s", { text: text }) } + { this.props.scBubbleGroupTimestamp } ); } @@ -338,6 +343,7 @@ export default class MFileBody extends React.Component { return {placeholder} { _t("Invalid file%(extra)s", { extra: extra }) } + { this.props.scBubbleGroupTimestamp } ; } } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 6505b1d66ae..c253e0b14ed 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -44,6 +44,10 @@ export default class MImageBody extends React.Component { /* the permalinkCreator */ permalinkCreator: PropTypes.object, + + scBubble: PropTypes.bool, + scBubbleGroupTimestamp: PropTypes.object, + scBubbleActionBar: PropTypes.object, }; static contextType = MatrixClientContext; @@ -352,7 +356,7 @@ export default class MImageBody extends React.Component { /> ); } - return this.wrapImage(contentUrl, imageElement); + return {thumbnail: this.wrapImage(contentUrl, imageElement)}; } infoWidth = this.state.loadedImageDimensions.naturalWidth; infoHeight = this.state.loadedImageDimensions.naturalHeight; @@ -426,7 +430,7 @@ export default class MImageBody extends React.Component { ); - return this.wrapImage(contentUrl, thumbnail); + return {thumbnail: this.wrapImage(contentUrl, thumbnail), maxWidth: maxWidth}; } // Overidden by MStickerBody @@ -460,6 +464,7 @@ export default class MImageBody extends React.Component { { _t("Error decrypting image") } + { this.props.scBubbleActionBar } ); } @@ -472,12 +477,15 @@ export default class MImageBody extends React.Component { thumbUrl = this._getThumbUrl(); } - const thumbnail = this._messageContent(contentUrl, thumbUrl, content); + const messageContent = this._messageContent(contentUrl, thumbUrl, content); + const thumbnail = messageContent.thumbnail; + const maxWidth = messageContent.maxWidth; const fileBody = this.getFileBody(); - return + return { thumbnail } { fileBody } + { this.props.scBubbleActionBar } ; } } diff --git a/src/components/views/messages/MStickerBody.js b/src/components/views/messages/MStickerBody.js index 54eb7649b42..543c9ed19bf 100644 --- a/src/components/views/messages/MStickerBody.js +++ b/src/components/views/messages/MStickerBody.js @@ -36,7 +36,8 @@ export default class MStickerBody extends MImageBody { if (!this.state.showImage) { onClick = this.onClick; } - return
    { children }
    ; + const wrapper =
    { children }
    ; + return [wrapper, this.props.scBubbleGroupTimestamp]; } // Placeholder to show in place of the sticker image if diff --git a/src/components/views/messages/MVideoBody.tsx b/src/components/views/messages/MVideoBody.tsx index 2efdce506e7..d5f79766ae8 100644 --- a/src/components/views/messages/MVideoBody.tsx +++ b/src/components/views/messages/MVideoBody.tsx @@ -29,6 +29,8 @@ interface IProps { mxEvent: any; /* called when the video has loaded */ onHeightChanged: () => void; + + scBubbleActionBar: any; } interface IState { @@ -193,6 +195,7 @@ export default class MVideoBody extends React.PureComponent { { _t("Error decrypting video") } + { this.props.scBubbleActionBar } ); } @@ -207,6 +210,7 @@ export default class MVideoBody extends React.PureComponent {
    + { this.props.scBubbleActionBar }
    ); } @@ -230,23 +234,26 @@ export default class MVideoBody extends React.PureComponent { } } return ( - - + + + + + { this.props.scBubbleActionBar } ); } diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index 78e0dc422d7..026a4de94cd 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -49,6 +49,10 @@ export default class MessageEvent extends React.Component { /* the permalinkCreator */ permalinkCreator: PropTypes.object, + + scBubble: PropTypes.bool, + scBubbleGroupTimestamp: PropTypes.object, + scBubbleActionBar: PropTypes.object, }; constructor(props) { @@ -126,6 +130,9 @@ export default class MessageEvent extends React.Component { onHeightChanged={this.props.onHeightChanged} onMessageAllowed={this.onTileUpdate} permalinkCreator={this.props.permalinkCreator} + scBubble={this.props.scBubble} + scBubbleGroupTimestamp={this.props.scBubbleGroupTimestamp} + scBubbleActionBar={this.props.scBubbleActionBar} />; } } diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 3adfea6ee6b..0c111a9fa5b 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -58,6 +58,8 @@ export default class TextualBody extends React.Component { /* the shape of the tile, used */ tileShape: PropTypes.string, + + scBubbleGroupTimestamp: PropTypes.object, }; constructor(props) { @@ -201,6 +203,7 @@ export default class TextualBody extends React.Component { _wrapInDiv(pre) { const div = document.createElement("div"); div.className = "mx_EventTile_pre_container"; + div.dir = "auto"; // Insert containing div in place of
     block
             pre.parentNode.replaceChild(div, pre);
    @@ -489,7 +492,7 @@ export default class TextualBody extends React.Component {
         render() {
             if (this.props.editState) {
                 const EditMessageComposer = sdk.getComponent('rooms.EditMessageComposer');
    -            return ;
    +            return ;
             }
             const mxEvent = this.props.mxEvent;
             const content = mxEvent.getContent();
    @@ -534,7 +537,7 @@ export default class TextualBody extends React.Component {
             switch (content.msgtype) {
                 case "m.emote":
                     return (
    -                    
    +                    
                     );
                 case "m.notice":
                     return (
    -                    
    +                    
                             { body }
                             { widgets }
    +                        { this.props.scBubbleGroupTimestamp }
                         
                     );
                 default: // including "m.text"
                     return (
    -                    
    +                    
                             { body }
                             { widgets }
    +                        { this.props.scBubbleGroupTimestamp }
                         
                     );
             }
    diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx
    index 0861f7fd5d7..70b6e9caeee 100644
    --- a/src/components/views/rooms/EventTile.tsx
    +++ b/src/components/views/rooms/EventTile.tsx
    @@ -1,6 +1,8 @@
     /*
     Copyright 2015-2021 The Matrix.org Foundation C.I.C.
     Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
    +Copyright 2020 Tobias Büttner 
    +Copyright 2020-2021 Quirin Götz 
     
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
    @@ -261,6 +263,9 @@ interface IProps {
         // which layout to use
         layout: Layout;
     
    +    // whether to use single side bubbles
    +    singleSideBubbles: boolean;
    +
         // whether or not to show flair at all
         enableFlair?: boolean;
     
    @@ -651,11 +656,7 @@ export default class EventTile extends React.Component {
                 // animation will start from the top of the timeline (because it
                 // lost its container).
                 // See also https://github.com/vector-im/element-web/issues/17561
    -            return (
    -                
    - -
    - ); + return (); } const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker'); @@ -677,7 +678,11 @@ export default class EventTile extends React.Component { // If hidden, set offset equal to the offset of the final visible avatar or // else set it proportional to index - left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset; + if (this.props.layout == Layout.Bubble) { + left = (hidden ? MAX_READ_AVATARS - 1 : i) * receiptOffset; + } else { + left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset; + } const userId = receipt.userId; let readReceiptInfo; @@ -707,22 +712,26 @@ export default class EventTile extends React.Component { let remText; if (!this.state.allReadAvatars) { const remainder = receipts.length - MAX_READ_AVATARS; + + let style; + if (this.props.layout == Layout.Bubble) { + style = { left: "calc(" + toRem(left) + " + " + receiptOffset + "px)" }; + } else { + style = { right: "calc(" + toRem(-left) + " + " + receiptOffset + "px)" }; + } + if (remainder > 0) { remText = { remainder }+ + style={style}>{ remainder }+ ; } } - return ( -
    - - { remText } - { avatars } - -
    - ) + return + { remText } + { avatars } + ; } onSenderProfileClick = event => { @@ -882,18 +891,28 @@ export default class EventTile extends React.Component { const isRedacted = isMessageEvent(this.props.mxEvent) && this.props.isRedacted; const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure(); + const client = MatrixClientPeg.get(); + const me = client && client.getUserId(); + const scBubbleEnabled = this.props.layout == Layout.Bubble + && this.props.tileShape !== 'reply_preview' && this.props.tileShape !== 'reply' + && this.props.tileShape !== 'notif' && this.props.tileShape !== 'file_grid'; + const sentByMe = me === this.props.mxEvent.getSender(); + const showRight = sentByMe && !this.props.singleSideBubbles; + const showLeft = !sentByMe || this.props.singleSideBubbles; + const isEditing = !!this.props.editState; const classes = classNames({ - mx_EventTile_bubbleContainer: isBubbleMessage, + mx_EventTile_bubbleContainer: isBubbleMessage && this.props.layout != Layout.Bubble, mx_EventTile: true, mx_EventTile_isEditing: isEditing, - mx_EventTile_info: isInfoMessage, + mx_EventTile_info: isInfoMessage && this.props.layout != Layout.Bubble, mx_EventTile_12hr: this.props.isTwelveHour, // Note: we keep the `sending` state class for tests, not for our styles mx_EventTile_sending: !isEditing && isSending, mx_EventTile_highlight: this.props.tileShape === 'notif' ? false : this.shouldHighlight(), mx_EventTile_selected: this.props.isSelectedEvent, - mx_EventTile_continuation: this.props.tileShape ? '' : this.props.continuation, + mx_EventTile_continuation: !isInfoMessage && !isBubbleMessage && + (this.props.tileShape ? '' : this.props.continuation), mx_EventTile_last: this.props.last, mx_EventTile_lastInSection: this.props.lastInSection, mx_EventTile_contextual: this.props.contextual, @@ -903,6 +922,7 @@ export default class EventTile extends React.Component { mx_EventTile_unknown: !isBubbleMessage && this.state.verified === E2E_STATE.UNKNOWN, mx_EventTile_bad: isEncryptionFailure, mx_EventTile_emote: msgtype === 'm.emote', + sc_EventTile_bubbleContainer: scBubbleEnabled, }); // If the tile is in the Sending state, don't speak the message. @@ -924,7 +944,10 @@ export default class EventTile extends React.Component { let avatarSize; let needsSenderProfile; - if (this.props.tileShape === "notif") { + if (!isInfoMessage && scBubbleEnabled && showRight) { + avatarSize = 0; + needsSenderProfile = false; + } else if (this.props.tileShape === "notif") { avatarSize = 24; needsSenderProfile = true; } else if (tileHandler === 'messages.RoomCreate' || isBubbleMessage) { @@ -989,7 +1012,7 @@ export default class EventTile extends React.Component { /> : undefined; const showTimestamp = this.props.mxEvent.getTs() && - (this.props.alwaysShowTimestamps || this.props.last || this.state.hover || this.state.actionBarFocused); + (scBubbleEnabled || this.props.alwaysShowTimestamps || this.props.last || this.state.hover || this.state.actionBarFocused); const timestamp = showTimestamp ? : null; @@ -1036,7 +1059,7 @@ export default class EventTile extends React.Component { />; } - const linkedTimestamp = { { timestamp } ; + const placeholderTimestamp = + { timestamp } + ; + const useIRCLayout = this.props.layout == Layout.IRC; const groupTimestamp = !useIRCLayout ? linkedTimestamp : null; const ircTimestamp = useIRCLayout ? linkedTimestamp : null; const groupPadlock = !useIRCLayout && !isBubbleMessage && this.renderE2EPadlock(); const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock(); + const msgOptionClasses = classNames( + "mx_EventTile_msgOption", + { + "sc_readReceipts_empty": ( + // Don't reserve space below bubbles if there are no read receipts + (!this.props.readReceipts || this.props.readReceipts.length === 0) && + // ToDo: Maybe incorporate sent/sending state into bubble?!? + !(this.shouldShowSentReceipt || this.shouldShowSendingReceipt) + ), + }, + ); let msgOption; if (this.props.showReadReceipts) { const readAvatars = this.getReadAvatars(); - msgOption = readAvatars; + msgOption = ( +
    + { readAvatars } +
    + ); } switch (this.props.tileShape) { @@ -1158,44 +1200,143 @@ export default class EventTile extends React.Component { this.props.alwaysShowTimestamps || this.state.hover, ); - // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers - return ( - React.createElement(this.props.as || "li", { - "ref": this.ref, - "className": classes, - "tabIndex": -1, - "aria-live": ariaLive, - "aria-atomic": "true", - "data-scroll-tokens": scrollToken, - "onMouseEnter": () => this.setState({ hover: true }), - "onMouseLeave": () => this.setState({ hover: false }), - }, [ - ircTimestamp, - sender, - ircPadlock, -
    - { groupTimestamp } - { groupPadlock } - { thread } - - { keyRequestInfo } - { reactionsRow } - { actionBar } -
    , - msgOption, - avatar, - - ]) - ) + if (scBubbleEnabled) { + const infoBubble = isInfoMessage || isBubbleMessage; + + const mediaBodyTypes = ['m.image', /* 'm.file', */ /* 'm.audio', */ 'm.video']; + const mediaEvTypes = ['m.sticker']; + let mediaBody = false; + let stickerBody = false; + let noticeBody = false; + + const content = this.props.mxEvent.getContent(); + const type = this.props.mxEvent.getType(); + const msgtype = content.msgtype; + + if (msgtype && mediaBodyTypes.indexOf(msgtype) != -1) { + mediaBody = true; + } + + if (type && mediaEvTypes.indexOf(type) != -1) { + mediaBody = true; + stickerBody = true; + } + + if (msgtype && msgtype == 'm.notice') noticeBody = true; + + const bubbleLineClasses = classNames( + "mx_EventTile_line", + "sc_EventTile_bubbleLine", + { + "sc_EventTile_bubbleLine_info": infoBubble, + }, + ); + const bubbleAreaClasses = classNames( + "sc_EventTile_bubbleArea", + { + "sc_EventTile_bubbleArea_right": !infoBubble && showRight, + "sc_EventTile_bubbleArea_left": !infoBubble && showLeft, + "sc_EventTile_bubbleArea_center": infoBubble, + "sc_EventTile_bubbleArea_info": infoBubble, + }, + ); + const bubbleClasses = classNames( + { + "sc_EventTile_bubble": !mediaBody, + "sc_EventTile_bubble_media": mediaBody, + "sc_EventTile_bubble_info": infoBubble, + "sc_EventTile_bubble_self": !infoBubble && sentByMe, + "sc_EventTile_bubble_right": !infoBubble && showRight, + "sc_EventTile_bubble_left": !infoBubble && showLeft, + "sc_EventTile_bubble_center": infoBubble, + "sc_EventTile_bubble_tail": !infoBubble && !this.props.continuation, + "sc_EventTile_bubble_notice": noticeBody, + "sc_EventTile_bubble_sticker": stickerBody, + }, + ); + + // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers + return ( + React.createElement(this.props.as || "li", { + "ref": this.ref, + "className": classes, + "tabIndex": -1, + "aria-live": ariaLive, + "aria-atomic": "true", + "data-scroll-tokens": scrollToken, + "onMouseEnter": () => this.setState({ hover: true }), + "onMouseLeave": () => this.setState({ hover: false }), + }, [ +
    + { groupPadlock } +
    +
    + { sender } + { thread } + { infoBubble ? avatar : null } + + { !mediaBody ? actionBar : null } +
    + { keyRequestInfo } + { reactionsRow } +
    +
    , + !infoBubble ? avatar : null, + msgOption, + + ]) + ); + } else { + // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers + return ( + React.createElement(this.props.as || "li", { + "ref": this.ref, + "className": classes, + "tabIndex": -1, + "aria-live": ariaLive, + "aria-atomic": "true", + "data-scroll-tokens": scrollToken, + "onMouseEnter": () => this.setState({ hover: true }), + "onMouseLeave": () => this.setState({ hover: false }), + }, [ + ircTimestamp, + sender, + ircPadlock, +
    + { groupTimestamp } + { groupPadlock } + { thread } + + { keyRequestInfo } + { reactionsRow } + { actionBar } +
    , + msgOption, + avatar, + + ]) + ); + } } } } @@ -1357,15 +1498,11 @@ class SentReceipt extends React.PureComponent; } - return ( -
    - - - {nonCssBadge} - {tooltip} - - -
    - ); + return + + {nonCssBadge} + {tooltip} + + ; } } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 36710699036..a126960b63c 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -33,6 +33,7 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import ReplyPreview from "./ReplyPreview"; import {UIFeature} from "../../../settings/UIFeature"; import {UPDATE_EVENT} from "../../../stores/AsyncStore"; +import { Layout } from '../../../settings/Layout'; import {replaceableComponent} from "../../../utils/replaceableComponent"; import VoiceRecordComposerTile from "./VoiceRecordComposerTile"; import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore"; @@ -183,6 +184,7 @@ interface IProps { permalinkCreator: RoomPermalinkCreator; replyToEvent?: MatrixEvent; e2eStatus?: E2EStatus; + layout: Layout; } interface IState { @@ -436,6 +438,17 @@ export default class MessageComposer extends React.Component { ); } + const msgComposerClassNames = classNames( + "mx_MessageComposer", + { + // IRC layout has nothing for message composer so use group layout stuff + // "mx_IRCLayout": this.props.layout == Layout.IRC, + // "mx_GroupLayout": this.props.layout == Layout.Group, + "mx_GroupLayout": this.props.layout == Layout.IRC || this.props.layout == Layout.Group, + "sc_BubbleLayout": this.props.layout == Layout.Bubble, + }, + ); + let recordingTooltip; const secondsLeft = Math.round(this.state.recordingTimeLeftSeconds); if (secondsLeft) { @@ -446,7 +459,7 @@ export default class MessageComposer extends React.Component { } return ( -
    +
    {recordingTooltip}
    diff --git a/src/components/views/rooms/SearchResultTile.js b/src/components/views/rooms/SearchResultTile.js index 3b79aa62463..cfdbb177f7d 100644 --- a/src/components/views/rooms/SearchResultTile.js +++ b/src/components/views/rooms/SearchResultTile.js @@ -70,6 +70,8 @@ export default class SearchResultTile extends React.Component { isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} alwaysShowTimestamps={alwaysShowTimestamps} enableFlair={SettingsStore.getValue(UIFeature.Flair)} + layout={this.props.layout} + singleSideBubbles={this.props.singleSideBubbles} /> )); } diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 9e27ed968e7..ee44d069fea 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -37,6 +37,8 @@ import StyledRadioGroup from "../../../elements/StyledRadioGroup"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import { UIFeature } from "../../../../../settings/UIFeature"; import { Layout } from "../../../../../settings/Layout"; +import classNames from 'classnames'; +import StyledRadioButton from '../../../elements/StyledRadioButton'; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; import { compare } from "../../../../../utils/strings"; @@ -65,6 +67,7 @@ interface IState extends IThemeState { systemFont: string; showAdvanced: boolean; layout: Layout; + adaptiveSideBubbles: boolean; // User profile data for the message preview userId: string; displayName: string; @@ -90,6 +93,7 @@ export default class AppearanceUserSettingsTab extends React.Component): void => { + let layout; + switch (e.target.value) { + case "irc": layout = Layout.IRC; break; + case "group": layout = Layout.Group; break; + case "bubble": layout = Layout.Bubble; break; + } + + this.setState({ + layout: layout, + }); + + SettingsStore.setValue("layout", null, SettingLevel.DEVICE, layout); + }; + private onIRCLayoutChange = (enabled: boolean) => { if (enabled) { this.setState({layout: Layout.IRC}); @@ -367,6 +386,77 @@ export default class AppearanceUserSettingsTab extends React.Component; } + private renderLayoutSection = () => { + return
    + {_t("Message layout")} + +
    +
    + + + {"IRC"} + +
    +
    +
    + + + {_t("Modern")} + +
    +
    +
    + + + {_t("Message bubbles")} + +
    +
    +
    ; + }; + private renderAdvancedSection() { if (!SettingsStore.getValue(UIFeature.AdvancedSettings)) return null; @@ -392,6 +482,19 @@ export default class AppearanceUserSettingsTab extends React.Component + + this.setState({adaptiveSideBubbles: checked})} + disabled={!(this.state.layout == Layout.Bubble)} + /> this.onIRCLayoutChange(ev.target.checked)} @@ -439,6 +542,7 @@ export default class AppearanceUserSettingsTab extends React.Component {this.renderThemeSection()} {this.renderFontSection()} + {this.renderLayoutSection()} {this.renderAdvancedSection()}
    ); diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 3464f952a65..1955daa086c 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -40,6 +40,8 @@ const RoomContext = createContext({ canReact: false, canReply: false, layout: Layout.Group, + singleSideBubbles: false, + adaptiveSideBubbles: false, lowBandwidth: false, showReadReceipts: true, showRedactions: true, diff --git a/src/settings/Layout.ts b/src/settings/Layout.ts index 3a42b2b5102..d451af96f87 100644 --- a/src/settings/Layout.ts +++ b/src/settings/Layout.ts @@ -1,5 +1,6 @@ /* Copyright 2021 Šimon Brandner +Copyright 2021 Quirin Götz Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +20,8 @@ import PropTypes from 'prop-types'; /* TODO: This should be later reworked into something more generic */ export enum Layout { IRC = "irc", - Group = "group" + Group = "group", + Bubble = "bubble" } /* We need this because multiple components are still using JavaScript */ diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 155d0395728..9d00da6776c 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -719,6 +719,16 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: Layout.Group, }, + "singleSideBubbles": { + supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM, + displayName: _td("Show message bubbles on one side only"), + default: false, + }, + "adaptiveSideBubbles": { + supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM, + displayName: _td("Show message bubbles depending on the width either on both sides or only on one side"), + default: true, + }, "showChatEffects": { supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM, displayName: _td("Show chat effects (animations when receiving e.g. confetti)"), From 43d7e16ee37b8ffd70d9892c3fefc998c8b2f029 Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 01:37:39 +0200 Subject: [PATCH 02/23] A lot of wobbling around --- res/css/views/rooms/_BubbleLayout.scss | 2 +- src/components/structures/MessagePanel.js | 15 +++++++++++---- src/components/structures/RoomView.tsx | 18 ++++++++++++++++++ .../views/elements/EventListSummary.tsx | 4 +++- .../views/elements/MemberEventListSummary.tsx | 6 +++++- src/components/views/rooms/EventTile.tsx | 7 +++---- .../views/rooms/LinkPreviewWidget.js | 2 +- 7 files changed, 42 insertions(+), 12 deletions(-) diff --git a/res/css/views/rooms/_BubbleLayout.scss b/res/css/views/rooms/_BubbleLayout.scss index 99876c0f290..3f2254dbb64 100644 --- a/res/css/views/rooms/_BubbleLayout.scss +++ b/res/css/views/rooms/_BubbleLayout.scss @@ -467,7 +467,7 @@ $left-gutter: 56px; display: inline-flex; margin-inline-start: .7em; opacity: 0; - line-height: 1; // don't add additional height + line-height: inherit; // don't add additional height .mx_MessageTimestamp { visibility: visible !important; diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 021e9190c29..a50847dbf41 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -552,7 +552,8 @@ export default class MessagePanel extends React.Component { for (const Grouper of groupers) { if (Grouper.canStartGroup(this, mxEv)) { - grouper = new Grouper(this, mxEv, prevEvent, lastShownEvent, nextEvent, nextTile); + grouper = new Grouper(this, mxEv, prevEvent, lastShownEvent, nextEvent, nextTile, + this.props.layout); } } if (!grouper) { @@ -919,7 +920,7 @@ class CreationGrouper { return ev.getType() === "m.room.create"; }; - constructor(panel, createEvent, prevEvent, lastShownEvent) { + constructor(panel, createEvent, prevEvent, lastShownEvent, nextEvent, nextEventTile, layout) { this.panel = panel; this.createEvent = createEvent; this.prevEvent = prevEvent; @@ -932,6 +933,7 @@ class CreationGrouper { createEvent.getId(), createEvent === lastShownEvent, ); + this.layout = layout; } shouldGroup(ev) { @@ -1030,6 +1032,7 @@ class CreationGrouper { onToggle={panel._onHeightChanged} // Update scroll state summaryMembers={[ev.sender]} summaryText={summaryText} + layout={this.layout} > { eventTiles } , @@ -1052,7 +1055,7 @@ class RedactionGrouper { return panel._shouldShowEvent(ev) && ev.isRedacted(); } - constructor(panel, ev, prevEvent, lastShownEvent, nextEvent, nextEventTile) { + constructor(panel, ev, prevEvent, lastShownEvent, nextEvent, nextEventTile, layout) { this.panel = panel; this.readMarker = panel._readMarkerForEvent( ev.getId(), @@ -1063,6 +1066,7 @@ class RedactionGrouper { this.lastShownEvent = lastShownEvent; this.nextEvent = nextEvent; this.nextEventTile = nextEventTile; + this.layout = layout; } shouldGroup(ev) { @@ -1128,6 +1132,7 @@ class RedactionGrouper { onToggle={panel._onHeightChanged} // Update scroll state summaryMembers={Array.from(senders)} summaryText={_t("%(count)s messages deleted.", { count: eventTiles.length })} + layout={this.layout} > { eventTiles } , @@ -1151,7 +1156,7 @@ class MemberGrouper { return panel._shouldShowEvent(ev) && isMembershipChange(ev); } - constructor(panel, ev, prevEvent, lastShownEvent) { + constructor(panel, ev, prevEvent, lastShownEvent, nextEvent, nextEventTile, layout) { this.panel = panel; this.readMarker = panel._readMarkerForEvent( ev.getId(), @@ -1160,6 +1165,7 @@ class MemberGrouper { this.events = [ev]; this.prevEvent = prevEvent; this.lastShownEvent = lastShownEvent; + this.layout = layout; } shouldGroup(ev) { @@ -1234,6 +1240,7 @@ class MemberGrouper { events={this.events} onToggle={panel._onHeightChanged} // Update scroll state startExpanded={highlightInMels} + layout={this.layout} > { eventTiles } , diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 91f372cc210..3c164a9049a 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1663,6 +1663,23 @@ export default class RoomView extends React.Component { if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50; this.setState({auxPanelMaxHeight: auxPanelMaxHeight}); + + // Let the bubble layout choose between single side and both sides by threshold + if (this.state.layout == Layout.Bubble && this.state.adaptiveSideBubbles && this.roomView.current) { + // ToDo: Find better way to get the current width (references, but which???) + const messagelists = this.roomView.current.getElementsByClassName("mx_RoomView_MessageList"); + let width = 0; + for (let i = 0; i < messagelists.length; i++) { + const boundingBox = messagelists[i].getBoundingClientRect(); + if (boundingBox.width > width) width = boundingBox.width; + } + // ToDo: Make threshold configurable? + if (width < 1280) { + this.setState({singleSideBubbles: false}); + } else { + this.setState({singleSideBubbles: true}); + } + } }; private onStatusBarVisible = () => { @@ -1753,6 +1770,7 @@ export default class RoomView extends React.Component { loading={loading} joining={this.state.joining} oobData={this.props.oobData} + layout={this.state.layout} />
    diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index 59e161c4f6e..3ec16ce6f12 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -84,7 +84,9 @@ const EventListSummary: React.FC = ({ if (layout == Layout.Bubble) { body = (
    -
    +
    { avatars } diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index 0290ef6d83b..9196feb6644 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -24,6 +24,7 @@ import { _t } from '../../../languageHandler'; import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; import { isValid3pidInvite } from "../../../RoomInvite"; import EventListSummary from "./EventListSummary"; +import { Layout } from '../../../settings/Layout'; import {replaceableComponent} from "../../../utils/replaceableComponent"; interface IProps { @@ -39,6 +40,8 @@ interface IProps { startExpanded?: boolean, // An array of EventTiles to render when expanded children: ReactChildren; + // which layout to use + layout: Layout, // Called when the MELS expansion is toggled onToggle?(): void, } @@ -448,6 +451,7 @@ export default class MemberEventListSummary extends React.Component { startExpanded={this.props.startExpanded} children={this.props.children} summaryMembers={[...latestUserAvatarMember.values()]} - summaryText={this.generateSummary(aggregate.names, orderedTransitionSequences)} />; + summaryText={this.generateSummary(aggregate.names, orderedTransitionSequences)} + layout={this.props.layout} />; } } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 70b6e9caeee..fad2398c4c2 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -1012,7 +1012,8 @@ export default class EventTile extends React.Component { /> : undefined; const showTimestamp = this.props.mxEvent.getTs() && - (scBubbleEnabled || this.props.alwaysShowTimestamps || this.props.last || this.state.hover || this.state.actionBarFocused); + (scBubbleEnabled || + this.props.alwaysShowTimestamps || this.props.last || this.state.hover || this.state.actionBarFocused); const timestamp = showTimestamp ? : null; @@ -1284,7 +1285,7 @@ export default class EventTile extends React.Component { onHeightChanged={this.props.onHeightChanged} scBubble={true} scBubbleActionBar={mediaBody ? actionBar : null} - scBubbleGroupTimestamp={[placeholderTimestamp, groupTimestamp]} + scBubbleGroupTimestamp={<>{placeholderTimestamp}{groupTimestamp}} /> { !mediaBody ? actionBar : null }
    @@ -1294,7 +1295,6 @@ export default class EventTile extends React.Component {
    , !infoBubble ? avatar : null, msgOption, - ]) ); } else { @@ -1333,7 +1333,6 @@ export default class EventTile extends React.Component {
    , msgOption, avatar, - ]) ); } diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js index 904040a067d..c43199ea02e 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -138,7 +138,7 @@ export default class LinkPreviewWidget extends React.Component { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); return ( -
    +
    { img }
    From 541b5bfebf89fd93cc61c4b725c586459ae2b0fe Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 02:08:16 +0200 Subject: [PATCH 03/23] Bubble layout: Remove tail --- res/css/views/rooms/_BubbleLayout.scss | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/res/css/views/rooms/_BubbleLayout.scss b/res/css/views/rooms/_BubbleLayout.scss index 3f2254dbb64..fae356a6276 100644 --- a/res/css/views/rooms/_BubbleLayout.scss +++ b/res/css/views/rooms/_BubbleLayout.scss @@ -438,14 +438,14 @@ $left-gutter: 56px; // Don't inherit bubbleArea alignment text-align: start; - &.sc_EventTile_bubble_tail::before { - content: ''; - border: 16px solid transparent; - border-top-color: $message-bubble-background; - border-bottom: 0; - position: absolute; - top: 0; - } + // &.sc_EventTile_bubble_tail::before { + // content: ''; + // border: 16px solid transparent; + // border-top-color: $message-bubble-background; + // border-bottom: 0; + // position: absolute; + // top: 0; + // } > .mx_SenderProfile { // Sender-profile within bubble @@ -514,9 +514,9 @@ $left-gutter: 56px; margin-left: 0px; margin-right: auto; - &.sc_EventTile_bubble_tail::before { - left: -8px; - } + // &.sc_EventTile_bubble_tail::before { + // left: -8px; + // } } .sc_EventTile_bubbleArea_right { @@ -539,18 +539,18 @@ $left-gutter: 56px; margin-right: 0px; margin-left: auto; - &.sc_EventTile_bubble_tail::before { - right: -8px; - } + // &.sc_EventTile_bubble_tail::before { + // right: -8px; + // } } .sc_EventTile_bubble_self { &:not(.sc_EventTile_bubble_media) { background-color: $message-bubble-background-self; - &.sc_EventTile_bubble_tail::before { - border-top-color: $message-bubble-background-self; - } + // &.sc_EventTile_bubble_tail::before { + // border-top-color: $message-bubble-background-self; + // } } .mx_MImageBody_thumbnail, span.mx_MVideoBody video.mx_MVideoBody { @@ -660,9 +660,9 @@ $left-gutter: 56px; .sc_EventTile_bubble { background: $message-bubble-background-selected; - &.sc_EventTile_bubble_tail::before { - border-top-color: $message-bubble-background-selected; - } + // &.sc_EventTile_bubble_tail::before { + // border-top-color: $message-bubble-background-selected; + // } } .sc_EventTile_bubble_media { From fbcac6850cc7d0548270c1401736e784549310bf Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 02:47:17 +0200 Subject: [PATCH 04/23] Hide new layout switcher behind feature flag --- .../tabs/user/AppearanceUserSettingsTab.tsx | 46 +++++++++++-------- src/settings/Settings.tsx | 8 ++++ .../NewLayoutSwitcherController.ts | 29 ++++++++++++ 3 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 src/settings/controllers/NewLayoutSwitcherController.ts diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index ee44d069fea..64318413488 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -482,25 +482,31 @@ export default class AppearanceUserSettingsTab extends React.Component - - this.setState({adaptiveSideBubbles: checked})} - disabled={!(this.state.layout == Layout.Bubble)} - /> - this.onIRCLayoutChange(ev.target.checked)} - > - {_t("Enable experimental, compact IRC style layout")} - + { SettingsStore.getValue("feature_new_layout_switcher") ? + : null + } + { SettingsStore.getValue("feature_new_layout_switcher") ? + this.setState({adaptiveSideBubbles: checked})} + disabled={!(this.state.layout == Layout.Bubble)} + /> : null + } + { !SettingsStore.getValue("feature_new_layout_switcher") ? + this.onIRCLayoutChange(ev.target.checked)} + > + {_t("Enable experimental, compact IRC style layout")} + : null + } {this.renderThemeSection()} + {SettingsStore.getValue("feature_new_layout_switcher") ? this.renderLayoutSection() : null} {this.renderFontSection()} - {this.renderLayoutSection()} {this.renderAdvancedSection()}
    ); diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 9d00da6776c..3a051bd773d 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -41,6 +41,7 @@ import { Layout } from "./Layout"; import ReducedMotionController from './controllers/ReducedMotionController'; import IncompatibleController from "./controllers/IncompatibleController"; import SdkConfig from "../SdkConfig"; +import NewLayoutSwitcherController from './controllers/NewLayoutSwitcherController'; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = [ @@ -285,6 +286,13 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Show info about bridges in room settings"), default: false, }, + "feature_new_layout_switcher": { + isFeature: true, + supportedLevels: LEVELS_FEATURE, + displayName: _td("Explore new ways switching layouts (including a new bubble layout)"), + default: false, + controller: new NewLayoutSwitcherController(), + }, "RoomList.backgroundImage": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: null, diff --git a/src/settings/controllers/NewLayoutSwitcherController.ts b/src/settings/controllers/NewLayoutSwitcherController.ts new file mode 100644 index 00000000000..e5df2e6753f --- /dev/null +++ b/src/settings/controllers/NewLayoutSwitcherController.ts @@ -0,0 +1,29 @@ +/* +Copyright 2019, 2020 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 SettingController from "./SettingController"; +import { SettingLevel } from "../SettingLevel"; +import SettingsStore from "../SettingsStore"; +import { Layout } from "../Layout"; + +export default class NewLayoutSwitcherController extends SettingController { + public onChange(level: SettingLevel, roomId: string, newValue: any) { + // On disabling switch back to Layout.Group if Layout.Bubble + if (!newValue && SettingsStore.getValue("layout") == Layout.Bubble) { + SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group); + } + } +} From 4a12d6238b46e99a2e1293098b856ab19ee790aa Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 03:14:59 +0200 Subject: [PATCH 05/23] Add some strings that sprinkled away --- src/i18n/strings/cs.json | 5 ++++- src/i18n/strings/de_DE.json | 8 +++++++- src/i18n/strings/en_EN.json | 8 +++++++- src/i18n/strings/es.json | 5 ++++- src/i18n/strings/et.json | 5 ++++- src/i18n/strings/eu.json | 5 ++++- src/i18n/strings/hu.json | 5 ++++- src/i18n/strings/nl.json | 5 ++++- src/i18n/strings/pt_BR.json | 5 ++++- src/i18n/strings/ru.json | 5 ++++- src/i18n/strings/sk.json | 5 ++++- 11 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 75472b4d38c..20b34c9090d 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -3292,5 +3292,8 @@ "See when people join, leave, or are invited to your active room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do vaší aktivní místnosti", "Kick, ban, or invite people to this room, and make you leave": "Vykopnout, vykázat, pozvat lidi do této místnosti nebo odejít", "Kick, ban, or invite people to your active room, and make you leave": "Vykopnout, vykázat, pozvat lidi do vaší aktivní místnosti nebo odejít", - "See when people join, leave, or are invited to this room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do této místnosti" + "See when people join, leave, or are invited to this room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do této místnosti", + "Show message bubbles on one side only": "Bubliny zpráv zobrazit pouze na jedné straně", + "Show message bubbles depending on the width either on both sides or only on one side": "Bubliny zpráv zobrazit v závislosti na šířce stránky buď na obou stranách, nebo pouze na jedné straně.", + "Message bubbles": "Bubliny zpráv" } diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index dcc5343af4d..a73f07a264a 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -3343,5 +3343,11 @@ "Your feedback will help make spaces better. The more detail you can go into, the better.": "Dein Feedback hilfst uns, die Spaces zu verbessern. Je genauer, desto besser.", "If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Durchs Verlassen lädt %(brand)s mit deaktivierten Spaces neu. Danach kannst du Communities und Custom Tags wieder verwenden.", "sends space invaders": "sendet Space Invaders", - "Sends the given message with a space themed effect": "Sendet die Nachricht mit Raumschiffen" + "Sends the given message with a space themed effect": "Sendet die Nachricht mit Raumschiffen", + "Message layout": "Nachrichtenlayout", + "Modern": "Modern", + "Message bubbles": "Sprechblasen", + "Explore new ways switching layouts (including a new bubble layout)": "Probiere neue Möglichkeiten zum Wechseln von Layouts aus (inklusive eines neuen Layouts mit Sprechblasen)", + "Show message bubbles on one side only": "Sprechblasen nur auf einer Seite anzeigen", + "Show message bubbles depending on the width either on both sides or only on one side": "Sprechblasen abhänging von der Breite auf beiden Seiten oder nur einer Seite anzeigen" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 874dc11bd25..0fe8ca294c7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2988,5 +2988,11 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End" + "End": "End", + "Message layout": "Message layout", + "Modern": "Modern", + "Explore new ways switching layouts (including a new bubble layout)": "Explore new ways switching layouts (including a new bubble layout)", + "Show message bubbles on one side only": "Show message bubbles on one side only", + "Show message bubbles depending on the width either on both sides or only on one side": "Show message bubbles depending on the width either on both sides or only on one side", + "Message bubbles": "Message bubbles" } diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 60f5d06bec5..4353aa40408 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -3315,5 +3315,8 @@ "See when people join, leave, or are invited to your active room": "Ver cuando alguien se una, salga o se le invite a tu sala activa", "Kick, ban, or invite people to this room, and make you leave": "Expulsar, vetar o invitar personas a esta sala, y hacerte salir de ella", "Kick, ban, or invite people to your active room, and make you leave": "Expulsar, vetar o invitar a gente a tu sala activa, o hacerte salir", - "See when people join, leave, or are invited to this room": "Ver cuando alguien se une, sale o se le invita a la sala" + "See when people join, leave, or are invited to this room": "Ver cuando alguien se une, sale o se le invita a la sala", + "Show message bubbles on one side only": "mostrar las burbujas de mensajes en un solo lado", + "Message bubbles": "Burbujas de mensajes", + "Show message bubbles depending on the width either on both sides or only on one side": "Mostrar mensajes de burbuja dependiendo del tamaño tanto en ambos lados o solo en un lado" } diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 5e8d744cca2..f3532bbd069 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -3345,5 +3345,8 @@ "Send and receive voice messages": "Saada ja võta vastu häälsõnumeid", "Your feedback will help make spaces better. The more detail you can go into, the better.": "Sinu tagasiside aitab teha kogukonnakeskuseid paremaks. Mida detailsemalt sa oma arvamust kirjeldad, seda parem.", "If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Kui sa lahkud, siis käivitame %(brand)s uuesti nii, et kogukonnakeskused ei ole kasutusel. Vana tüüpi kogukonnad ja kohandatud sildid saavad jälle olema kasutusel.", - "Message search initialisation failed": "Sõnumite otsingu alustamine ei õnnestunud" + "Message search initialisation failed": "Sõnumite otsingu alustamine ei õnnestunud", + "Show message bubbles on one side only": "Näita sõnumimulle ainult ühel poolel", + "Show message bubbles depending on the width either on both sides or only on one side": "Näita sõnumimullid sõltuvalt laiusest kas mõlemal või ainult ühel küljel", + "Message bubbles": "Sõnumimullid" } diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 2740ea20796..23edadf8813 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -2293,5 +2293,8 @@ "Wrong file type": "Okerreko fitxategi-mota", "Looks good!": "Itxura ona du!", "Search rooms": "Bilatu gelak", - "User menu": "Erabiltzailea-menua" + "User menu": "Erabiltzailea-menua", + "Show message bubbles on one side only": "Erakutsi mezu burbuilak alde batetik bakarrik", + "Message bubbles": "Mezu burbuilak", + "Show message bubbles depending on the width either on both sides or only on one side": "Erakutsi mezu burbuilak arabera zabalera bai, bi aldeetatik edo alde batetik bakarrik" } diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index a6e99928668..990f11d5d39 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -3370,5 +3370,8 @@ "See when people join, leave, or are invited to your active room": "Emberek belépésének, távozásának vagy meghívásának a megjelenítése az aktív szobájában", "Kick, ban, or invite people to your active room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket az aktív szobába és, hogy ön elhagyja a szobát", "See when people join, leave, or are invited to this room": "Emberek belépésének, távozásának vagy meghívásának a megjelenítése ebben a szobában", - "Kick, ban, or invite people to this room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket ebbe a szobába és, hogy ön elhagyja a szobát" + "Kick, ban, or invite people to this room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket ebbe a szobába és, hogy ön elhagyja a szobát", + "Show message bubbles on one side only": "Üzenetbuborékok megjelenítése csak az egyik oldalon", + "Message bubbles": "Üzenet buborékok", + "Show message bubbles depending on the width either on both sides or only on one side": "Üzenetbuborékok megjelenítése a szélességtől függően mindkét oldalon vagy csak az egyik oldalon" } diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 16f74e7b2df..cde8b1317d4 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -3261,5 +3261,8 @@ "See when people join, leave, or are invited to your active room": "Zie wanneer personen deelnemen, vertrekken of worden uitgenodigd in uw actieve gesprek", "Kick, ban, or invite people to your active room, and make you leave": "Verwijder, verban of nodig personen uit voor uw actieve gesprek en uzelf laten vertrekken", "See when people join, leave, or are invited to this room": "Zie wanneer personen deelnemen, vertrekken of worden uitgenodigd voor dit gesprek", - "Kick, ban, or invite people to this room, and make you leave": "Verwijder, verban of verwijder personen uit dit gesprek en uzelf laten vertrekken" + "Kick, ban, or invite people to this room, and make you leave": "Verwijder, verban of verwijder personen uit dit gesprek en uzelf laten vertrekken", + "Show message bubbles on one side only": "Boodschappenbubbels slechts aan één kant tonen", + "Show message bubbles depending on the width either on both sides or only on one side": "Toon berichtbubbels, afhankelijk van de breedte, aan beide zijden of slechts aan één zijde", + "Message bubbles": "Boodschap bellen" } diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 8497ae7164c..a0fa62884fc 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -3110,5 +3110,8 @@ "Inviting...": "Convidando...", "Invite by username": "Convidar por nome de usuário", "Support": "Suporte", - "Original event source": "Fonte do evento original" + "Original event source": "Fonte do evento original", + "Message bubbles": "Bolhas de mensagens", + "Show message bubbles on one side only": "Mostrar bolhas de mensagem em apenas um lado", + "Show message bubbles depending on the width either on both sides or only on one side": "Mostrar bolhas de mensagem dependendo da largura de ambos os lados ou somente de um lado" } diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index da42347b494..9a4c03ba24f 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -3211,5 +3211,8 @@ "Failed to remove some rooms. Try again later": "Не удалось удалить несколько комнат. Попробуйте позже", "%(count)s rooms and 1 space|one": "%(count)s комната и одно пространство", "%(count)s rooms and 1 space|other": "%(count)s комнат и одно пространство", - "Sends the given message as a spoiler": "Отправить данное сообщение под спойлером" + "Sends the given message as a spoiler": "Отправить данное сообщение под спойлером", + "Message bubbles": "Пузырьки сообщений", + "Show message bubbles on one side only": "Показывайте пузырьки с сообщениями только на одной стороне", + "Show message bubbles depending on the width either on both sides or only on one side": "Показывать пузырьки сообщений в зависимости от ширины либо с обеих сторон, либо только с одной стороны" } diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 0ee0c6cbc3a..fe61416a240 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -2080,5 +2080,8 @@ "The call was answered on another device.": "Hovor bol prijatý na inom zariadení.", "The call could not be established": "Hovor nemohol byť realizovaný", "The other party declined the call.": "Druhá strana odmietla hovor.", - "Call Declined": "Hovor odmietnutý" + "Call Declined": "Hovor odmietnutý", + "Show message bubbles on one side only": "Zobrazenie bublín správ len na jednej strane", + "Show message bubbles depending on the width either on both sides or only on one side": "Zobrazenie bublín správ v závislosti od šírky buď na oboch stranách, alebo len na jednej strane", + "Message bubbles": "Bubliny správ" } From 57ffed4eec51d707d268cfa9b550dbfe07348be8 Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 15:48:24 +0200 Subject: [PATCH 06/23] Apply suggestions from code review round 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Šimon Brandner --- src/components/structures/RoomView.tsx | 4 ++-- src/components/views/messages/MImageBody.js | 8 +++----- src/components/views/messages/TextualBody.js | 6 +++--- .../settings/tabs/user/AppearanceUserSettingsTab.tsx | 8 ++++---- src/settings/Layout.ts | 2 +- src/settings/controllers/NewLayoutSwitcherController.ts | 2 +- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 3c164a9049a..303535875e5 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1675,9 +1675,9 @@ export default class RoomView extends React.Component { } // ToDo: Make threshold configurable? if (width < 1280) { - this.setState({singleSideBubbles: false}); + this.setState({ singleSideBubbles: false }); } else { - this.setState({singleSideBubbles: true}); + this.setState({ singleSideBubbles: true }); } } }; diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index c253e0b14ed..2e307033a95 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -356,7 +356,7 @@ export default class MImageBody extends React.Component { /> ); } - return {thumbnail: this.wrapImage(contentUrl, imageElement)}; + return { thumbnail: this.wrapImage(contentUrl, imageElement) }; } infoWidth = this.state.loadedImageDimensions.naturalWidth; infoHeight = this.state.loadedImageDimensions.naturalHeight; @@ -430,7 +430,7 @@ export default class MImageBody extends React.Component {
    ); - return {thumbnail: this.wrapImage(contentUrl, thumbnail), maxWidth: maxWidth}; + return { thumbnail: this.wrapImage(contentUrl, thumbnail), maxWidth: maxWidth }; } // Overidden by MStickerBody @@ -477,9 +477,7 @@ export default class MImageBody extends React.Component { thumbUrl = this._getThumbUrl(); } - const messageContent = this._messageContent(contentUrl, thumbUrl, content); - const thumbnail = messageContent.thumbnail; - const maxWidth = messageContent.maxWidth; + const { thumbnail, maxWidth } = this._messageContent(contentUrl, thumbUrl, content); const fileBody = this.getFileBody(); return diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 0c111a9fa5b..e026e4c1d7c 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -537,7 +537,7 @@ export default class TextualBody extends React.Component { switch (content.msgtype) { case "m.emote": return ( - + + { body } { widgets } { this.props.scBubbleGroupTimestamp } @@ -561,7 +561,7 @@ export default class TextualBody extends React.Component { ); default: // including "m.text" return ( - + { body } { widgets } { this.props.scBubbleGroupTimestamp } diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 64318413488..2fec9e23a78 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -546,10 +546,10 @@ export default class AppearanceUserSettingsTab extends React.Component {_t("Appearance Settings only affect this %(brand)s session.", { brand })}
    - {this.renderThemeSection()} - {SettingsStore.getValue("feature_new_layout_switcher") ? this.renderLayoutSection() : null} - {this.renderFontSection()} - {this.renderAdvancedSection()} + { this.renderThemeSection() } + { SettingsStore.getValue("feature_new_layout_switcher") ? this.renderLayoutSection() : null } + { this.renderFontSection() } + { this.renderAdvancedSection() }
    ); } diff --git a/src/settings/Layout.ts b/src/settings/Layout.ts index d451af96f87..d4e1f06c0ab 100644 --- a/src/settings/Layout.ts +++ b/src/settings/Layout.ts @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; export enum Layout { IRC = "irc", Group = "group", - Bubble = "bubble" + Bubble = "bubble", } /* We need this because multiple components are still using JavaScript */ diff --git a/src/settings/controllers/NewLayoutSwitcherController.ts b/src/settings/controllers/NewLayoutSwitcherController.ts index e5df2e6753f..1c623db7134 100644 --- a/src/settings/controllers/NewLayoutSwitcherController.ts +++ b/src/settings/controllers/NewLayoutSwitcherController.ts @@ -1,5 +1,5 @@ /* -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. +Copyright 2021 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. From 3b25e3180b2b2affa82b1c2847ca44a40e459b46 Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 15:56:27 +0200 Subject: [PATCH 07/23] Leave strings to Weblate Revert "Add some strings that sprinkled away" This reverts commit 4a12d6238b46e99a2e1293098b856ab19ee790aa. --- src/i18n/strings/cs.json | 5 +---- src/i18n/strings/de_DE.json | 8 +------- src/i18n/strings/en_EN.json | 8 +------- src/i18n/strings/es.json | 5 +---- src/i18n/strings/et.json | 5 +---- src/i18n/strings/eu.json | 5 +---- src/i18n/strings/hu.json | 5 +---- src/i18n/strings/nl.json | 5 +---- src/i18n/strings/pt_BR.json | 5 +---- src/i18n/strings/ru.json | 5 +---- src/i18n/strings/sk.json | 5 +---- 11 files changed, 11 insertions(+), 50 deletions(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 20b34c9090d..75472b4d38c 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -3292,8 +3292,5 @@ "See when people join, leave, or are invited to your active room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do vaší aktivní místnosti", "Kick, ban, or invite people to this room, and make you leave": "Vykopnout, vykázat, pozvat lidi do této místnosti nebo odejít", "Kick, ban, or invite people to your active room, and make you leave": "Vykopnout, vykázat, pozvat lidi do vaší aktivní místnosti nebo odejít", - "See when people join, leave, or are invited to this room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do této místnosti", - "Show message bubbles on one side only": "Bubliny zpráv zobrazit pouze na jedné straně", - "Show message bubbles depending on the width either on both sides or only on one side": "Bubliny zpráv zobrazit v závislosti na šířce stránky buď na obou stranách, nebo pouze na jedné straně.", - "Message bubbles": "Bubliny zpráv" + "See when people join, leave, or are invited to this room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do této místnosti" } diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index a73f07a264a..dcc5343af4d 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -3343,11 +3343,5 @@ "Your feedback will help make spaces better. The more detail you can go into, the better.": "Dein Feedback hilfst uns, die Spaces zu verbessern. Je genauer, desto besser.", "If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Durchs Verlassen lädt %(brand)s mit deaktivierten Spaces neu. Danach kannst du Communities und Custom Tags wieder verwenden.", "sends space invaders": "sendet Space Invaders", - "Sends the given message with a space themed effect": "Sendet die Nachricht mit Raumschiffen", - "Message layout": "Nachrichtenlayout", - "Modern": "Modern", - "Message bubbles": "Sprechblasen", - "Explore new ways switching layouts (including a new bubble layout)": "Probiere neue Möglichkeiten zum Wechseln von Layouts aus (inklusive eines neuen Layouts mit Sprechblasen)", - "Show message bubbles on one side only": "Sprechblasen nur auf einer Seite anzeigen", - "Show message bubbles depending on the width either on both sides or only on one side": "Sprechblasen abhänging von der Breite auf beiden Seiten oder nur einer Seite anzeigen" + "Sends the given message with a space themed effect": "Sendet die Nachricht mit Raumschiffen" } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0fe8ca294c7..874dc11bd25 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2988,11 +2988,5 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End", - "Message layout": "Message layout", - "Modern": "Modern", - "Explore new ways switching layouts (including a new bubble layout)": "Explore new ways switching layouts (including a new bubble layout)", - "Show message bubbles on one side only": "Show message bubbles on one side only", - "Show message bubbles depending on the width either on both sides or only on one side": "Show message bubbles depending on the width either on both sides or only on one side", - "Message bubbles": "Message bubbles" + "End": "End" } diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 4353aa40408..60f5d06bec5 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -3315,8 +3315,5 @@ "See when people join, leave, or are invited to your active room": "Ver cuando alguien se una, salga o se le invite a tu sala activa", "Kick, ban, or invite people to this room, and make you leave": "Expulsar, vetar o invitar personas a esta sala, y hacerte salir de ella", "Kick, ban, or invite people to your active room, and make you leave": "Expulsar, vetar o invitar a gente a tu sala activa, o hacerte salir", - "See when people join, leave, or are invited to this room": "Ver cuando alguien se une, sale o se le invita a la sala", - "Show message bubbles on one side only": "mostrar las burbujas de mensajes en un solo lado", - "Message bubbles": "Burbujas de mensajes", - "Show message bubbles depending on the width either on both sides or only on one side": "Mostrar mensajes de burbuja dependiendo del tamaño tanto en ambos lados o solo en un lado" + "See when people join, leave, or are invited to this room": "Ver cuando alguien se une, sale o se le invita a la sala" } diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index f3532bbd069..5e8d744cca2 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -3345,8 +3345,5 @@ "Send and receive voice messages": "Saada ja võta vastu häälsõnumeid", "Your feedback will help make spaces better. The more detail you can go into, the better.": "Sinu tagasiside aitab teha kogukonnakeskuseid paremaks. Mida detailsemalt sa oma arvamust kirjeldad, seda parem.", "If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Kui sa lahkud, siis käivitame %(brand)s uuesti nii, et kogukonnakeskused ei ole kasutusel. Vana tüüpi kogukonnad ja kohandatud sildid saavad jälle olema kasutusel.", - "Message search initialisation failed": "Sõnumite otsingu alustamine ei õnnestunud", - "Show message bubbles on one side only": "Näita sõnumimulle ainult ühel poolel", - "Show message bubbles depending on the width either on both sides or only on one side": "Näita sõnumimullid sõltuvalt laiusest kas mõlemal või ainult ühel küljel", - "Message bubbles": "Sõnumimullid" + "Message search initialisation failed": "Sõnumite otsingu alustamine ei õnnestunud" } diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 23edadf8813..2740ea20796 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -2293,8 +2293,5 @@ "Wrong file type": "Okerreko fitxategi-mota", "Looks good!": "Itxura ona du!", "Search rooms": "Bilatu gelak", - "User menu": "Erabiltzailea-menua", - "Show message bubbles on one side only": "Erakutsi mezu burbuilak alde batetik bakarrik", - "Message bubbles": "Mezu burbuilak", - "Show message bubbles depending on the width either on both sides or only on one side": "Erakutsi mezu burbuilak arabera zabalera bai, bi aldeetatik edo alde batetik bakarrik" + "User menu": "Erabiltzailea-menua" } diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 990f11d5d39..a6e99928668 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -3370,8 +3370,5 @@ "See when people join, leave, or are invited to your active room": "Emberek belépésének, távozásának vagy meghívásának a megjelenítése az aktív szobájában", "Kick, ban, or invite people to your active room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket az aktív szobába és, hogy ön elhagyja a szobát", "See when people join, leave, or are invited to this room": "Emberek belépésének, távozásának vagy meghívásának a megjelenítése ebben a szobában", - "Kick, ban, or invite people to this room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket ebbe a szobába és, hogy ön elhagyja a szobát", - "Show message bubbles on one side only": "Üzenetbuborékok megjelenítése csak az egyik oldalon", - "Message bubbles": "Üzenet buborékok", - "Show message bubbles depending on the width either on both sides or only on one side": "Üzenetbuborékok megjelenítése a szélességtől függően mindkét oldalon vagy csak az egyik oldalon" + "Kick, ban, or invite people to this room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket ebbe a szobába és, hogy ön elhagyja a szobát" } diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index cde8b1317d4..16f74e7b2df 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -3261,8 +3261,5 @@ "See when people join, leave, or are invited to your active room": "Zie wanneer personen deelnemen, vertrekken of worden uitgenodigd in uw actieve gesprek", "Kick, ban, or invite people to your active room, and make you leave": "Verwijder, verban of nodig personen uit voor uw actieve gesprek en uzelf laten vertrekken", "See when people join, leave, or are invited to this room": "Zie wanneer personen deelnemen, vertrekken of worden uitgenodigd voor dit gesprek", - "Kick, ban, or invite people to this room, and make you leave": "Verwijder, verban of verwijder personen uit dit gesprek en uzelf laten vertrekken", - "Show message bubbles on one side only": "Boodschappenbubbels slechts aan één kant tonen", - "Show message bubbles depending on the width either on both sides or only on one side": "Toon berichtbubbels, afhankelijk van de breedte, aan beide zijden of slechts aan één zijde", - "Message bubbles": "Boodschap bellen" + "Kick, ban, or invite people to this room, and make you leave": "Verwijder, verban of verwijder personen uit dit gesprek en uzelf laten vertrekken" } diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index a0fa62884fc..8497ae7164c 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -3110,8 +3110,5 @@ "Inviting...": "Convidando...", "Invite by username": "Convidar por nome de usuário", "Support": "Suporte", - "Original event source": "Fonte do evento original", - "Message bubbles": "Bolhas de mensagens", - "Show message bubbles on one side only": "Mostrar bolhas de mensagem em apenas um lado", - "Show message bubbles depending on the width either on both sides or only on one side": "Mostrar bolhas de mensagem dependendo da largura de ambos os lados ou somente de um lado" + "Original event source": "Fonte do evento original" } diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 9a4c03ba24f..da42347b494 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -3211,8 +3211,5 @@ "Failed to remove some rooms. Try again later": "Не удалось удалить несколько комнат. Попробуйте позже", "%(count)s rooms and 1 space|one": "%(count)s комната и одно пространство", "%(count)s rooms and 1 space|other": "%(count)s комнат и одно пространство", - "Sends the given message as a spoiler": "Отправить данное сообщение под спойлером", - "Message bubbles": "Пузырьки сообщений", - "Show message bubbles on one side only": "Показывайте пузырьки с сообщениями только на одной стороне", - "Show message bubbles depending on the width either on both sides or only on one side": "Показывать пузырьки сообщений в зависимости от ширины либо с обеих сторон, либо только с одной стороны" + "Sends the given message as a spoiler": "Отправить данное сообщение под спойлером" } diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index fe61416a240..0ee0c6cbc3a 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -2080,8 +2080,5 @@ "The call was answered on another device.": "Hovor bol prijatý na inom zariadení.", "The call could not be established": "Hovor nemohol byť realizovaný", "The other party declined the call.": "Druhá strana odmietla hovor.", - "Call Declined": "Hovor odmietnutý", - "Show message bubbles on one side only": "Zobrazenie bublín správ len na jednej strane", - "Show message bubbles depending on the width either on both sides or only on one side": "Zobrazenie bublín správ v závislosti od šírky buď na oboch stranách, alebo len na jednej strane", - "Message bubbles": "Bubliny správ" + "Call Declined": "Hovor odmietnutý" } From 48d38b3549f4f535ab6a2e19272e31f94aa3dc82 Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 15:57:30 +0200 Subject: [PATCH 08/23] Regenerate base strings --- src/i18n/strings/en_EN.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 874dc11bd25..078b9b3894a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -809,6 +809,7 @@ "Offline encrypted messaging using dehydrated devices": "Offline encrypted messaging using dehydrated devices", "Enable advanced debugging for the room list": "Enable advanced debugging for the room list", "Show info about bridges in room settings": "Show info about bridges in room settings", + "Explore new ways switching layouts (including a new bubble layout)": "Explore new ways switching layouts (including a new bubble layout)", "Font size": "Font size", "Use custom size": "Use custom size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", @@ -862,6 +863,8 @@ "How fast should messages be downloaded.": "How fast should messages be downloaded.", "Manually verify all remote sessions": "Manually verify all remote sessions", "IRC display name width": "IRC display name width", + "Show message bubbles on one side only": "Show message bubbles on one side only", + "Show message bubbles depending on the width either on both sides or only on one side": "Show message bubbles depending on the width either on both sides or only on one side", "Show chat effects (animations when receiving e.g. confetti)": "Show chat effects (animations when receiving e.g. confetti)", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", @@ -1221,6 +1224,9 @@ "Custom theme URL": "Custom theme URL", "Add theme": "Add theme", "Theme": "Theme", + "Message layout": "Message layout", + "Modern": "Modern", + "Message bubbles": "Message bubbles", "Hide advanced": "Hide advanced", "Show advanced": "Show advanced", "Set the name of a font installed on your system & %(brand)s will attempt to use it.": "Set the name of a font installed on your system & %(brand)s will attempt to use it.", From 5248c05016773fdc5e72a542ebb6f0aff61b84cc Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 16:04:55 +0200 Subject: [PATCH 09/23] Change div back to li --- src/components/views/elements/EventListSummary.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index 3ec16ce6f12..e0e78d0f64e 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -67,9 +67,9 @@ const EventListSummary: React.FC = ({ // If we are only given few events then just pass them through if (events.length < threshold) { return ( -
    +
  • { children } -
  • + ); } From 540fca4dd9629a7ba03fdc50ba9508e11482b5d0 Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 16:29:03 +0200 Subject: [PATCH 10/23] Get rid of some prefixes (no css classes yet) --- src/components/views/messages/MAudioBody.js | 2 +- src/components/views/messages/MFileBody.js | 12 ++++++------ src/components/views/messages/MImageBody.js | 12 ++++++------ src/components/views/messages/MStickerBody.js | 2 +- src/components/views/messages/MVideoBody.tsx | 8 ++++---- src/components/views/messages/MessageEvent.js | 12 ++++++------ src/components/views/messages/TextualBody.js | 8 ++++---- src/components/views/rooms/EventTile.tsx | 18 ++++++++++-------- 8 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js index 1292d6d3039..7ea88f547bf 100644 --- a/src/components/views/messages/MAudioBody.js +++ b/src/components/views/messages/MAudioBody.js @@ -28,7 +28,7 @@ import {mediaFromContent} from "../../../customisations/Media"; @replaceableComponent("views.messages.MAudioBody") export default class MAudioBody extends React.Component { static propTypes = { - scBubbleGroupTimestamp: PropTypes.object, + bubbleTimestamp: PropTypes.object, }; constructor(props) { diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index c104fadab8e..2adab0c8a85 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -102,7 +102,7 @@ export default class MFileBody extends React.Component { tileShape: PropTypes.string, /* whether or not to show the default placeholder for the file. Defaults to true. */ showGenericPlaceholder: PropTypes.bool, - scBubbleGroupTimestamp: PropTypes.object, + bubbleTimestamp: PropTypes.object, }; static defaultProps = { @@ -215,7 +215,7 @@ export default class MFileBody extends React.Component { { _t("Decrypt %(text)s", { text: text }) }
    - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp } ); } @@ -258,7 +258,7 @@ export default class MFileBody extends React.Component { ref={this._iframe} sandbox="allow-scripts allow-downloads allow-downloads-without-user-activation" />
    - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp } ); } else if (contentUrl) { @@ -321,7 +321,7 @@ export default class MFileBody extends React.Component { { content.info && content.info.size ? filesize(content.info.size) : "" }
    - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp }
    ); } else { @@ -334,7 +334,7 @@ export default class MFileBody extends React.Component { { _t("Download %(text)s", { text: text }) } - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp }
    ); } @@ -343,7 +343,7 @@ export default class MFileBody extends React.Component { return {placeholder} { _t("Invalid file%(extra)s", { extra: extra }) } - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp } ; } } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 2e307033a95..53cfacbb8c6 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -45,9 +45,9 @@ export default class MImageBody extends React.Component { /* the permalinkCreator */ permalinkCreator: PropTypes.object, - scBubble: PropTypes.bool, - scBubbleGroupTimestamp: PropTypes.object, - scBubbleActionBar: PropTypes.object, + bubbleEnabled: PropTypes.bool, + bubbleTimestamp: PropTypes.object, + bubbleActionBar: PropTypes.object, }; static contextType = MatrixClientContext; @@ -464,7 +464,7 @@ export default class MImageBody extends React.Component { { _t("Error decrypting image") } - { this.props.scBubbleActionBar } + { this.props.bubbleActionBar } ); } @@ -480,10 +480,10 @@ export default class MImageBody extends React.Component { const { thumbnail, maxWidth } = this._messageContent(contentUrl, thumbUrl, content); const fileBody = this.getFileBody(); - return + return { thumbnail } { fileBody } - { this.props.scBubbleActionBar } + { this.props.bubbleActionBar } ; } } diff --git a/src/components/views/messages/MStickerBody.js b/src/components/views/messages/MStickerBody.js index 543c9ed19bf..0100accf56d 100644 --- a/src/components/views/messages/MStickerBody.js +++ b/src/components/views/messages/MStickerBody.js @@ -37,7 +37,7 @@ export default class MStickerBody extends MImageBody { onClick = this.onClick; } const wrapper =
    { children }
    ; - return [wrapper, this.props.scBubbleGroupTimestamp]; + return [wrapper, this.props.bubbleTimestamp]; } // Placeholder to show in place of the sticker image if diff --git a/src/components/views/messages/MVideoBody.tsx b/src/components/views/messages/MVideoBody.tsx index d5f79766ae8..800dca2dc06 100644 --- a/src/components/views/messages/MVideoBody.tsx +++ b/src/components/views/messages/MVideoBody.tsx @@ -30,7 +30,7 @@ interface IProps { /* called when the video has loaded */ onHeightChanged: () => void; - scBubbleActionBar: any; + bubbleActionBar: any; } interface IState { @@ -195,7 +195,7 @@ export default class MVideoBody extends React.PureComponent { { _t("Error decrypting video") } - { this.props.scBubbleActionBar } + { this.props.bubbleActionBar } ); } @@ -210,7 +210,7 @@ export default class MVideoBody extends React.PureComponent {
    - { this.props.scBubbleActionBar } + { this.props.bubbleActionBar }
    ); } @@ -253,7 +253,7 @@ export default class MVideoBody extends React.PureComponent {
    - { this.props.scBubbleActionBar } + { this.props.bubbleActionBar }
    ); } diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js index 026a4de94cd..a0abcdfe3d3 100644 --- a/src/components/views/messages/MessageEvent.js +++ b/src/components/views/messages/MessageEvent.js @@ -50,9 +50,9 @@ export default class MessageEvent extends React.Component { /* the permalinkCreator */ permalinkCreator: PropTypes.object, - scBubble: PropTypes.bool, - scBubbleGroupTimestamp: PropTypes.object, - scBubbleActionBar: PropTypes.object, + bubbleEnabled: PropTypes.bool, + bubbleTimestamp: PropTypes.object, + bubbleActionBar: PropTypes.object, }; constructor(props) { @@ -130,9 +130,9 @@ export default class MessageEvent extends React.Component { onHeightChanged={this.props.onHeightChanged} onMessageAllowed={this.onTileUpdate} permalinkCreator={this.props.permalinkCreator} - scBubble={this.props.scBubble} - scBubbleGroupTimestamp={this.props.scBubbleGroupTimestamp} - scBubbleActionBar={this.props.scBubbleActionBar} + bubbleEnabled={this.props.bubbleEnabled} + bubbleTimestamp={this.props.bubbleTimestamp} + bubbleActionBar={this.props.bubbleActionBar} />; } } diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index e026e4c1d7c..df8112dd371 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -59,7 +59,7 @@ export default class TextualBody extends React.Component { /* the shape of the tile, used */ tileShape: PropTypes.string, - scBubbleGroupTimestamp: PropTypes.object, + bubbleTimestamp: PropTypes.object, }; constructor(props) { @@ -548,7 +548,7 @@ export default class TextualBody extends React.Component {   { body } { widgets } - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp }
    ); case "m.notice": @@ -556,7 +556,7 @@ export default class TextualBody extends React.Component { { body } { widgets } - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp } ); default: // including "m.text" @@ -564,7 +564,7 @@ export default class TextualBody extends React.Component { { body } { widgets } - { this.props.scBubbleGroupTimestamp } + { this.props.bubbleTimestamp } ); } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index fad2398c4c2..7c59141fb1e 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -893,7 +893,7 @@ export default class EventTile extends React.Component { const client = MatrixClientPeg.get(); const me = client && client.getUserId(); - const scBubbleEnabled = this.props.layout == Layout.Bubble + const bubbleEnabled = this.props.layout == Layout.Bubble && this.props.tileShape !== 'reply_preview' && this.props.tileShape !== 'reply' && this.props.tileShape !== 'notif' && this.props.tileShape !== 'file_grid'; const sentByMe = me === this.props.mxEvent.getSender(); @@ -922,7 +922,7 @@ export default class EventTile extends React.Component { mx_EventTile_unknown: !isBubbleMessage && this.state.verified === E2E_STATE.UNKNOWN, mx_EventTile_bad: isEncryptionFailure, mx_EventTile_emote: msgtype === 'm.emote', - sc_EventTile_bubbleContainer: scBubbleEnabled, + sc_EventTile_bubbleContainer: bubbleEnabled, }); // If the tile is in the Sending state, don't speak the message. @@ -944,7 +944,7 @@ export default class EventTile extends React.Component { let avatarSize; let needsSenderProfile; - if (!isInfoMessage && scBubbleEnabled && showRight) { + if (!isInfoMessage && bubbleEnabled && showRight) { avatarSize = 0; needsSenderProfile = false; } else if (this.props.tileShape === "notif") { @@ -1012,7 +1012,7 @@ export default class EventTile extends React.Component { /> : undefined; const showTimestamp = this.props.mxEvent.getTs() && - (scBubbleEnabled || + (bubbleEnabled || this.props.alwaysShowTimestamps || this.props.last || this.state.hover || this.state.actionBarFocused); const timestamp = showTimestamp ? : null; @@ -1072,6 +1072,8 @@ export default class EventTile extends React.Component { { timestamp }
    ; + const bubbleTimestamp = <>{placeholderTimestamp}{linkedTimestamp}; + const useIRCLayout = this.props.layout == Layout.IRC; const groupTimestamp = !useIRCLayout ? linkedTimestamp : null; const ircTimestamp = useIRCLayout ? linkedTimestamp : null; @@ -1201,7 +1203,7 @@ export default class EventTile extends React.Component { this.props.alwaysShowTimestamps || this.state.hover, ); - if (scBubbleEnabled) { + if (bubbleEnabled) { const infoBubble = isInfoMessage || isBubbleMessage; const mediaBodyTypes = ['m.image', /* 'm.file', */ /* 'm.audio', */ 'm.video']; @@ -1283,9 +1285,9 @@ export default class EventTile extends React.Component { highlightLink={this.props.highlightLink} showUrlPreview={this.props.showUrlPreview} onHeightChanged={this.props.onHeightChanged} - scBubble={true} - scBubbleActionBar={mediaBody ? actionBar : null} - scBubbleGroupTimestamp={<>{placeholderTimestamp}{groupTimestamp}} + bubbleEnabled={true} + bubbleActionBar={mediaBody ? actionBar : null} + bubbleTimestamp={bubbleTimestamp} /> { !mediaBody ? actionBar : null } From 050a5a2625c4bfc15594961f941926c85131059c Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 17:00:28 +0200 Subject: [PATCH 11/23] Apply suggestions from code review round 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Šimon Brandner --- src/components/views/elements/EventListSummary.tsx | 2 +- src/components/views/rooms/MessageComposer.tsx | 4 +++- .../tabs/user/AppearanceUserSettingsTab.tsx | 14 ++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index e0e78d0f64e..6b2795a9e0c 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -81,7 +81,7 @@ const EventListSummary: React.FC = ({ ; } else { const avatars = summaryMembers.map((m) => ); - if (layout == Layout.Bubble) { + if (layout === Layout.Bubble) { body = (
    { const msgComposerClassNames = classNames( "mx_MessageComposer", { - // IRC layout has nothing for message composer so use group layout stuff + // When IRC layout gets something for the message composer we can use the following // "mx_IRCLayout": this.props.layout == Layout.IRC, // "mx_GroupLayout": this.props.layout == Layout.Group, + + // IRC layout has nothing for message composer so use group layout stuff "mx_GroupLayout": this.props.layout == Layout.IRC || this.props.layout == Layout.Group, "sc_BubbleLayout": this.props.layout == Layout.Bubble, }, diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 2fec9e23a78..74a5e288f24 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -247,9 +247,7 @@ export default class AppearanceUserSettingsTab extends React.Component { return
    - {_t("Message layout")} + { _t("Message layout") }
    - {"IRC"} + { "IRC" }
    @@ -487,7 +485,7 @@ export default class AppearanceUserSettingsTab extends React.Component : null } { SettingsStore.getValue("feature_new_layout_switcher") ? @@ -495,8 +493,8 @@ export default class AppearanceUserSettingsTab extends React.Component this.setState({adaptiveSideBubbles: checked})} - disabled={!(this.state.layout == Layout.Bubble)} + onChange={(checked) => this.setState({ adaptiveSideBubbles: checked })} + disabled={this.state.layout !== Layout.Bubble} /> : null } { !SettingsStore.getValue("feature_new_layout_switcher") ? From 44f82b01c8c25300488555867688fee42bf6befe Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 17:35:11 +0200 Subject: [PATCH 12/23] Make singleSideBubbles optional in EventTile --- src/components/views/rooms/EventTile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 7c59141fb1e..08ac1d63331 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -264,7 +264,7 @@ interface IProps { layout: Layout; // whether to use single side bubbles - singleSideBubbles: boolean; + singleSideBubbles?: boolean; // whether or not to show flair at all enableFlair?: boolean; From 0f43c831b88d17fe95b27a5e5e28ca517003080c Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 18:13:56 +0200 Subject: [PATCH 13/23] Appease linter --- res/css/views/rooms/_BubbleLayout.scss | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/res/css/views/rooms/_BubbleLayout.scss b/res/css/views/rooms/_BubbleLayout.scss index fae356a6276..d1974dbe6a0 100644 --- a/res/css/views/rooms/_BubbleLayout.scss +++ b/res/css/views/rooms/_BubbleLayout.scss @@ -91,18 +91,25 @@ $left-gutter: 56px; position: relative; // Consistence with other bubbles + .mx_EventTile { padding-top: 0; } + .sc_EventTile_bubbleLine_info { margin-top: 6px; + + // ToDo: Find better way to not crash bubble with toggle on small width screens + // padding-left: 110px; + margin-right: 0; + padding-left: 0; } .mx_EventTile_line { padding-top: 3px; padding-bottom: 3px; - } - + } + .sc_EventTile_bubbleArea_info { // For toggle max-width: 72rem; @@ -168,7 +175,7 @@ $left-gutter: 56px; .mx_EventTile_bubbleContainer .mx_EventTile_msgOption { grid-column: unset; // show read avatara like always - } + } .mx_EventTile_readAvatars { // SC-TODO align left below msg area @@ -206,7 +213,7 @@ $left-gutter: 56px; .mx_EditMessageComposer { margin: 0; padding: 0; - + .mx_EditMessageComposer_buttons { position: unset; margin: 0; @@ -216,7 +223,7 @@ $left-gutter: 56px; max-width: 100%; background: transparent; flex-wrap: wrap; - + .mx_AccessibleButton { padding: 5px 0; width: 11em; @@ -224,7 +231,7 @@ $left-gutter: 56px; } } } - + .mx_BasicMessageComposer { max-width: 100%; } @@ -266,7 +273,7 @@ $left-gutter: 56px; .mx_MFileBody_download { display: inline; } - + .mx_MessageTimestamp { position: absolute; @@ -381,13 +388,6 @@ $left-gutter: 56px; line-height: unset; } - .mx_EventListSummary .sc_EventTile_bubbleLine_info { - // ToDo: Find better way to not crash bubble with toggle on small width screens - // padding-left: 110px; - padding-left: 0; - margin-right: 0; - } - .sc_EventTile_bubbleContainer { > .mx_EventTile_avatar { top: 9px; @@ -530,7 +530,7 @@ $left-gutter: 56px; .mx_ReactionsRow_addReactionButton { margin-left: unset; - margin-right: 6px; + margin-right: 6px; } } } @@ -543,7 +543,7 @@ $left-gutter: 56px; // right: -8px; // } } - + .sc_EventTile_bubble_self { &:not(.sc_EventTile_bubble_media) { background-color: $message-bubble-background-self; @@ -597,7 +597,7 @@ $left-gutter: 56px; display: inline; position: unset; - &:after { + &::after { content: "\00a0"; } } @@ -632,24 +632,24 @@ $left-gutter: 56px; &.sc_EventTile_bubble_right { margin-left: auto; - + > * { margin-left: auto; } - + .mx_MImageBody_thumbnail { left: unset; right: 0; } } - + // maybe: // & + .mx_ReactionsRow { // margin-top: 0; // margin-bottom: 0; // } } - + .sc_EventTile_bubble_sticker { .mx_MImageBody_thumbnail { border: none; @@ -682,7 +682,7 @@ $left-gutter: 56px; border-radius: 8px; display: block; } - + video.mx_MVideoBody { opacity: 0.7; } @@ -696,7 +696,7 @@ $left-gutter: 56px; width: calc(100% - 16px); max-width: unset; } - + .sc_EventTile_bubbleArea_info { width: 80%; max-width: 72rem; From 0b61faf42e854b9913eb29aeb46b64caa47bcb48 Mon Sep 17 00:00:00 2001 From: su-ex Date: Fri, 11 Jun 2021 19:00:52 +0200 Subject: [PATCH 14/23] Switch prefix (sc_ --> mx_) --- res/css/views/rooms/_BubbleLayout.scss | 90 +++++++++---------- res/css/views/rooms/_EventTile.scss | 2 +- src/components/structures/RoomView.tsx | 4 +- .../views/elements/EventListSummary.tsx | 6 +- .../views/elements/EventTilePreview.tsx | 2 +- src/components/views/messages/MVideoBody.tsx | 2 +- src/components/views/rooms/EventTile.tsx | 64 ++++++------- .../views/rooms/MessageComposer.tsx | 2 +- 8 files changed, 86 insertions(+), 86 deletions(-) diff --git a/res/css/views/rooms/_BubbleLayout.scss b/res/css/views/rooms/_BubbleLayout.scss index d1974dbe6a0..0a360b4fc63 100644 --- a/res/css/views/rooms/_BubbleLayout.scss +++ b/res/css/views/rooms/_BubbleLayout.scss @@ -19,7 +19,7 @@ limitations under the License. $left-gutter: 56px; -.sc_BubbleLayout { +.mx_BubbleLayout { // ---- Overrides ---- .mx_RoomView_MessageList { @@ -96,7 +96,7 @@ $left-gutter: 56px; padding-top: 0; } - .sc_EventTile_bubbleLine_info { + .mx_EventTile_bubbleLine_info { margin-top: 6px; // ToDo: Find better way to not crash bubble with toggle on small width screens @@ -110,7 +110,7 @@ $left-gutter: 56px; padding-bottom: 3px; } - .sc_EventTile_bubbleArea_info { + .mx_EventTile_bubbleArea_info { // For toggle max-width: 72rem; } @@ -166,14 +166,14 @@ $left-gutter: 56px; // stop read avatars overflowing width: 100%; - &.sc_readReceipts_empty { + &.mx_readReceipts_empty { height: 0; margin-top: 0; margin-bottom: 0; } } - .mx_EventTile_bubbleContainer .mx_EventTile_msgOption { + .mx_EventTile_bigInfoContainer .mx_EventTile_msgOption { grid-column: unset; // show read avatara like always } @@ -307,7 +307,7 @@ $left-gutter: 56px; display: none; } - .sc_PlaceholderTimestamp { + .mx_PlaceholderTimestamp { display: none; } @@ -361,11 +361,11 @@ $left-gutter: 56px; text-overflow: ellipsis; } - .sc_PlaceholderTimestamp { + .mx_PlaceholderTimestamp { display: none; } - .sc_LinkedTimestamp { + .mx_LinkedTimestamp { margin-left: 1rem; .mx_MessageTimestamp { @@ -380,15 +380,15 @@ $left-gutter: 56px; // ---- Bubble specific ---- - .mx_EventTile > .sc_EventTile_bubbleLine_info { + .mx_EventTile > .mx_EventTile_bubbleLine_info { padding-left: 0 !important; } - .mx_EventTile .sc_EventTile_bubbleLine_info { + .mx_EventTile .mx_EventTile_bubbleLine_info { line-height: unset; } - .sc_EventTile_bubbleContainer { + .mx_EventTile_bubbleContainer { > .mx_EventTile_avatar { top: 9px; } @@ -398,17 +398,17 @@ $left-gutter: 56px; display: none !important; } - // .sc_EventTile_bubbleLine { + // .mx_EventTile_bubbleLine { // .mx_EventTile_e2eIcon { // left: 16px; - // .sc_EventTile_bubbleTailLeftContainer & { + // .mx_EventTile_bubbleTailLeftContainer & { // top: 35px; // } // } // } - .sc_EventTile_bubbleArea { + .mx_EventTile_bubbleArea { // No full width for both-side bubbles only width: 84%; max-width: 88rem; @@ -416,13 +416,13 @@ $left-gutter: 56px; margin-bottom: 0; } - .sc_EventTile_bubbleArea_left { + .mx_EventTile_bubbleArea_left { margin-left: 0px; margin-right: auto; text-align: left; } - .sc_EventTile_bubble { + .mx_EventTile_bubble { background-color: $message-bubble-background; padding: 7px 10px; border-radius: 8px; @@ -438,7 +438,7 @@ $left-gutter: 56px; // Don't inherit bubbleArea alignment text-align: start; - // &.sc_EventTile_bubble_tail::before { + // &.mx_EventTile_bubble_tail::before { // content: ''; // border: 16px solid transparent; // border-top-color: $message-bubble-background; @@ -463,7 +463,7 @@ $left-gutter: 56px; margin-top: 3px !important; } - .sc_PlaceholderTimestamp { + .mx_PlaceholderTimestamp { display: inline-flex; margin-inline-start: .7em; opacity: 0; @@ -484,7 +484,7 @@ $left-gutter: 56px; } } - .sc_LinkedTimestamp { + .mx_LinkedTimestamp { position: absolute; inset-block-end: 4px; inset-inline-end: 10px; @@ -510,16 +510,16 @@ $left-gutter: 56px; } } - .sc_EventTile_bubble_left { + .mx_EventTile_bubble_left { margin-left: 0px; margin-right: auto; - // &.sc_EventTile_bubble_tail::before { + // &.mx_EventTile_bubble_tail::before { // left: -8px; // } } - .sc_EventTile_bubbleArea_right { + .mx_EventTile_bubbleArea_right { margin-right: 16px; margin-left: auto; text-align: right; @@ -535,20 +535,20 @@ $left-gutter: 56px; } } - .sc_EventTile_bubble_right { + .mx_EventTile_bubble_right { margin-right: 0px; margin-left: auto; - // &.sc_EventTile_bubble_tail::before { + // &.mx_EventTile_bubble_tail::before { // right: -8px; // } } - .sc_EventTile_bubble_self { - &:not(.sc_EventTile_bubble_media) { + .mx_EventTile_bubble_self { + &:not(.mx_EventTile_bubble_media) { background-color: $message-bubble-background-self; - // &.sc_EventTile_bubble_tail::before { + // &.mx_EventTile_bubble_tail::before { // border-top-color: $message-bubble-background-self; // } } @@ -558,21 +558,21 @@ $left-gutter: 56px; } } - .sc_EventTile_bubbleArea_center { + .mx_EventTile_bubbleArea_center { margin-left: auto; margin-right: auto; } - .sc_EventTile_bubble_center { + .mx_EventTile_bubble_center { margin-left: auto; margin-right: auto; } - .sc_EventTile_bubbleArea_info { + .mx_EventTile_bubbleArea_info { width: 76%; } - .sc_EventTile_bubble_info { + .mx_EventTile_bubble_info { text-align: center; background: transparent; box-shadow: inset 0px 0px 0px 1px rgba($roomtopic-color, 0.15); @@ -622,15 +622,15 @@ $left-gutter: 56px; } } - .sc_EventTile_bubble_notice { + .mx_EventTile_bubble_notice { opacity: 0.6; } - .sc_EventTile_bubble_media { + .mx_EventTile_bubble_media { max-width: 600px; position: relative; - &.sc_EventTile_bubble_right { + &.mx_EventTile_bubble_right { margin-left: auto; > * { @@ -650,22 +650,22 @@ $left-gutter: 56px; // } } - .sc_EventTile_bubble_sticker { + .mx_EventTile_bubble_sticker { .mx_MImageBody_thumbnail { border: none; } } .mx_EventTile_selected { - .sc_EventTile_bubble { + .mx_EventTile_bubble { background: $message-bubble-background-selected; - // &.sc_EventTile_bubble_tail::before { + // &.mx_EventTile_bubble_tail::before { // border-top-color: $message-bubble-background-selected; // } } - .sc_EventTile_bubble_media { + .mx_EventTile_bubble_media { > .mx_MImageBody { .mx_MImageBody_thumbnail_container { background: $accent-color; @@ -677,7 +677,7 @@ $left-gutter: 56px; } > span.mx_MVideoBody { - .sc_MVideoBody_video_container { + .mx_MVideoBody_video_container { background: $accent-color; border-radius: 8px; display: block; @@ -691,22 +691,22 @@ $left-gutter: 56px; } // single side bubbles overrides - &.sc_BubbleLayout_singleSide { - .sc_EventTile_bubbleArea { + &.mx_BubbleLayout_singleSide { + .mx_EventTile_bubbleArea { width: calc(100% - 16px); max-width: unset; } - .sc_EventTile_bubbleArea_info { + .mx_EventTile_bubbleArea_info { width: 80%; max-width: 72rem; } - .sc_EventTile_bubbleLine { + .mx_EventTile_bubbleLine { max-width: 104rem; } - .sc_EventTile_bubbleLine_info, .mx_DateSeparator, .mx_EventListSummary { + .mx_EventTile_bubbleLine_info, .mx_DateSeparator, .mx_EventListSummary { max-width: calc($left-gutter + 104rem); } } @@ -715,7 +715,7 @@ $left-gutter: 56px; /* Compact layout overrides */ // .mx_MatrixChat_useCompactLayout { -// .sc_BubbleLayout { +// .mx_BubbleLayout { // // nothing (yet?) // // ToDo: figure out if there is a useful way to make it more compact // // maybe reduce text line-height and some paddings and margins diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index c8b4138f27f..d8700689b76 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -115,7 +115,7 @@ $left-gutter: 64px; } } -.mx_EventTile_bubbleContainer { +.mx_EventTile_bigInfoContainer { display: grid; grid-template-columns: 1fr 100px; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 303535875e5..d48680b1a57 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2024,8 +2024,8 @@ export default class RoomView extends React.Component { const layout = { "mx_IRCLayout": this.state.layout == Layout.IRC, "mx_GroupLayout": this.state.layout == Layout.Group, - "sc_BubbleLayout": this.state.layout == Layout.Bubble, - "sc_BubbleLayout_singleSide": this.state.layout == Layout.Bubble && this.state.singleSideBubbles, + "mx_BubbleLayout": this.state.layout == Layout.Bubble, + "mx_BubbleLayout_singleSide": this.state.layout == Layout.Bubble && this.state.singleSideBubbles, }; // if we have search results, we keep the messagepanel (so that it preserves its diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index 6b2795a9e0c..001d1e1649a 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -83,11 +83,11 @@ const EventListSummary: React.FC = ({ const avatars = summaryMembers.map((m) => ); if (layout === Layout.Bubble) { body = ( -
    +
    -
    +
    { avatars } diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index e4849db6009..07a82c57540 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -121,7 +121,7 @@ export default class EventTilePreview extends React.Component { const className = classnames(this.props.className, { "mx_IRCLayout": this.props.layout == Layout.IRC, "mx_GroupLayout": this.props.layout == Layout.Group, - "sc_BubbleLayout": this.props.layout == Layout.Bubble, + "mx_BubbleLayout": this.props.layout == Layout.Bubble, }); return
    diff --git a/src/components/views/messages/MVideoBody.tsx b/src/components/views/messages/MVideoBody.tsx index 800dca2dc06..4d71bdb35c7 100644 --- a/src/components/views/messages/MVideoBody.tsx +++ b/src/components/views/messages/MVideoBody.tsx @@ -235,7 +235,7 @@ export default class MVideoBody extends React.PureComponent { } return ( - +