From 56456b84e890e19d9a883e007a94d2f36fd0654f Mon Sep 17 00:00:00 2001 From: menturion Date: Wed, 16 Dec 2020 18:16:15 +0100 Subject: [PATCH 01/62] Call "MatrixClientPeg.get()" only once in method "findOverrideMuteRule" --- src/RoomNotifs.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/RoomNotifs.js b/src/RoomNotifs.js index a86c521ac4..600655f635 100644 --- a/src/RoomNotifs.js +++ b/src/RoomNotifs.js @@ -202,12 +202,13 @@ function setRoomNotifsStateUnmuted(roomId, newState) { } function findOverrideMuteRule(roomId) { - if (!MatrixClientPeg.get().pushRules || - !MatrixClientPeg.get().pushRules['global'] || - !MatrixClientPeg.get().pushRules['global'].override) { + const cli = MatrixClientPeg.get(); + if (!cli.pushRules || + !cli.pushRules['global'] || + !cli.pushRules['global'].override) { return null; } - for (const rule of MatrixClientPeg.get().pushRules['global'].override) { + for (const rule of cli.pushRules['global'].override) { if (isRuleForRoom(roomId, rule)) { if (isMuteRule(rule) && rule.enabled) { return rule; From eae3c1c49660ea75c19157109e6e3e61722cebe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 26 Dec 2020 08:32:51 +0100 Subject: [PATCH 02/62] Get screen-sharing working, somehow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../_DesktopCapturerSourcePicker.scss | 73 ++++++++++++ src/CallHandler.tsx | 25 +++- .../elements/DesktopCapturerSourcePicker.tsx | 109 ++++++++++++++++++ 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 res/css/views/elements/_DesktopCapturerSourcePicker.scss create mode 100644 src/components/views/elements/DesktopCapturerSourcePicker.tsx diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss new file mode 100644 index 0000000000..c23fbdf6ac --- /dev/null +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -0,0 +1,73 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_streamSelectorDialog { + -ms-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin: 0 auto; + padding-right: 80px; +} +.desktop-capturer-selection-scroller { + width: 100%; + max-height: 100vh; + overflow-y: auto; +} +.desktop-capturer-selection-list { + max-width: calc(100% - 100px); + margin: 0 50px 0 50px; + padding: 0; + display: flex; + flex-wrap: wrap; + list-style: none; + overflow: hidden; + justify-content: center; +} +.desktop-capturer-selection-item { + display: flex; + margin: 4px; +} +.desktop-capturer-selection-button { + display: flex; + flex-direction: column; + align-items: stretch; + width: 145px; + margin: 0; + border: 0; + border-radius: 4px; + padding: 4px; + background: #20262b; + color: #ffffff; + text-align: left; + transition: background-color .15s, box-shadow .15s; +} +.desktop-capturer-selection-button:hover, +.desktop-capturer-selection-button:focus { + background: #363c43; +} +.desktop-capturer-selection-thumbnail { + width: 100%; + height: 81px; + object-fit: cover; +} +.desktop-capturer-selection-name { + margin: 6px 0 6px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index 504dae5c84..83af34c165 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -82,6 +82,13 @@ import CountlyAnalytics from "./CountlyAnalytics"; import {UIFeature} from "./settings/UIFeature"; import { CallError } from "matrix-js-sdk/src/webrtc/call"; import { logger } from 'matrix-js-sdk/src/logger'; +import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker" + +export interface ElectronDesktopCapturerSource { + display_id: string; + id: string; + name: string; +} enum AudioID { Ring = 'ringAudio', @@ -474,7 +481,23 @@ export default class CallHandler { }); return; } - call.placeScreenSharingCall(remoteElement, localElement); + + const selectDesktopCapturerSource = async ( + sources: Array, + ): Promise => { + console.log(DesktopCapturerSourcePicker); + const params = { + sources: sources, + }; + + const {finished} = Modal.createDialog(DesktopCapturerSourcePicker, params); + + const [source] = await finished; + console.log("Dialog closed with", source); + return source; + }; + + call.placeScreenSharingCall(remoteElement, localElement, selectDesktopCapturerSource); } else { console.error("Unknown conf call type: %s", type); } diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx new file mode 100644 index 0000000000..a1d57884e1 --- /dev/null +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -0,0 +1,109 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { _td } from '../../../languageHandler'; +import BaseDialog from "..//dialogs/BaseDialog" + +export interface DesktopCapturerSourceIProps { + source: ElectronDesktopCapturerSource, + onSelect(source: ElectronDesktopCapturerSource): void, +} + +export class DesktopCapturerSource extends React.Component { + constructor(props) { + super(props); + } + + onClick = (ev) => { + //ev.stopPropagation(); + this.props.onSelect(this.props.source); + } + + render() { + return ( + + ); + } +} + +export interface ElectronDesktopCapturerSource { + display_id: string; + id: string; + name: string; + thumbnail, + appIcon, +} + + +export interface DesktopCapturerSourcePickerIProps { + sources: Array; + onFinished(source: ElectronDesktopCapturerSource): void, +} + +// TODO: Figure out a way to update sources for live preview + +export default class DesktopCapturerSourcePicker extends React.Component { + constructor(props) { + super(props); + } + + onSelect = (source) => { + this.props.onFinished(source); + } + + render() { + const screens = this.props.sources + .filter((source) => { + return source.id.startsWith("screen"); + }) + .map((source) => { + return ; + }); + + const windows = this.props.sources + .filter((source) => { + return source.id.startsWith("window"); + }) + .map((source) => { + return ; + }); + + return ( + +

{_td("Screens")}

+
+ { screens } +
+

{_td("Windows")}

+
+ { windows } +
+
+ ); + } +} From 1dc1bc68dbab8e0cd193167a99ee9de7b149fdcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 26 Dec 2020 08:40:58 +0100 Subject: [PATCH 03/62] Clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/CallHandler.tsx | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index 83af34c165..3958420329 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -482,22 +482,14 @@ export default class CallHandler { return; } - const selectDesktopCapturerSource = async ( - sources: Array, - ): Promise => { - console.log(DesktopCapturerSourcePicker); - const params = { - sources: sources, - }; - - const {finished} = Modal.createDialog(DesktopCapturerSourcePicker, params); - - const [source] = await finished; - console.log("Dialog closed with", source); - return source; - }; - - call.placeScreenSharingCall(remoteElement, localElement, selectDesktopCapturerSource); + call.placeScreenSharingCall( + remoteElement, + localElement, + async (sources: Array) : Promise => { + const {finished} = Modal.createDialog(DesktopCapturerSourcePicker, {sources}); + const [source] = await finished; + return source; + }); } else { console.error("Unknown conf call type: %s", type); } From 675ca58eef8e2032bf0937655de1cd760f9f7fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 26 Dec 2020 16:56:25 +0100 Subject: [PATCH 04/62] Improve UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/_components.scss | 1 + .../_DesktopCapturerSourcePicker.scss | 73 ++++++----- .../elements/DesktopCapturerSourcePicker.tsx | 118 ++++++++++++------ 3 files changed, 123 insertions(+), 69 deletions(-) diff --git a/res/css/_components.scss b/res/css/_components.scss index d8bc238db5..d2000b0e23 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -106,6 +106,7 @@ @import "./views/elements/_AddressTile.scss"; @import "./views/elements/_DesktopBuildsNotice.scss"; @import "./views/elements/_DirectorySearchBox.scss"; +@import "./views/elements/_DesktopCapturerSourcePicker.scss"; @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; @import "./views/elements/_ErrorBoundary.scss"; diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss index c23fbdf6ac..9f2c23eb48 100644 --- a/res/css/views/elements/_DesktopCapturerSourcePicker.scss +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -21,53 +21,60 @@ limitations under the License. white-space: nowrap; overflow: hidden; margin: 0 auto; - padding-right: 80px; } -.desktop-capturer-selection-scroller { + +.mx_streamSelectorDialog_tabLabels { + display: flex; + padding: 0 0 8px 0; +} + +.mx_streamSelectorDialog_tabLabel, +.mx_streamSelectorDialog_tabLabel_selected +{ width: 100%; - max-height: 100vh; - overflow-y: auto; + text-align: center; + border-radius: 8px; + padding: 8px 0; + border-radius: 8px; + font-size: $font-13px; + position: relative; } -.desktop-capturer-selection-list { - max-width: calc(100% - 100px); - margin: 0 50px 0 50px; - padding: 0; + +.mx_streamSelectorDialog_tabLabel_selected { + background-color: $tab-label-active-bg-color; + color: $tab-label-active-fg-color; +} + +.mx_streamSelectorDialog_panel { display: flex; flex-wrap: wrap; - list-style: none; - overflow: hidden; justify-content: center; + align-items: flex-start; + height: 500px; + overflow: overlay; } -.desktop-capturer-selection-item { - display: flex; - margin: 4px; -} -.desktop-capturer-selection-button { + +.mx_streamSelectorDialog_stream_button { display: flex; flex-direction: column; - align-items: stretch; - width: 145px; - margin: 0; - border: 0; + margin: 8px; border-radius: 4px; - padding: 4px; - background: #20262b; - color: #ffffff; - text-align: left; - transition: background-color .15s, box-shadow .15s; } -.desktop-capturer-selection-button:hover, -.desktop-capturer-selection-button:focus { - background: #363c43; + +.mx_streamSelectorDialog_stream_button:hover, +.mx_streamSelectorDialog_stream_button:focus { + background: $roomtile-selected-bg-color; } -.desktop-capturer-selection-thumbnail { - width: 100%; - height: 81px; - object-fit: cover; + +.mx_streamSelectorDialog_stream_thumbnail { + margin: 4px; + width: 312px; } -.desktop-capturer-selection-name { - margin: 6px 0 6px; + +.mx_streamSelectorDialog_stream_name { + margin: 0 4px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; + width: 312px; } diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index a1d57884e1..399aec1e2c 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -18,7 +18,19 @@ limitations under the License. import React from 'react'; import { _td } from '../../../languageHandler'; import BaseDialog from "..//dialogs/BaseDialog" +import AccessibleButton from './AccessibleButton'; +export enum Tabs { + Screens = "screens", + Windows = "windows", +} +export interface ElectronDesktopCapturerSource { + display_id: string; + id: string; + name: string; + thumbnail, + appIcon, +} export interface DesktopCapturerSourceIProps { source: ElectronDesktopCapturerSource, onSelect(source: ElectronDesktopCapturerSource): void, @@ -36,30 +48,25 @@ export class DesktopCapturerSource extends React.Component - {this.props.source.name} - + {this.props.source.name} + ); } } -export interface ElectronDesktopCapturerSource { - display_id: string; - id: string; - name: string; - thumbnail, - appIcon, + +export interface DesktopCapturerSourcePickerIState { + selectedTab: Tabs; } - - export interface DesktopCapturerSourcePickerIProps { sources: Array; onFinished(source: ElectronDesktopCapturerSource): void, @@ -67,41 +74,80 @@ export interface DesktopCapturerSourcePickerIProps { // TODO: Figure out a way to update sources for live preview -export default class DesktopCapturerSourcePicker extends React.Component { +export default class DesktopCapturerSourcePicker extends React.Component< + DesktopCapturerSourcePickerIProps, + DesktopCapturerSourcePickerIState + > { constructor(props) { super(props); + + this.state = { + selectedTab: Tabs.Screens, + } } onSelect = (source) => { this.props.onFinished(source); } - render() { - const screens = this.props.sources - .filter((source) => { - return source.id.startsWith("screen"); - }) - .map((source) => { - return ; - }); + onScreensClick = (ev) => { + this.setState({selectedTab: Tabs.Screens}); + } - const windows = this.props.sources - .filter((source) => { - return source.id.startsWith("window"); - }) - .map((source) => { - return ; - }); + onWindowsClick = (ev) => { + this.setState({selectedTab: Tabs.Windows}); + } + + onCloseClick = (ev) => { + this.props.onFinished(null); + } + + render() { + let sources; + if (this.state.selectedTab === Tabs.Screens) { + sources = this.props.sources + .filter((source) => { + return source.id.startsWith("screen"); + }) + .map((source) => { + return ; + }); + } else { + sources = this.props.sources + .filter((source) => { + return source.id.startsWith("window"); + }) + .map((source) => { + return ; + }); + } + const buttonStyle = "mx_streamSelectorDialog_tabLabel"; + const screensButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Screens) ? "_selected" : ""); + const windowsButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Windows) ? "_selected" : ""); + console.log(screensButtonStyle, windowsButtonStyle); return ( - -

{_td("Screens")}

-
- { screens } + +
+ + {_td("Screens")} + + + {_td("Windows")} +
-

{_td("Windows")}

-
- { windows } +
+ { sources }
); From 322afe6450b136dfc6a77d55e9f6c077ae4db499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 26 Dec 2020 18:00:08 +0100 Subject: [PATCH 05/62] Remove log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/DesktopCapturerSourcePicker.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index 399aec1e2c..b9e5037dee 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -124,7 +124,6 @@ export default class DesktopCapturerSourcePicker extends React.Component< const buttonStyle = "mx_streamSelectorDialog_tabLabel"; const screensButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Screens) ? "_selected" : ""); const windowsButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Windows) ? "_selected" : ""); - console.log(screensButtonStyle, windowsButtonStyle); return ( Date: Sat, 26 Dec 2020 18:10:50 +0100 Subject: [PATCH 06/62] Type cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/@types/global.d.ts | 9 ++++++++ src/CallHandler.tsx | 8 +------ .../elements/DesktopCapturerSourcePicker.tsx | 23 +++++++------------ 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 741798761f..72b9ee56fb 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -66,6 +66,15 @@ declare global { mxModalWidgetStore: ModalWidgetStore; } + export interface DesktopCapturerSource { + id: string; + name: string; + thumbnail; + // This property is not camelcase and isn't used, therefore it is commented + //display_id: string; + appIcon; + } + interface Document { // https://developer.mozilla.org/en-US/docs/Web/API/Document/hasStorageAccess hasStorageAccess?: () => Promise; diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index 3958420329..a8f121dfb9 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -84,12 +84,6 @@ import { CallError } from "matrix-js-sdk/src/webrtc/call"; import { logger } from 'matrix-js-sdk/src/logger'; import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker" -export interface ElectronDesktopCapturerSource { - display_id: string; - id: string; - name: string; -} - enum AudioID { Ring = 'ringAudio', Ringback = 'ringbackAudio', @@ -485,7 +479,7 @@ export default class CallHandler { call.placeScreenSharingCall( remoteElement, localElement, - async (sources: Array) : Promise => { + async (sources: Array) : Promise => { const {finished} = Modal.createDialog(DesktopCapturerSourcePicker, {sources}); const [source] = await finished; return source; diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index b9e5037dee..a134df6d68 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -24,19 +24,13 @@ export enum Tabs { Screens = "screens", Windows = "windows", } -export interface ElectronDesktopCapturerSource { - display_id: string; - id: string; - name: string; - thumbnail, - appIcon, -} + export interface DesktopCapturerSourceIProps { - source: ElectronDesktopCapturerSource, - onSelect(source: ElectronDesktopCapturerSource): void, + source: DesktopCapturerSource, + onSelect(source: DesktopCapturerSource): void, } -export class DesktopCapturerSource extends React.Component { +export class ExistingSource extends React.Component { constructor(props) { super(props); } @@ -63,13 +57,12 @@ export class DesktopCapturerSource extends React.Component; - onFinished(source: ElectronDesktopCapturerSource): void, + sources: Array; + onFinished(source: DesktopCapturerSource): void, } // TODO: Figure out a way to update sources for live preview @@ -110,7 +103,7 @@ export default class DesktopCapturerSourcePicker extends React.Component< return source.id.startsWith("screen"); }) .map((source) => { - return ; + return ; }); } else { sources = this.props.sources @@ -118,7 +111,7 @@ export default class DesktopCapturerSourcePicker extends React.Component< return source.id.startsWith("window"); }) .map((source) => { - return ; + return ; }); } const buttonStyle = "mx_streamSelectorDialog_tabLabel"; From b24464269f4e054258bdbbe96dcd335bcb24caff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 26 Dec 2020 18:28:53 +0100 Subject: [PATCH 07/62] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 507f3e071f..51757a89c1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1831,6 +1831,9 @@ "Use the Desktop app to search encrypted messages": "Use the Desktop app to search encrypted messages", "This version of %(brand)s does not support viewing some encrypted files": "This version of %(brand)s does not support viewing some encrypted files", "This version of %(brand)s does not support searching encrypted messages": "This version of %(brand)s does not support searching encrypted messages", + "Share your screen": "Share your screen", + "Screens": "Screens", + "Windows": "Windows", "Join": "Join", "No results": "No results", "Please create a new issue on GitHub so that we can investigate this bug.": "Please create a new issue on GitHub so that we can investigate this bug.", From eff26600e8e3bbe68f2f738fcb09b99f6d0c7805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 26 Dec 2020 18:33:23 +0100 Subject: [PATCH 08/62] Linting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_DesktopCapturerSourcePicker.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss index 9f2c23eb48..8a257b697c 100644 --- a/res/css/views/elements/_DesktopCapturerSourcePicker.scss +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -29,13 +29,11 @@ limitations under the License. } .mx_streamSelectorDialog_tabLabel, -.mx_streamSelectorDialog_tabLabel_selected -{ +.mx_streamSelectorDialog_tabLabel_selected { width: 100%; text-align: center; border-radius: 8px; padding: 8px 0; - border-radius: 8px; font-size: $font-13px; position: relative; } From 14a3b884972768da3280fc2e7a4b7a64bf295f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 28 Dec 2020 08:15:54 +0100 Subject: [PATCH 09/62] Consistent formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/elements/DesktopCapturerSourcePicker.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index a134df6d68..3d9068727e 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -26,8 +26,8 @@ export enum Tabs { } export interface DesktopCapturerSourceIProps { - source: DesktopCapturerSource, - onSelect(source: DesktopCapturerSource): void, + source: DesktopCapturerSource; + onSelect(source: DesktopCapturerSource): void; } export class ExistingSource extends React.Component { @@ -62,7 +62,7 @@ export interface DesktopCapturerSourcePickerIState { } export interface DesktopCapturerSourcePickerIProps { sources: Array; - onFinished(source: DesktopCapturerSource): void, + onFinished(source: DesktopCapturerSource): void; } // TODO: Figure out a way to update sources for live preview From 95d93d143ed27e58a3af28bdab650bf8f3df5b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 28 Dec 2020 08:38:29 +0100 Subject: [PATCH 10/62] Clean up the SCSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_DesktopCapturerSourcePicker.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss index 8a257b697c..4cb984e83c 100644 --- a/res/css/views/elements/_DesktopCapturerSourcePicker.scss +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -16,11 +16,7 @@ limitations under the License. */ .mx_streamSelectorDialog { - -ms-text-overflow: ellipsis; - text-overflow: ellipsis; - white-space: nowrap; overflow: hidden; - margin: 0 auto; } .mx_streamSelectorDialog_tabLabels { @@ -35,7 +31,6 @@ limitations under the License. border-radius: 8px; padding: 8px 0; font-size: $font-13px; - position: relative; } .mx_streamSelectorDialog_tabLabel_selected { From b8334bfd4a45543c051a749aedbfca5ea18ceae9 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Sat, 2 Jan 2021 15:31:49 -0600 Subject: [PATCH 11/62] Add option to hide the stickers button in the composer Signed-off-by: Aaron Raimist --- src/components/views/rooms/MessageComposer.js | 3 ++- .../views/settings/tabs/user/PreferencesUserSettingsTab.js | 1 + src/i18n/strings/en_EN.json | 1 + src/settings/Settings.ts | 5 +++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 4ddff8f4b0..2d22b591b4 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -423,7 +423,8 @@ export default class MessageComposer extends React.Component { , ); - if (SettingsStore.getValue(UIFeature.Widgets)) { + if (SettingsStore.getValue(UIFeature.Widgets) && + SettingsStore.getValue("MessageComposerInput.showStickersButton")) { controls.push(); } diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 4d8493401e..b03ea8763c 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -34,6 +34,7 @@ export default class PreferencesUserSettingsTab extends React.Component { 'MessageComposerInput.suggestEmoji', 'sendTypingNotifications', 'MessageComposerInput.ctrlEnterToSend', + 'MessageComposerInput.showStickersButton', ]; static TIMELINE_SETTINGS = [ diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c0939871e2..14906df935 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -783,6 +783,7 @@ "Font size": "Font size", "Use custom size": "Use custom size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", + "Show stickers button": "Show stickers button", "Use a more compact ‘Modern’ layout": "Use a more compact ‘Modern’ layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b239b809fe..3a187d45d8 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -240,6 +240,11 @@ export const SETTINGS: {[setting: string]: ISetting} = { default: true, invertedSettingName: 'MessageComposerInput.dontSuggestEmoji', }, + "MessageComposerInput.showStickersButton": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Show stickers button'), + default: true, + }, // TODO: Wire up appropriately to UI (FTUE notifications) "Notifications.alwaysShowBadgeCounts": { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, From efd889c09abf125dd83aadfe3597b6fc348d33cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 5 Jan 2021 19:32:59 +0100 Subject: [PATCH 12/62] Remove commented line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/DesktopCapturerSourcePicker.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index 3d9068727e..46155f1cbc 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -36,7 +36,6 @@ export class ExistingSource extends React.Component } onClick = (ev) => { - //ev.stopPropagation(); this.props.onSelect(this.props.source); } From 8652c2e65366b9ffbdb6da0f9111e6bcc47b4874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 5 Jan 2021 19:49:09 +0100 Subject: [PATCH 13/62] Fixed class names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../_DesktopCapturerSourcePicker.scss | 22 +++++++++---------- .../elements/DesktopCapturerSourcePicker.tsx | 14 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss index 4cb984e83c..f4ceedbd11 100644 --- a/res/css/views/elements/_DesktopCapturerSourcePicker.scss +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -15,17 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_streamSelectorDialog { +.mx_desktopCapturerSourcePicker { overflow: hidden; } -.mx_streamSelectorDialog_tabLabels { +.mx_desktopCapturerSourcePicker_tabLabels { display: flex; padding: 0 0 8px 0; } -.mx_streamSelectorDialog_tabLabel, -.mx_streamSelectorDialog_tabLabel_selected { +.mx_desktopCapturerSourcePicker_tabLabel, +.mx_desktopCapturerSourcePicker_tabLabel_selected { width: 100%; text-align: center; border-radius: 8px; @@ -33,12 +33,12 @@ limitations under the License. font-size: $font-13px; } -.mx_streamSelectorDialog_tabLabel_selected { +.mx_desktopCapturerSourcePicker_tabLabel_selected { background-color: $tab-label-active-bg-color; color: $tab-label-active-fg-color; } -.mx_streamSelectorDialog_panel { +.mx_desktopCapturerSourcePicker_panel { display: flex; flex-wrap: wrap; justify-content: center; @@ -47,24 +47,24 @@ limitations under the License. overflow: overlay; } -.mx_streamSelectorDialog_stream_button { +.mx_desktopCapturerSourcePicker_stream_button { display: flex; flex-direction: column; margin: 8px; border-radius: 4px; } -.mx_streamSelectorDialog_stream_button:hover, -.mx_streamSelectorDialog_stream_button:focus { +.mx_desktopCapturerSourcePicker_stream_button:hover, +.mx_desktopCapturerSourcePicker_stream_button:focus { background: $roomtile-selected-bg-color; } -.mx_streamSelectorDialog_stream_thumbnail { +.mx_desktopCapturerSourcePicker_stream_thumbnail { margin: 4px; width: 312px; } -.mx_streamSelectorDialog_stream_name { +.mx_desktopCapturerSourcePicker_stream_name { margin: 0 4px; white-space: nowrap; text-overflow: ellipsis; diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index 46155f1cbc..e1586c881f 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -42,15 +42,15 @@ export class ExistingSource extends React.Component render() { return ( - {this.props.source.name} + {this.props.source.name} ); } @@ -113,17 +113,17 @@ export default class DesktopCapturerSourcePicker extends React.Component< return ; }); } - const buttonStyle = "mx_streamSelectorDialog_tabLabel"; + const buttonStyle = "mx_desktopCapturerSourcePicker_tabLabel"; const screensButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Screens) ? "_selected" : ""); const windowsButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Windows) ? "_selected" : ""); return ( -
+
-
+
{ sources }
From d497594a6f1981272836924e2f10301e07cf9123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 5 Jan 2021 19:55:08 +0100 Subject: [PATCH 14/62] Remove unnecessary line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/DesktopCapturerSourcePicker.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index e1586c881f..d398b8ba0d 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -43,7 +43,6 @@ export class ExistingSource extends React.Component return ( Date: Tue, 5 Jan 2021 20:27:11 +0100 Subject: [PATCH 15/62] Fixed translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/elements/DesktopCapturerSourcePicker.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index d398b8ba0d..ea0573eb4a 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -16,7 +16,7 @@ limitations under the License. */ import React from 'react'; -import { _td } from '../../../languageHandler'; +import { _t } from '../../../languageHandler'; import BaseDialog from "..//dialogs/BaseDialog" import AccessibleButton from './AccessibleButton'; @@ -120,20 +120,20 @@ export default class DesktopCapturerSourcePicker extends React.Component<
- {_td("Screens")} + {_t("Screens")} - {_td("Windows")} + {_t("Windows")}
From bb7e76f69e9cf901acf041b00e12b0c2542278d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 5 Jan 2021 20:36:54 +0100 Subject: [PATCH 16/62] Hopefully fixed copyrights MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_DesktopCapturerSourcePicker.scss | 5 ++--- .../views/elements/DesktopCapturerSourcePicker.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss index f4ceedbd11..b5e148ee47 100644 --- a/res/css/views/elements/_DesktopCapturerSourcePicker.scss +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -1,12 +1,11 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2021 Šimon Brandner 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 +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, diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index ea0573eb4a..4ed27cad8c 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -1,12 +1,11 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2021 Šimon Brandner 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 +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, From b1010189be9a0e47a8d04735be3f32f0c52e32b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 5 Jan 2021 20:41:33 +0100 Subject: [PATCH 17/62] Tabbed copyright link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/elements/_DesktopCapturerSourcePicker.scss | 2 +- src/components/views/elements/DesktopCapturerSourcePicker.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_DesktopCapturerSourcePicker.scss b/res/css/views/elements/_DesktopCapturerSourcePicker.scss index b5e148ee47..69dde5925e 100644 --- a/res/css/views/elements/_DesktopCapturerSourcePicker.scss +++ b/res/css/views/elements/_DesktopCapturerSourcePicker.scss @@ -5,7 +5,7 @@ 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 + 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, diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index 4ed27cad8c..acb6376887 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -5,7 +5,7 @@ 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 + 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, From 20b17202584f384d0397759481bf813dabd08457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 5 Jan 2021 21:08:25 +0100 Subject: [PATCH 18/62] Added a line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/DesktopCapturerSourcePicker.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index acb6376887..b6fbad62f1 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -111,6 +111,7 @@ export default class DesktopCapturerSourcePicker extends React.Component< return ; }); } + const buttonStyle = "mx_desktopCapturerSourcePicker_tabLabel"; const screensButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Screens) ? "_selected" : ""); const windowsButtonStyle = buttonStyle + ((this.state.selectedTab === Tabs.Windows) ? "_selected" : ""); From 8f91b04eb34992d47ce01329acc56b16c8b09568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 14 Jan 2021 08:35:04 +0100 Subject: [PATCH 19/62] Use contextBridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/DesktopCapturerSourcePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index b6fbad62f1..b04b50ca99 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -46,7 +46,7 @@ export class ExistingSource extends React.Component onClick={this.onClick} > {this.props.source.name} From eca8ef3b3505f52aadb356ff5718054c725a207c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 14 Jan 2021 12:44:48 +0100 Subject: [PATCH 20/62] Update thumbnails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/CallHandler.tsx | 4 +-- .../elements/DesktopCapturerSourcePicker.tsx | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index e9ccd6ef20..804c6a6699 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -513,8 +513,8 @@ export default class CallHandler { call.placeScreenSharingCall( remoteElement, localElement, - async (sources: Array) : Promise => { - const {finished} = Modal.createDialog(DesktopCapturerSourcePicker, {sources}); + async () : Promise => { + const {finished} = Modal.createDialog(DesktopCapturerSourcePicker); const [source] = await finished; return source; }); diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx index b04b50ca99..70c5fbaa8d 100644 --- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx +++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx @@ -18,6 +18,7 @@ import React from 'react'; import { _t } from '../../../languageHandler'; import BaseDialog from "..//dialogs/BaseDialog" import AccessibleButton from './AccessibleButton'; +import {getDesktopCapturerSources} from "matrix-js-sdk/src/webrtc/call"; export enum Tabs { Screens = "screens", @@ -56,24 +57,37 @@ export class ExistingSource extends React.Component export interface DesktopCapturerSourcePickerIState { selectedTab: Tabs; + sources: Array; } export interface DesktopCapturerSourcePickerIProps { - sources: Array; onFinished(source: DesktopCapturerSource): void; } -// TODO: Figure out a way to update sources for live preview - export default class DesktopCapturerSourcePicker extends React.Component< DesktopCapturerSourcePickerIProps, DesktopCapturerSourcePickerIState > { + interval; + constructor(props) { super(props); this.state = { selectedTab: Tabs.Screens, - } + sources: [], + }; + } + + componentDidMount() { + this.interval = setInterval(async () => { + this.setState({ + sources: await getDesktopCapturerSources(), + }); + }, 500); + } + + componentWillUnmount() { + clearInterval(this.interval); } onSelect = (source) => { @@ -95,7 +109,7 @@ export default class DesktopCapturerSourcePicker extends React.Component< render() { let sources; if (this.state.selectedTab === Tabs.Screens) { - sources = this.props.sources + sources = this.state.sources .filter((source) => { return source.id.startsWith("screen"); }) @@ -103,7 +117,7 @@ export default class DesktopCapturerSourcePicker extends React.Component< return ; }); } else { - sources = this.props.sources + sources = this.state.sources .filter((source) => { return source.id.startsWith("window"); }) From 7f727be4f65598457786a4c9fc742fff40394b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 18 Jan 2021 17:44:32 +0100 Subject: [PATCH 21/62] Added expand code block option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/settings/tabs/user/PreferencesUserSettingsTab.js | 1 + src/settings/Settings.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 4d8493401e..636c5cb7c8 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -46,6 +46,7 @@ export default class PreferencesUserSettingsTab extends React.Component { 'alwaysShowTimestamps', 'showRedactions', 'enableSyntaxHighlightLanguageDetection', + 'expandCodeByDefault', 'showJoinLeaves', 'showAvatarChanges', 'showDisplaynameChanges', diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b239b809fe..bef1564e90 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -300,6 +300,11 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td('Enable automatic language detection for syntax highlighting'), default: false, }, + "expandCodeByDefault": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Expand code blocks by default'), + default: false, + }, "Pill.shouldShowPillAvatar": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Show avatars in user and room mentions'), From 49dce58027c5f754ad88a2a8b34811cdac6225da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 18 Jan 2021 17:49:09 +0100 Subject: [PATCH 22/62] Added expanding based on the option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 7 ++++++- src/components/views/messages/TextualBody.js | 10 +++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 429ac7ed4b..5fc7a5f04b 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -493,7 +493,6 @@ $left-gutter: 64px; // https://github.com/vector-im/vector-web/issues/754 overflow-x: overlay; overflow-y: visible; - max-height: 30vh; } code { @@ -502,6 +501,12 @@ $left-gutter: 64px; } } +.mx_EventTile_content_collapsedCode { + pre { + max-height: 30vh; + } +} + .mx_EventTile:hover .mx_EventTile_body pre, .mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre { border: 1px solid #e5e5e5; // deliberate constant as we're behind an invert filter diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 8f153e48e9..ce11e63026 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -35,6 +35,7 @@ import {isPermalinkHost} from "../../../utils/permalinks/Permalinks"; import {toRightOf} from "../../structures/ContextMenu"; import {copyPlaintext} from "../../../utils/strings"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; +import classNames from "classnames"; export default class TextualBody extends React.Component { static propTypes = { @@ -69,6 +70,7 @@ export default class TextualBody extends React.Component { // track whether the preview widget is hidden widgetHidden: false, + codeBlockExpanded: SettingsStore.getValue("expandCodeByDefault"), }; } @@ -434,6 +436,12 @@ export default class TextualBody extends React.Component { }); } + const defaultCaseClasses = classNames({ + mx_MTextBody: true, + mx_EventTile_content: true, + mx_EventTile_content_collapsedCode: !this.state.codeBlockExpanded, + }); + switch (content.msgtype) { case "m.emote": return ( @@ -459,7 +467,7 @@ export default class TextualBody extends React.Component { ); default: // including "m.text" return ( - + { body } { widgets } From 61281a855c08d9a9eacd4e1da103acc3f59cfb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 19 Jan 2021 16:35:32 +0100 Subject: [PATCH 23/62] Redo expanding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 39 ++++++- src/components/views/messages/TextualBody.js | 111 ++++++++++--------- 2 files changed, 91 insertions(+), 59 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 5fc7a5f04b..c587251d3e 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -501,10 +501,12 @@ $left-gutter: 64px; } } -.mx_EventTile_content_collapsedCode { - pre { - max-height: 30vh; - } +.mx_EventTile_expandedCodeBlock { + max-height: 100vh; +} + +.mx_EventTile_collapsedCodeBlock { + max-height: 30vh; } .mx_EventTile:hover .mx_EventTile_body pre, @@ -531,6 +533,35 @@ $left-gutter: 64px; background-color: $message-action-bar-fg-color; } + +// Inserted adjacent to
 blocks, (See TextualBody)
+.mx_EventTile_expandButton {
+    position: absolute;
+    display: inline-block;
+    visibility: hidden;
+    cursor: pointer;
+    top: 6px;
+    right: 6px;
+    width: 19px;
+    height: 19px;
+    mask-image: url($copy-button-url);
+    background-color: $message-action-bar-fg-color;
+}
+
+// Inserted adjacent to 
 blocks, (See TextualBody)
+.mx_EventTile_collapseButton {
+    position: absolute;
+    display: inline-block;
+    visibility: hidden;
+    cursor: pointer;
+    top: 6px;
+    right: 6px;
+    width: 19px;
+    height: 19px;
+    mask-image: url($copy-button-url);
+    background-color: $message-action-bar-fg-color;
+}
+
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
 .mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
     visibility: visible;
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index ff864d9c13..8d341e4a63 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -35,7 +35,6 @@ import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
 import {toRightOf} from "../../structures/ContextMenu";
 import {copyPlaintext} from "../../../utils/strings";
 import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
-import classNames from "classnames";
 
 export default class TextualBody extends React.Component {
     static propTypes = {
@@ -70,7 +69,6 @@ export default class TextualBody extends React.Component {
 
             // track whether the preview widget is hidden
             widgetHidden: false,
-            codeBlockExpanded: SettingsStore.getValue("expandCodeByDefault"),
         };
     }
 
@@ -93,29 +91,70 @@ export default class TextualBody extends React.Component {
         this.calculateUrlPreview();
 
         if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
-            const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("code");
+            const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
             if (blocks.length > 0) {
+                for (let i = 0; i < blocks.length; i++) {
+                    this._handleCodeBlockExpansion(blocks[i]);
+                    this._addCodeCopyButton(blocks[i]);
+                }
                 // Do this asynchronously: parsing code takes time and we don't
                 // need to block the DOM update on it.
                 setTimeout(() => {
                     if (this._unmounted) return;
                     for (let i = 0; i < blocks.length; i++) {
-                        if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
-                            highlight.highlightBlock(blocks[i]);
-                        } else {
-                            // Only syntax highlight if there's a class starting with language-
-                            const classes = blocks[i].className.split(/\s+/).filter(function(cl) {
-                                return cl.startsWith('language-') && !cl.startsWith('language-_');
-                            });
-
-                            if (classes.length != 0) {
-                                highlight.highlightBlock(blocks[i]);
-                            }
-                        }
+                        this._highlightCode(blocks[i].firstChild);
                     }
                 }, 10);
             }
-            this._addCodeCopyButton();
+        }
+    }
+
+    _addCodeCopyButton(codeBlock) {
+        const button = document.createElement("span");
+        button.className = "mx_EventTile_copyButton";
+        button.onclick = async () => {
+            const copyCode = button.parentNode.getElementsByTagName("pre")[0];
+            const successful = await copyPlaintext(copyCode.textContent);
+
+            const buttonRect = button.getBoundingClientRect();
+            const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
+            const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
+                ...toRightOf(buttonRect, 2),
+                message: successful ? _t('Copied!') : _t('Failed to copy'),
+            });
+            button.onmouseleave = close;
+        };
+
+        // Wrap a div around 
 so that the copy button can be correctly positioned
+        // when the 
 overflows and is scrolled horizontally.
+        const div = document.createElement("div");
+        div.className = "mx_EventTile_pre_container";
+
+        // Insert containing div in place of 
 block
+        codeBlock.parentNode.replaceChild(div, codeBlock);
+
+        // Append 
 block and copy button to container
+        div.appendChild(codeBlock);
+        div.appendChild(button);
+    }
+
+    _handleCodeBlockExpansion(codeBlock) {
+        const expandCodeBlock = SettingsStore.getValue("expandCodeByDefault");
+        codeBlock.className = expandCodeBlock ? "mx_EventTile_expandedCodeBlock" : "mx_EventTile_collapsedCodeBlock";
+    }
+
+    _highlightCode(codeBlock) {
+        if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
+            highlight.highlightBlock(codeBlock);
+        } else {
+            // Only syntax highlight if there's a class starting with language-
+            const classes = codeBlock.className.split(/\s+/).filter(function(cl) {
+                return cl.startsWith('language-') && !cl.startsWith('language-_');
+            });
+
+            if (classes.length != 0) {
+                highlight.highlightBlock(codeBlock);
+            }
         }
     }
 
@@ -256,38 +295,6 @@ export default class TextualBody extends React.Component {
         }
     }
 
-    _addCodeCopyButton() {
-        // Add 'copy' buttons to pre blocks
-        Array.from(ReactDOM.findDOMNode(this).querySelectorAll('.mx_EventTile_body pre')).forEach((p) => {
-            const button = document.createElement("span");
-            button.className = "mx_EventTile_copyButton";
-            button.onclick = async () => {
-                const copyCode = button.parentNode.getElementsByTagName("pre")[0];
-                const successful = await copyPlaintext(copyCode.textContent);
-
-                const buttonRect = button.getBoundingClientRect();
-                const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
-                const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
-                    ...toRightOf(buttonRect, 2),
-                    message: successful ? _t('Copied!') : _t('Failed to copy'),
-                });
-                button.onmouseleave = close;
-            };
-
-            // Wrap a div around 
 so that the copy button can be correctly positioned
-            // when the 
 overflows and is scrolled horizontally.
-            const div = document.createElement("div");
-            div.className = "mx_EventTile_pre_container";
-
-            // Insert containing div in place of 
 block
-            p.parentNode.replaceChild(div, p);
-
-            // Append 
 block and copy button to container
-            div.appendChild(p);
-            div.appendChild(button);
-        });
-    }
-
     onCancelClick = event => {
         this.setState({ widgetHidden: true });
         // FIXME: persist this somewhere smarter than local storage
@@ -439,12 +446,6 @@ export default class TextualBody extends React.Component {
             });
         }
 
-        const defaultCaseClasses = classNames({
-            mx_MTextBody: true,
-            mx_EventTile_content: true,
-            mx_EventTile_content_collapsedCode: !this.state.codeBlockExpanded,
-        });
-
         switch (content.msgtype) {
             case "m.emote":
                 return (
@@ -470,7 +471,7 @@ export default class TextualBody extends React.Component {
                 );
             default: // including "m.text"
                 return (
-                    
+                    
                         { body }
                         { widgets }
                     

From e6ab47ff764b28cdbbba2e190345f95435b5319d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Tue, 19 Jan 2021 17:30:02 +0100
Subject: [PATCH 24/62] Fix bug
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_EventTile.scss          | 33 --------------------
 src/components/views/messages/TextualBody.js |  5 +--
 2 files changed, 3 insertions(+), 35 deletions(-)

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index c587251d3e..83aa3ec294 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -501,10 +501,6 @@ $left-gutter: 64px;
     }
 }
 
-.mx_EventTile_expandedCodeBlock {
-    max-height: 100vh;
-}
-
 .mx_EventTile_collapsedCodeBlock {
     max-height: 30vh;
 }
@@ -533,35 +529,6 @@ $left-gutter: 64px;
     background-color: $message-action-bar-fg-color;
 }
 
-
-// Inserted adjacent to 
 blocks, (See TextualBody)
-.mx_EventTile_expandButton {
-    position: absolute;
-    display: inline-block;
-    visibility: hidden;
-    cursor: pointer;
-    top: 6px;
-    right: 6px;
-    width: 19px;
-    height: 19px;
-    mask-image: url($copy-button-url);
-    background-color: $message-action-bar-fg-color;
-}
-
-// Inserted adjacent to 
 blocks, (See TextualBody)
-.mx_EventTile_collapseButton {
-    position: absolute;
-    display: inline-block;
-    visibility: hidden;
-    cursor: pointer;
-    top: 6px;
-    right: 6px;
-    width: 19px;
-    height: 19px;
-    mask-image: url($copy-button-url);
-    background-color: $message-action-bar-fg-color;
-}
-
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
 .mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
     visibility: visible;
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 8d341e4a63..84a21c0da3 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -139,8 +139,9 @@ export default class TextualBody extends React.Component {
     }
 
     _handleCodeBlockExpansion(codeBlock) {
-        const expandCodeBlock = SettingsStore.getValue("expandCodeByDefault");
-        codeBlock.className = expandCodeBlock ? "mx_EventTile_expandedCodeBlock" : "mx_EventTile_collapsedCodeBlock";
+        if (!SettingsStore.getValue("expandCodeByDefault")) {
+            codeBlock.className = "mx_EventTile_collapsedCodeBlock";
+        }
     }
 
     _highlightCode(codeBlock) {

From 95939f3d6ca34c1757903f1efa28aeca4c911fd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Tue, 19 Jan 2021 18:21:59 +0100
Subject: [PATCH 25/62] Added _wrapInDiv() method
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 84a21c0da3..a6b32baea2 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -94,8 +94,11 @@ export default class TextualBody extends React.Component {
             const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
             if (blocks.length > 0) {
                 for (let i = 0; i < blocks.length; i++) {
-                    this._handleCodeBlockExpansion(blocks[i]);
-                    this._addCodeCopyButton(blocks[i]);
+                    // Wrap a div around 
 so that the copy button can be correctly positioned
+                    // when the 
 overflows and is scrolled horizontally.
+                    const div = this._wrapInDiv(blocks[i]);
+                    this._handleCodeBlockExpansion(div);
+                    this._addCodeCopyButton(div);
                 }
                 // Do this asynchronously: parsing code takes time and we don't
                 // need to block the DOM update on it.
@@ -125,17 +128,19 @@ export default class TextualBody extends React.Component {
             button.onmouseleave = close;
         };
 
-        // Wrap a div around 
 so that the copy button can be correctly positioned
-        // when the 
 overflows and is scrolled horizontally.
+        codeBlock.appendChild(button);
+    }
+
+    _wrapInDiv(codeBlock) {
         const div = document.createElement("div");
         div.className = "mx_EventTile_pre_container";
 
         // Insert containing div in place of 
 block
         codeBlock.parentNode.replaceChild(div, codeBlock);
-
         // Append 
 block and copy button to container
         div.appendChild(codeBlock);
-        div.appendChild(button);
+
+        return div;
     }
 
     _handleCodeBlockExpansion(codeBlock) {

From 58b2c18cf55845715b8b6261f476caee86701cc1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Tue, 19 Jan 2021 21:02:39 +0100
Subject: [PATCH 26/62] This somehow works
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_EventTile.scss           | 48 ++++++++++++++++++-
 res/img/feather-customised/maximize.svg       | 24 ++++++++++
 res/img/feather-customised/minimize.svg       | 24 ++++++++++
 .../legacy-light/css/_legacy-light.scss       |  3 +-
 res/themes/light/css/_light.scss              |  2 +
 src/components/views/messages/TextualBody.js  | 27 ++++++++++-
 6 files changed, 124 insertions(+), 4 deletions(-)
 create mode 100644 res/img/feather-customised/maximize.svg
 create mode 100644 res/img/feather-customised/minimize.svg

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 83aa3ec294..d80d6703a7 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -521,16 +521,60 @@ $left-gutter: 64px;
     display: inline-block;
     visibility: hidden;
     cursor: pointer;
-    top: 6px;
+    top: 31px;
     right: 6px;
     width: 19px;
     height: 19px;
     mask-image: url($copy-button-url);
     background-color: $message-action-bar-fg-color;
 }
+.mx_EventTile_collapseButton {
+    position: absolute;
+    display: inline-block;
+    visibility: hidden;
+    cursor: pointer;
+    top: 6px;
+    right: 6px;
+    width: 19px;
+    height: 19px;
+    mask-image: url($collapse-button-url);
+    background-color: $message-action-bar-fg-color;
+}
+.mx_EventTile_expandButton {
+    position: absolute;
+    display: inline-block;
+    visibility: hidden;
+    cursor: pointer;
+    top: 6px;
+    right: 6px;
+    width: 19px;
+    height: 19px;
+    mask-image: url($expand-button-url);
+    background-color: $message-action-bar-fg-color;
+}
+
+/*.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
+    visibility: visible;
+}
+
+.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_collapseButton,
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_collapseButton {
+    visibility: visible;
+}
+
+.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_expandButton,
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_expandButton {
+    visibility: visible;
+}*/
+
 
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
-.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton, 
+.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_collapseButton,
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_collapseButton, 
+.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_expandButton,
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_expandButton {
     visibility: visible;
 }
 
diff --git a/res/img/feather-customised/maximize.svg b/res/img/feather-customised/maximize.svg
new file mode 100644
index 0000000000..ad74c8b1eb
--- /dev/null
+++ b/res/img/feather-customised/maximize.svg
@@ -0,0 +1,24 @@
+
+  
+  
+  
+  
+
diff --git a/res/img/feather-customised/minimize.svg b/res/img/feather-customised/minimize.svg
new file mode 100644
index 0000000000..102af6df95
--- /dev/null
+++ b/res/img/feather-customised/minimize.svg
@@ -0,0 +1,24 @@
+
+  
+  
+  
+  
+
diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss
index 085d6d7f10..6219d5a5bf 100644
--- a/res/themes/legacy-light/css/_legacy-light.scss
+++ b/res/themes/legacy-light/css/_legacy-light.scss
@@ -237,7 +237,8 @@ $event-redacted-border-color: #cccccc;
 $event-timestamp-color: #acacac;
 
 $copy-button-url: "$(res)/img/feather-customised/clipboard.svg";
-
+$collapse-button-url: "$(res)/img/feather-customised/minimize.svg";
+$expand-button-url: "$(res)/img/feather-customised/maximize.svg";
 
 // e2e
 $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss
index 4cfeeae05e..77c0e72726 100644
--- a/res/themes/light/css/_light.scss
+++ b/res/themes/light/css/_light.scss
@@ -237,6 +237,8 @@ $event-redacted-border-color: #cccccc;
 $event-timestamp-color: #acacac;
 
 $copy-button-url: "$(res)/img/feather-customised/clipboard.svg";
+$collapse-button-url: "$(res)/img/feather-customised/minimize.svg";
+$expand-button-url: "$(res)/img/feather-customised/maximize.svg";
 
 // e2e
 $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index a6b32baea2..f7d66c3308 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -97,8 +97,9 @@ export default class TextualBody extends React.Component {
                     // Wrap a div around 
 so that the copy button can be correctly positioned
                     // when the 
 overflows and is scrolled horizontally.
                     const div = this._wrapInDiv(blocks[i]);
-                    this._handleCodeBlockExpansion(div);
+                    this._handleCodeBlockExpansion(div.firstChild);
                     this._addCodeCopyButton(div);
+                    this._addCodeExpansionButton(div);
                 }
                 // Do this asynchronously: parsing code takes time and we don't
                 // need to block the DOM update on it.
@@ -112,6 +113,30 @@ export default class TextualBody extends React.Component {
         }
     }
 
+    _addCodeExpansionButton(codeBlock) {
+        // TODO: What if the block is small and the we don't need the icon?
+
+        const pre = codeBlock.getElementsByTagName("pre")[0];
+        const button = document.createElement("span");
+        if (pre.className == "mx_EventTile_collapsedCodeBlock") {
+            button.className = "mx_EventTile_expandButton";
+        } else {
+            button.className = "mx_EventTile_collapseButton";
+        }
+
+        button.onclick = async () => {
+            if (pre.className == "mx_EventTile_collapsedCodeBlock") {
+                pre.className = "";
+                button.className = "mx_EventTile_expandButton";
+            } else {
+                pre.className = "mx_EventTile_collapsedCodeBlock";
+                button.className = "mx_EventTile_collapseButton";
+            }
+        };
+
+        codeBlock.appendChild(button);
+    }
+
     _addCodeCopyButton(codeBlock) {
         const button = document.createElement("span");
         button.className = "mx_EventTile_copyButton";

From 8535a11dd27569121f823dcbc6d25123e48e8358 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Wed, 20 Jan 2021 08:44:32 +0100
Subject: [PATCH 27/62] Rename some variable and cleanup a bit
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 65 +++++++++++---------
 1 file changed, 36 insertions(+), 29 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index f7d66c3308..afda815e6d 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -91,32 +91,39 @@ export default class TextualBody extends React.Component {
         this.calculateUrlPreview();
 
         if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
-            const blocks = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
-            if (blocks.length > 0) {
-                for (let i = 0; i < blocks.length; i++) {
-                    // Wrap a div around 
 so that the copy button can be correctly positioned
-                    // when the 
 overflows and is scrolled horizontally.
-                    const div = this._wrapInDiv(blocks[i]);
-                    this._handleCodeBlockExpansion(div.firstChild);
+            // Handle expansion and add buttons
+            const pres = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
+            if (pres.length > 0) {
+                for (let i = 0; i < pres.length; i++) {
+                    /* Wrap a div around 
 so that the copy button can be correctly positioned
+                     * when the 
 overflows and is scrolled horizontally. */
+                    const div = this._wrapInDiv(pres[i]);
+                    this._handleCodeBlockExpansion(pres[i]);
                     this._addCodeCopyButton(div);
                     this._addCodeExpansionButton(div);
                 }
-                // Do this asynchronously: parsing code takes time and we don't
-                // need to block the DOM update on it.
-                setTimeout(() => {
-                    if (this._unmounted) return;
-                    for (let i = 0; i < blocks.length; i++) {
-                        this._highlightCode(blocks[i].firstChild);
-                    }
-                }, 10);
+            }
+            // Highlight code
+            const codes = ReactDOM.findDOMNode(this).getElementsByTagName("code");
+            if (codes.length >0) {
+                for (let i = 0; i < codes.length; i++) {
+                    /* Do this asynchronously: parsing code takes time and we don't
+                     * need to block the DOM update on it. */
+                    setTimeout(() => {
+                        if (this._unmounted) return;
+                            for (let i = 0; i < pres.length; i++) {
+                                this._highlightCode(codes[i]);
+                            }
+                    }, 10);
+                }
             }
         }
     }
 
-    _addCodeExpansionButton(codeBlock) {
+    _addCodeExpansionButton(div) {
         // TODO: What if the block is small and the we don't need the icon?
 
-        const pre = codeBlock.getElementsByTagName("pre")[0];
+        const pre = div.getElementsByTagName("pre")[0];
         const button = document.createElement("span");
         if (pre.className == "mx_EventTile_collapsedCodeBlock") {
             button.className = "mx_EventTile_expandButton";
@@ -134,10 +141,10 @@ export default class TextualBody extends React.Component {
             }
         };
 
-        codeBlock.appendChild(button);
+        div.appendChild(button);
     }
 
-    _addCodeCopyButton(codeBlock) {
+    _addCodeCopyButton(div) {
         const button = document.createElement("span");
         button.className = "mx_EventTile_copyButton";
         button.onclick = async () => {
@@ -153,38 +160,38 @@ export default class TextualBody extends React.Component {
             button.onmouseleave = close;
         };
 
-        codeBlock.appendChild(button);
+        div.appendChild(button);
     }
 
-    _wrapInDiv(codeBlock) {
+    _wrapInDiv(pre) {
         const div = document.createElement("div");
         div.className = "mx_EventTile_pre_container";
 
         // Insert containing div in place of 
 block
-        codeBlock.parentNode.replaceChild(div, codeBlock);
+        pre.parentNode.replaceChild(div, pre);
         // Append 
 block and copy button to container
-        div.appendChild(codeBlock);
+        div.appendChild(pre);
 
         return div;
     }
 
-    _handleCodeBlockExpansion(codeBlock) {
+    _handleCodeBlockExpansion(pre) {
         if (!SettingsStore.getValue("expandCodeByDefault")) {
-            codeBlock.className = "mx_EventTile_collapsedCodeBlock";
+            pre.className = "mx_EventTile_collapsedCodeBlock";
         }
     }
 
-    _highlightCode(codeBlock) {
+    _highlightCode(code) {
         if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
-            highlight.highlightBlock(codeBlock);
+            highlight.highlightBlock(code);
         } else {
             // Only syntax highlight if there's a class starting with language-
-            const classes = codeBlock.className.split(/\s+/).filter(function(cl) {
+            const classes = code.className.split(/\s+/).filter(function(cl) {
                 return cl.startsWith('language-') && !cl.startsWith('language-_');
             });
 
             if (classes.length != 0) {
-                highlight.highlightBlock(codeBlock);
+                highlight.highlightBlock(code);
             }
         }
     }

From 072cbe98b5c0dbfee81383a5b4785c24a5673711 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Wed, 20 Jan 2021 13:03:05 +0100
Subject: [PATCH 28/62] Simplifie
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index afda815e6d..5dc7d21176 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -100,7 +100,7 @@ export default class TextualBody extends React.Component {
                     const div = this._wrapInDiv(pres[i]);
                     this._handleCodeBlockExpansion(pres[i]);
                     this._addCodeCopyButton(div);
-                    this._addCodeExpansionButton(div);
+                    this._addCodeExpansionButton(div, pres[i]);
                 }
             }
             // Highlight code
@@ -120,10 +120,9 @@ export default class TextualBody extends React.Component {
         }
     }
 
-    _addCodeExpansionButton(div) {
+    _addCodeExpansionButton(div, pre) {
         // TODO: What if the block is small and the we don't need the icon?
 
-        const pre = div.getElementsByTagName("pre")[0];
         const button = document.createElement("span");
         if (pre.className == "mx_EventTile_collapsedCodeBlock") {
             button.className = "mx_EventTile_expandButton";

From 25f30ca79f81361bcfa2813de57d0d8b97d3e21b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Wed, 20 Jan 2021 13:05:23 +0100
Subject: [PATCH 29/62] Fix indent
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 5dc7d21176..902271a29e 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -111,9 +111,9 @@ export default class TextualBody extends React.Component {
                      * need to block the DOM update on it. */
                     setTimeout(() => {
                         if (this._unmounted) return;
-                            for (let i = 0; i < pres.length; i++) {
-                                this._highlightCode(codes[i]);
-                            }
+                        for (let i = 0; i < pres.length; i++) {
+                            this._highlightCode(codes[i]);
+                        }
                     }, 10);
                 }
             }

From 19be3293e0e10af6df269620aff30a53cbbacdfc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Wed, 20 Jan 2021 13:06:08 +0100
Subject: [PATCH 30/62] Remove commented code
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_EventTile.scss | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index d80d6703a7..3acebd03e9 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -553,22 +553,6 @@ $left-gutter: 64px;
     background-color: $message-action-bar-fg-color;
 }
 
-/*.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
-.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton {
-    visibility: visible;
-}
-
-.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_collapseButton,
-.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_collapseButton {
-    visibility: visible;
-}
-
-.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_expandButton,
-.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_expandButton {
-    visibility: visible;
-}*/
-
-
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
 .mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton, 
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_collapseButton,

From f8179f0c7a2c7cfb92d9cd3d6f6ecfdeb5a45513 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Wed, 20 Jan 2021 14:08:57 +0100
Subject: [PATCH 31/62] Fix inverted icon
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 902271a29e..511ea61904 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -133,10 +133,10 @@ export default class TextualBody extends React.Component {
         button.onclick = async () => {
             if (pre.className == "mx_EventTile_collapsedCodeBlock") {
                 pre.className = "";
-                button.className = "mx_EventTile_expandButton";
+                button.className = "mx_EventTile_collapseButton";
             } else {
                 pre.className = "mx_EventTile_collapsedCodeBlock";
-                button.className = "mx_EventTile_collapseButton";
+                button.className = "mx_EventTile_expandButton";
             }
         };
 

From 14bf1696315093e019c68088d1d65c54f0615808 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Wed, 20 Jan 2021 15:01:23 +0100
Subject: [PATCH 32/62] Handle small codeblocks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_EventTile.scss          | 29 ++++++--------------
 src/components/views/messages/TextualBody.js | 26 +++++++++++++-----
 2 files changed, 27 insertions(+), 28 deletions(-)

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 3acebd03e9..2d986a6fc6 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -516,41 +516,28 @@ $left-gutter: 64px;
 }
 
 // Inserted adjacent to 
 blocks, (See TextualBody)
-.mx_EventTile_copyButton {
+.mx_EventTile_button {
     position: absolute;
     display: inline-block;
     visibility: hidden;
     cursor: pointer;
-    top: 31px;
+    top: 6px;
     right: 6px;
     width: 19px;
     height: 19px;
-    mask-image: url($copy-button-url);
     background-color: $message-action-bar-fg-color;
 }
+.mx_EventTile_buttonBottom {
+    top: 31px;
+}
+.mx_EventTile_copyButton {
+    mask-image: url($copy-button-url);
+}
 .mx_EventTile_collapseButton {
-    position: absolute;
-    display: inline-block;
-    visibility: hidden;
-    cursor: pointer;
-    top: 6px;
-    right: 6px;
-    width: 19px;
-    height: 19px;
     mask-image: url($collapse-button-url);
-    background-color: $message-action-bar-fg-color;
 }
 .mx_EventTile_expandButton {
-    position: absolute;
-    display: inline-block;
-    visibility: hidden;
-    cursor: pointer;
-    top: 6px;
-    right: 6px;
-    width: 19px;
-    height: 19px;
     mask-image: url($expand-button-url);
-    background-color: $message-action-bar-fg-color;
 }
 
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 511ea61904..da6aaa2830 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -99,8 +99,8 @@ export default class TextualBody extends React.Component {
                      * when the 
 overflows and is scrolled horizontally. */
                     const div = this._wrapInDiv(pres[i]);
                     this._handleCodeBlockExpansion(pres[i]);
-                    this._addCodeCopyButton(div);
                     this._addCodeExpansionButton(div, pres[i]);
+                    this._addCodeCopyButton(div);
                 }
             }
             // Highlight code
@@ -121,22 +121,28 @@ export default class TextualBody extends React.Component {
     }
 
     _addCodeExpansionButton(div, pre) {
-        // TODO: What if the block is small and the we don't need the icon?
+        /* Calculate how many percent does the pre element take up.
+         * If it's less than 30% we don't add the expansion button. */
+        const percentageOfViewport = pre.offsetHeight / window.innerHeight * 100;
+        console.log("expansionButtonExists", percentageOfViewport);
+        if (percentageOfViewport < 30) return;
 
         const button = document.createElement("span");
+        button.className = "mx_EventTile_button ";
         if (pre.className == "mx_EventTile_collapsedCodeBlock") {
-            button.className = "mx_EventTile_expandButton";
+            button.className += "mx_EventTile_expandButton";
         } else {
-            button.className = "mx_EventTile_collapseButton";
+            button.className += "mx_EventTile_collapseButton";
         }
 
         button.onclick = async () => {
+            button.className = "mx_EventTile_button ";
             if (pre.className == "mx_EventTile_collapsedCodeBlock") {
                 pre.className = "";
-                button.className = "mx_EventTile_collapseButton";
+                button.className += "mx_EventTile_collapseButton";
             } else {
                 pre.className = "mx_EventTile_collapsedCodeBlock";
-                button.className = "mx_EventTile_expandButton";
+                button.className += "mx_EventTile_expandButton";
             }
         };
 
@@ -145,7 +151,13 @@ export default class TextualBody extends React.Component {
 
     _addCodeCopyButton(div) {
         const button = document.createElement("span");
-        button.className = "mx_EventTile_copyButton";
+        button.className = "mx_EventTile_button mx_EventTile_copyButton ";
+
+        /* Check if expansion button exists. If so
+         * we put the copy button to the bottom */
+        const expansionButtonExists = div.getElementsByClassName("mx_EventTile_button");
+        if (expansionButtonExists.length > 0) button.className += "mx_EventTile_buttonBottom";
+
         button.onclick = async () => {
             const copyCode = button.parentNode.getElementsByTagName("pre")[0];
             const successful = await copyPlaintext(copyCode.textContent);

From bd2423a52c9d7712179b2a519af42de80bfe99df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 10:53:18 +0100
Subject: [PATCH 33/62] Added line numbering
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_EventTile.scss          | 12 ++++++++++++
 src/components/views/messages/TextualBody.js | 17 ++++++++++++++++-
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 2d986a6fc6..826604c081 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -501,6 +501,18 @@ $left-gutter: 64px;
     }
 }
 
+.mx_EventTile_lineNumbers {
+    float: left;
+    margin: 0 0.5em 0 -1.5em;
+    color: gray;
+}
+
+.mx_EventTile_lineNumber {
+    text-align: right;
+    display: block;
+    padding-left: 1em;
+}
+
 .mx_EventTile_collapsedCodeBlock {
     max-height: 30vh;
 }
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index da6aaa2830..18f1302a4a 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -101,6 +101,8 @@ export default class TextualBody extends React.Component {
                     this._handleCodeBlockExpansion(pres[i]);
                     this._addCodeExpansionButton(div, pres[i]);
                     this._addCodeCopyButton(div);
+                    // TODO: Add option to disable this
+                    this._addLineNumbers(pres[i]);
                 }
             }
             // Highlight code
@@ -159,7 +161,7 @@ export default class TextualBody extends React.Component {
         if (expansionButtonExists.length > 0) button.className += "mx_EventTile_buttonBottom";
 
         button.onclick = async () => {
-            const copyCode = button.parentNode.getElementsByTagName("pre")[0];
+            const copyCode = button.parentNode.getElementsByTagName("code")[0];
             const successful = await copyPlaintext(copyCode.textContent);
 
             const buttonRect = button.getBoundingClientRect();
@@ -192,6 +194,19 @@ export default class TextualBody extends React.Component {
         }
     }
 
+    _addLineNumbers(pre) {
+        //const lineNumbers = document.createElement("span");
+        //lineNumbers.className = "mx_EventTile_lineNumbers";
+        pre.innerHTML = '' + pre.innerHTML + '';
+        const lineNumbers = pre.getElementsByClassName("mx_EventTile_lineNumbers")[0];
+        // Calculate number of lines in pre
+        const number = pre.innerHTML.split(/\n/).length;
+        // Iterate through lines starting with 1 (number of the first line is 1)
+        for (let i = 1; i < number; i++) {
+            lineNumbers.innerHTML += '' + i + '';
+        }
+    }
+
     _highlightCode(code) {
         if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) {
             highlight.highlightBlock(code);

From c7375431146a17697ba13bbdacdbd9ca40104572 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 13:04:51 +0100
Subject: [PATCH 34/62] Remove commented lines
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 18f1302a4a..e0931df097 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -195,9 +195,7 @@ export default class TextualBody extends React.Component {
     }
 
     _addLineNumbers(pre) {
-        //const lineNumbers = document.createElement("span");
-        //lineNumbers.className = "mx_EventTile_lineNumbers";
-        pre.innerHTML = '' + pre.innerHTML + '';
+        pre.innerHTML = '' + pre.innerHTML + '';
         const lineNumbers = pre.getElementsByClassName("mx_EventTile_lineNumbers")[0];
         // Calculate number of lines in pre
         const number = pre.innerHTML.split(/\n/).length;

From da09362f99fc477220b42f5921c96075b065c570 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 13:08:55 +0100
Subject: [PATCH 35/62] Added option to hide line numbers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js                | 6 ++++--
 .../views/settings/tabs/user/PreferencesUserSettingsTab.js  | 1 +
 src/settings/Settings.ts                                    | 5 +++++
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index e0931df097..a019b1a47b 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -81,6 +81,7 @@ export default class TextualBody extends React.Component {
     }
 
     _applyFormatting() {
+        const showLineNumbers = SettingsStore.getValue("showCodeLineNumbers");
         this.activateSpoilers([this._content.current]);
 
         // pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer
@@ -101,8 +102,9 @@ export default class TextualBody extends React.Component {
                     this._handleCodeBlockExpansion(pres[i]);
                     this._addCodeExpansionButton(div, pres[i]);
                     this._addCodeCopyButton(div);
-                    // TODO: Add option to disable this
-                    this._addLineNumbers(pres[i]);
+                    if (showLineNumbers) {
+                        this._addLineNumbers(pres[i]);
+                    }
                 }
             }
             // Highlight code
diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
index 636c5cb7c8..e9abe4dbd3 100644
--- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
+++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
@@ -47,6 +47,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
         'showRedactions',
         'enableSyntaxHighlightLanguageDetection',
         'expandCodeByDefault',
+        'showCodeLineNumbers',
         'showJoinLeaves',
         'showAvatarChanges',
         'showDisplaynameChanges',
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index f6af68bedd..b3601213bf 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -305,6 +305,11 @@ export const SETTINGS: {[setting: string]: ISetting} = {
         displayName: _td('Expand code blocks by default'),
         default: false,
     },
+    "showCodeLineNumbers": {
+        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
+        displayName: _td('Show line numbers in code blocks'),
+        default: true,
+    },
     "Pill.shouldShowPillAvatar": {
         supportedLevels: LEVELS_ACCOUNT_SETTINGS,
         displayName: _td('Show avatars in user and room mentions'),

From 97c5058f009c6981474189fd07395df0da1140ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 13:10:21 +0100
Subject: [PATCH 36/62] Removed log
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index a019b1a47b..a9f73404e9 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -128,7 +128,6 @@ export default class TextualBody extends React.Component {
         /* Calculate how many percent does the pre element take up.
          * If it's less than 30% we don't add the expansion button. */
         const percentageOfViewport = pre.offsetHeight / window.innerHeight * 100;
-        console.log("expansionButtonExists", percentageOfViewport);
         if (percentageOfViewport < 30) return;
 
         const button = document.createElement("span");

From c7e61ac71d090be8f4880af02edc461447026953 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 17:32:57 +0100
Subject: [PATCH 37/62] Rerender MessagePanel
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/structures/MessagePanel.js    | 11 +++++++++++
 src/components/views/messages/TextualBody.js |  4 ++++
 2 files changed, 15 insertions(+)

diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index 375545f819..4298e75b32 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -32,6 +32,7 @@ import {textForEvent} from "../../TextForEvent";
 import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer";
 import DMRoomMap from "../../utils/DMRoomMap";
 import NewRoomIntro from "../views/rooms/NewRoomIntro";
+import dis from "../../dispatcher/dispatcher";
 
 const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
 const continuedTypes = ['m.sticker', 'm.room.message'];
@@ -203,6 +204,16 @@ export default class MessagePanel extends React.Component {
 
         this._showTypingNotificationsWatcherRef =
             SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange);
+
+        dis.register(this.onAction);
+    }
+
+    onAction = payload => {
+        switch (payload.action) {
+            case "rerender_MessagePanel":
+                this.forceUpdate();
+                break;
+        }
     }
 
     componentDidMount() {
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index a9f73404e9..45df78d480 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -147,6 +147,10 @@ export default class TextualBody extends React.Component {
                 pre.className = "mx_EventTile_collapsedCodeBlock";
                 button.className += "mx_EventTile_expandButton";
             }
+            /* Now we need to rerender the MessagePanel because the
+             * content's size has changed. Otherwise scrolling could
+             * get broken */
+            dis.dispatch({action: "rerender_MessagePanel"});
         };
 
         div.appendChild(button);

From 828d401c6c953f17b4c941871041c06f03b708fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 17:37:22 +0100
Subject: [PATCH 38/62] i18n
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/i18n/strings/en_EN.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 274bd247e2..05faf26a8d 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -794,6 +794,8 @@
     "Always show message timestamps": "Always show message timestamps",
     "Autoplay GIFs and videos": "Autoplay GIFs and videos",
     "Enable automatic language detection for syntax highlighting": "Enable automatic language detection for syntax highlighting",
+    "Expand code blocks by default": "Expand code blocks by default",
+    "Show line numbers in code blocks": "Show line numbers in code blocks",
     "Show avatars in user and room mentions": "Show avatars in user and room mentions",
     "Enable big emoji in chat": "Enable big emoji in chat",
     "Send typing notifications": "Send typing notifications",

From 5ca9e8d32394c0d0838d285d6a18d9cbb284c3b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 21 Jan 2021 18:18:07 +0100
Subject: [PATCH 39/62] Remove spaces
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 res/css/views/rooms/_EventTile.scss | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 826604c081..187e207d15 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -553,9 +553,9 @@ $left-gutter: 64px;
 }
 
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
-.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton, 
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton,
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_collapseButton,
-.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_collapseButton, 
+.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_collapseButton,
 .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_expandButton,
 .mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_expandButton {
     visibility: visible;

From 28ff4e6955dba3eea69ef14a03a4d7647150161c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Mon, 1 Feb 2021 15:53:53 +0100
Subject: [PATCH 40/62] Move DesktopCapturerSource
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/@types/global.d.ts                                   | 9 ---------
 .../views/elements/DesktopCapturerSourcePicker.tsx       | 6 ++++++
 2 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts
index 72b9ee56fb..741798761f 100644
--- a/src/@types/global.d.ts
+++ b/src/@types/global.d.ts
@@ -66,15 +66,6 @@ declare global {
         mxModalWidgetStore: ModalWidgetStore;
     }
 
-    export interface DesktopCapturerSource {
-        id: string;
-        name: string;
-        thumbnail;
-        // This property is not camelcase and isn't used, therefore it is commented
-        //display_id: string;
-        appIcon;
-    }
-
     interface Document {
         // https://developer.mozilla.org/en-US/docs/Web/API/Document/hasStorageAccess
         hasStorageAccess?: () => Promise;
diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx
index 70c5fbaa8d..384befbb01 100644
--- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx
+++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx
@@ -20,6 +20,12 @@ import BaseDialog from "..//dialogs/BaseDialog"
 import AccessibleButton from './AccessibleButton';
 import {getDesktopCapturerSources} from "matrix-js-sdk/src/webrtc/call";
 
+export interface DesktopCapturerSource {
+    id: string;
+    name: string;
+    thumbnailURL;
+}
+
 export enum Tabs {
     Screens = "screens",
     Windows = "windows",

From 022781e9bcaef4a35d1b4d242b2e57f0f71ef57a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Mon, 1 Feb 2021 16:03:20 +0100
Subject: [PATCH 41/62] Added a comment about updating thumbnails
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/elements/DesktopCapturerSourcePicker.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/views/elements/DesktopCapturerSourcePicker.tsx b/src/components/views/elements/DesktopCapturerSourcePicker.tsx
index 384befbb01..e53683b0ef 100644
--- a/src/components/views/elements/DesktopCapturerSourcePicker.tsx
+++ b/src/components/views/elements/DesktopCapturerSourcePicker.tsx
@@ -85,6 +85,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<
     }
 
     componentDidMount() {
+        // We update the sources every 500ms to get newer thumbnails
         this.interval = setInterval(async () => {
             this.setState({
                 sources: await getDesktopCapturerSources(),

From c1823e2e5ae46996b169842cbe7876e10254cc9d Mon Sep 17 00:00:00 2001
From: dangwynne <31453528+dangwynne@users.noreply.github.com>
Date: Wed, 3 Feb 2021 22:31:28 +0000
Subject: [PATCH 42/62] Close current modal when session is logged out

---
 src/components/structures/MatrixChat.tsx | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index 8ed4b6cd11..ce5467ec89 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -1378,11 +1378,14 @@ export default class MatrixChat extends React.PureComponent {
                 Lifecycle.softLogout();
                 return;
             }
+            
+            Modal.closeCurrentModal("User has been logged out.");
 
             Modal.createTrackedDialog('Signed out', '', ErrorDialog, {
                 title: _t('Signed Out'),
                 description: _t('For security, this session has been signed out. Please sign in again.'),
             });
+            
             dis.dispatch({
                 action: 'logout',
             });

From 88aa2a6f70e87fb1e218fd24b3c5e7530e421ceb Mon Sep 17 00:00:00 2001
From: dangwynne 
Date: Thu, 4 Feb 2021 09:33:21 +0000
Subject: [PATCH 43/62] fix whitespace change. Signed-off-by: Dan Gwynne
 dangwynne1@gmail.com

---
 src/components/structures/MatrixChat.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index ce5467ec89..5d938317cb 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -1378,14 +1378,14 @@ export default class MatrixChat extends React.PureComponent {
                 Lifecycle.softLogout();
                 return;
             }
-            
+
             Modal.closeCurrentModal("User has been logged out.");
 
             Modal.createTrackedDialog('Signed out', '', ErrorDialog, {
                 title: _t('Signed Out'),
                 description: _t('For security, this session has been signed out. Please sign in again.'),
             });
-            
+
             dis.dispatch({
                 action: 'logout',
             });

From 9db86a38f628d270b7d26c2ab18616d88849fe39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Thu, 4 Feb 2021 17:58:21 +0100
Subject: [PATCH 44/62] Update src/components/views/messages/TextualBody.js

Co-authored-by: J. Ryan Stinnett 
---
 src/components/views/messages/TextualBody.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 45df78d480..30fbc08fd3 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -109,7 +109,7 @@ export default class TextualBody extends React.Component {
             }
             // Highlight code
             const codes = ReactDOM.findDOMNode(this).getElementsByTagName("code");
-            if (codes.length >0) {
+            if (codes.length > 0) {
                 for (let i = 0; i < codes.length; i++) {
                     /* Do this asynchronously: parsing code takes time and we don't
                      * need to block the DOM update on it. */

From 9bd8fef6b3603c3ca9fd301fd66266a551ba41d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Fri, 5 Feb 2021 08:22:02 +0100
Subject: [PATCH 45/62] Change comment styling

Co-authored-by: J. Ryan Stinnett 
---
 src/components/views/messages/TextualBody.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 30fbc08fd3..68e018631a 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -96,8 +96,8 @@ export default class TextualBody extends React.Component {
             const pres = ReactDOM.findDOMNode(this).getElementsByTagName("pre");
             if (pres.length > 0) {
                 for (let i = 0; i < pres.length; i++) {
-                    /* Wrap a div around 
 so that the copy button can be correctly positioned
-                     * when the 
 overflows and is scrolled horizontally. */
+                    // Wrap a div around 
 so that the copy button can be correctly positioned
+                    // when the 
 overflows and is scrolled horizontally.
                     const div = this._wrapInDiv(pres[i]);
                     this._handleCodeBlockExpansion(pres[i]);
                     this._addCodeExpansionButton(div, pres[i]);

From ad5c8fe7e4f8caac9015c48eca0e060f38a7f340 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Fri, 5 Feb 2021 08:27:07 +0100
Subject: [PATCH 46/62] Revert "Rerender MessagePanel"

This reverts commit c7e61ac71d090be8f4880af02edc461447026953.
---
 src/components/structures/MessagePanel.js    | 11 -----------
 src/components/views/messages/TextualBody.js |  4 ----
 2 files changed, 15 deletions(-)

diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index 4298e75b32..375545f819 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -32,7 +32,6 @@ import {textForEvent} from "../../TextForEvent";
 import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer";
 import DMRoomMap from "../../utils/DMRoomMap";
 import NewRoomIntro from "../views/rooms/NewRoomIntro";
-import dis from "../../dispatcher/dispatcher";
 
 const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
 const continuedTypes = ['m.sticker', 'm.room.message'];
@@ -204,16 +203,6 @@ export default class MessagePanel extends React.Component {
 
         this._showTypingNotificationsWatcherRef =
             SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange);
-
-        dis.register(this.onAction);
-    }
-
-    onAction = payload => {
-        switch (payload.action) {
-            case "rerender_MessagePanel":
-                this.forceUpdate();
-                break;
-        }
     }
 
     componentDidMount() {
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index 68e018631a..a2ed349812 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -147,10 +147,6 @@ export default class TextualBody extends React.Component {
                 pre.className = "mx_EventTile_collapsedCodeBlock";
                 button.className += "mx_EventTile_expandButton";
             }
-            /* Now we need to rerender the MessagePanel because the
-             * content's size has changed. Otherwise scrolling could
-             * get broken */
-            dis.dispatch({action: "rerender_MessagePanel"});
         };
 
         div.appendChild(button);

From 77248725edd68ef2f8cb3b64d313f7eea33ec5ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Fri, 5 Feb 2021 08:29:24 +0100
Subject: [PATCH 47/62] Added onHeightChanged call
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index a2ed349812..bd0d0e3457 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -147,6 +147,10 @@ export default class TextualBody extends React.Component {
                 pre.className = "mx_EventTile_collapsedCodeBlock";
                 button.className += "mx_EventTile_expandButton";
             }
+
+            // By expanding/collapsing we changed
+            // the height, therefore we call this
+            this.props.onHeightChanged();
         };
 
         div.appendChild(button);

From ec73f2ec49f4a49e8bf09126bda5240177c32231 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0imon=20Brandner?= 
Date: Fri, 5 Feb 2021 08:30:54 +0100
Subject: [PATCH 48/62] Change comment styling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Šimon Brandner 
---
 src/components/views/messages/TextualBody.js | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index bd0d0e3457..71f7572a25 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -111,8 +111,8 @@ export default class TextualBody extends React.Component {
             const codes = ReactDOM.findDOMNode(this).getElementsByTagName("code");
             if (codes.length > 0) {
                 for (let i = 0; i < codes.length; i++) {
-                    /* Do this asynchronously: parsing code takes time and we don't
-                     * need to block the DOM update on it. */
+                    // Do this asynchronously: parsing code takes time and we don't
+                    // need to block the DOM update on it.
                     setTimeout(() => {
                         if (this._unmounted) return;
                         for (let i = 0; i < pres.length; i++) {
@@ -125,8 +125,8 @@ export default class TextualBody extends React.Component {
     }
 
     _addCodeExpansionButton(div, pre) {
-        /* Calculate how many percent does the pre element take up.
-         * If it's less than 30% we don't add the expansion button. */
+        // Calculate how many percent does the pre element take up.
+        // If it's less than 30% we don't add the expansion button.
         const percentageOfViewport = pre.offsetHeight / window.innerHeight * 100;
         if (percentageOfViewport < 30) return;
 
@@ -160,8 +160,8 @@ export default class TextualBody extends React.Component {
         const button = document.createElement("span");
         button.className = "mx_EventTile_button mx_EventTile_copyButton ";
 
-        /* Check if expansion button exists. If so
-         * we put the copy button to the bottom */
+        // Check if expansion button exists. If so
+        // we put the copy button to the bottom
         const expansionButtonExists = div.getElementsByClassName("mx_EventTile_button");
         if (expansionButtonExists.length > 0) button.className += "mx_EventTile_buttonBottom";
 

From 12f6272ab47887f187f810378c06b51b35956abe Mon Sep 17 00:00:00 2001
From: dangwynne 
Date: Fri, 5 Feb 2021 09:24:12 +0000
Subject: [PATCH 49/62] implement qa feedback for closing modals when logging
 out

---
 src/components/structures/MatrixChat.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index 5d938317cb..2f5500e054 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -1373,14 +1373,15 @@ export default class MatrixChat extends React.PureComponent {
         cli.on('Session.logged_out', function(errObj) {
             if (Lifecycle.isLoggingOut()) return;
 
+            // A modal might have been open when we were logged out by the server
+            Modal.closeCurrentModal('Session.logged_out');
+
             if (errObj.httpStatus === 401 && errObj.data && errObj.data['soft_logout']) {
                 console.warn("Soft logout issued by server - avoiding data deletion");
                 Lifecycle.softLogout();
                 return;
             }
 
-            Modal.closeCurrentModal("User has been logged out.");
-
             Modal.createTrackedDialog('Signed out', '', ErrorDialog, {
                 title: _t('Signed Out'),
                 description: _t('For security, this session has been signed out. Please sign in again.'),

From bffce8689a435eb876fe0f7accfa97a6a664ea81 Mon Sep 17 00:00:00 2001
From: "J. Ryan Stinnett" 
Date: Fri, 5 Feb 2021 13:34:06 +0000
Subject: [PATCH 50/62] Allow saving room topic or name only

This changes the room profile settings to support saving _only_ the room topic or
name in case you have limited access to set one but not the other.

Fixes https://github.com/vector-im/element-web/issues/16375
---
 src/components/views/room_settings/RoomProfileSettings.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/components/views/room_settings/RoomProfileSettings.js b/src/components/views/room_settings/RoomProfileSettings.js
index c76c0823e4..e90dab3b97 100644
--- a/src/components/views/room_settings/RoomProfileSettings.js
+++ b/src/components/views/room_settings/RoomProfileSettings.js
@@ -81,7 +81,11 @@ export default class RoomProfileSettings extends React.Component {
 
         if (!this.state.enableProfileSave) return;
         this._removeAvatar();
-        this.setState({enableProfileSave: false, displayName: this.state.originalDisplayName});
+        this.setState({
+            enableProfileSave: false,
+            displayName: this.state.originalDisplayName,
+            topic: this.state.originalTopic,
+        });
     };
 
     _saveProfile = async (e) => {
@@ -164,7 +168,7 @@ export default class RoomProfileSettings extends React.Component {
         const AvatarSetting = sdk.getComponent('settings.AvatarSetting');
 
         let profileSettingsButtons;
-        if (this.state.canSetTopic && this.state.canSetName) {
+        if (this.state.canSetTopic || this.state.canSetName) {
             profileSettingsButtons = (
                 
Date: Fri, 5 Feb 2021 13:47:20 +0000 Subject: [PATCH 51/62] Normalise cancel behaviour for room avatar This normalises the behaviour of the "Cancel" button for the room profile so that it _always_ restores the existing value for all of room name, topic, and avatar, instead of performing a mix of restore and remove. Fixes https://github.com/vector-im/element-web/issues/16375 --- .../room_settings/RoomProfileSettings.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/views/room_settings/RoomProfileSettings.js b/src/components/views/room_settings/RoomProfileSettings.js index e90dab3b97..c651216e8c 100644 --- a/src/components/views/room_settings/RoomProfileSettings.js +++ b/src/components/views/room_settings/RoomProfileSettings.js @@ -69,22 +69,23 @@ export default class RoomProfileSettings extends React.Component { // clear file upload field so same file can be selected this._avatarUpload.current.value = ""; this.setState({ - avatarUrl: undefined, - avatarFile: undefined, + avatarUrl: null, + avatarFile: null, enableProfileSave: true, }); }; - _clearProfile = async (e) => { + _cancelProfileChanges = async (e) => { e.stopPropagation(); e.preventDefault(); if (!this.state.enableProfileSave) return; - this._removeAvatar(); this.setState({ enableProfileSave: false, displayName: this.state.originalDisplayName, topic: this.state.originalTopic, + avatarUrl: this.state.originalAvatarUrl, + avatarFile: null, }); }; @@ -112,7 +113,7 @@ export default class RoomProfileSettings extends React.Component { newState.originalAvatarUrl = newState.avatarUrl; newState.avatarFile = null; } else if (this.state.originalAvatarUrl !== this.state.avatarUrl) { - await client.sendStateEvent(this.props.roomId, 'm.room.avatar', {url: undefined}, ''); + await client.sendStateEvent(this.props.roomId, 'm.room.avatar', {}, ''); } if (this.state.originalTopic !== this.state.topic) { @@ -168,11 +169,15 @@ export default class RoomProfileSettings extends React.Component { const AvatarSetting = sdk.getComponent('settings.AvatarSetting'); let profileSettingsButtons; - if (this.state.canSetTopic || this.state.canSetName) { + if ( + this.state.canSetName || + this.state.canSetTopic || + this.state.canSetAvatar + ) { profileSettingsButtons = (
From 81bd919a185e0c1ec7cb4e7845a405b349b81abd Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 5 Feb 2021 14:11:57 +0000 Subject: [PATCH 52/62] Port avatar restore to user profile settings --- src/components/views/settings/ProfileSettings.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/views/settings/ProfileSettings.js b/src/components/views/settings/ProfileSettings.js index c11a2e3a5e..4dc69884c3 100644 --- a/src/components/views/settings/ProfileSettings.js +++ b/src/components/views/settings/ProfileSettings.js @@ -52,19 +52,23 @@ export default class ProfileSettings extends React.Component { // clear file upload field so same file can be selected this._avatarUpload.current.value = ""; this.setState({ - avatarUrl: undefined, - avatarFile: undefined, + avatarUrl: null, + avatarFile: null, enableProfileSave: true, }); }; - _clearProfile = async (e) => { + _cancelProfileChanges = async (e) => { e.stopPropagation(); e.preventDefault(); if (!this.state.enableProfileSave) return; - this._removeAvatar(); - this.setState({enableProfileSave: false, displayName: this.state.originalDisplayName}); + this.setState({ + enableProfileSave: false, + displayName: this.state.originalDisplayName, + avatarUrl: this.state.originalAvatarUrl, + avatarFile: null, + }); }; _saveProfile = async (e) => { @@ -186,7 +190,7 @@ export default class ProfileSettings extends React.Component {
From 69e81119e957a78caac2ec03d06eb5838b959158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 5 Feb 2021 15:15:20 +0100 Subject: [PATCH 53/62] Don't jump to bottom on reaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/MessagePanel.js | 2 +- src/components/views/rooms/SendMessageComposer.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index b8dae41ef8..371623642b 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -229,7 +229,7 @@ export default class MessagePanel extends React.Component { onAction = (payload) => { switch (payload.action) { - case "message_sent": + case "scroll_to_bottom": this.scrollToBottom(); break; } diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 7b516e1f52..62c474e417 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -403,6 +403,7 @@ export default class SendMessageComposer extends React.Component { this._editorRef.clearUndoHistory(); this._editorRef.focus(); this._clearStoredEditorState(); + dis.dispatch({action: "scroll_to_bottom"}); } componentWillUnmount() { From 1d1a3e72ca945e0de9555de6c983568afbd2d7e5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 5 Feb 2021 17:18:13 +0000 Subject: [PATCH 54/62] Improve SSO login start screen and 3pid invite handling somewhat --- src/components/structures/MatrixChat.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 8ed4b6cd11..accdd16b4e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -755,6 +755,8 @@ export default class MatrixChat extends React.PureComponent { break; case 'on_logged_in': if ( + // Skip this handling for token login as that always calls onLoggedIn itself + !this.tokenLogin && !Lifecycle.isSoftLogout() && this.state.view !== Views.LOGIN && this.state.view !== Views.REGISTER && @@ -1652,10 +1654,16 @@ export default class MatrixChat extends React.PureComponent { // TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149 let threepidInvite: IThreepidInvite; + // if we landed here from a 3PID invite, persist it if (params.signurl && params.email) { threepidInvite = ThreepidInviteStore.instance .storeInvite(roomString, params as IThreepidInviteWireFormat); } + // otherwise check that this room doesn't already have a known invite + if (!threepidInvite) { + const invites = ThreepidInviteStore.instance.getInvites(); + threepidInvite = invites.find(invite => invite.roomId === roomString); + } // on our URLs there might be a ?via=matrix.org or similar to help // joins to the room succeed. We'll pass these through as an array From f0f6e4678e5a9d1d2d4a75c5545357c21d1938a6 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Sun, 17 Jan 2021 17:49:00 +0100 Subject: [PATCH 55/62] switch room explorer list to css grid Signed-off-by: Michael Weimann --- res/css/structures/_RoomDirectory.scss | 24 +++---- src/components/structures/RoomDirectory.js | 82 ++++++++++++++-------- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/res/css/structures/_RoomDirectory.scss b/res/css/structures/_RoomDirectory.scss index 29e6fecd34..89cb21b7a6 100644 --- a/res/css/structures/_RoomDirectory.scss +++ b/res/css/structures/_RoomDirectory.scss @@ -64,28 +64,23 @@ limitations under the License. } .mx_RoomDirectory_table { - font-size: $font-12px; color: $primary-fg-color; - width: 100%; + display: grid; + font-size: $font-12px; + grid-template-columns: max-content auto max-content max-content max-content; + row-gap: 24px; text-align: left; - table-layout: fixed; + width: 100%; } .mx_RoomDirectory_roomAvatar { - width: 32px; - padding-right: 14px; - vertical-align: top; -} - -.mx_RoomDirectory_roomDescription { - padding-bottom: 16px; + padding: 2px 14px 0 0; } .mx_RoomDirectory_roomMemberCount { + align-self: center; color: $light-fg-color; - width: 60px; - padding: 0 10px; - text-align: center; + padding: 3px 10px 0; &::before { background-color: $light-fg-color; @@ -105,8 +100,7 @@ limitations under the License. } .mx_RoomDirectory_join, .mx_RoomDirectory_preview { - width: 80px; - text-align: center; + align-self: center; white-space: nowrap; } diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 9ddacaf829..7387e1aac0 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -477,7 +477,7 @@ export default class RoomDirectory extends React.Component { dis.dispatch(payload); } - getRow(room) { + createRoomCells(room) { const client = MatrixClientPeg.get(); const clientRoom = client.getRoom(room.room_id); const hasJoinedRoom = clientRoom && clientRoom.getMyMembership() === "join"; @@ -523,31 +523,56 @@ export default class RoomDirectory extends React.Component { MatrixClientPeg.get().getHomeserverUrl(), room.avatar_url, 32, 32, "crop", ); - return ( - this.onRoomClicked(room, ev)} // cancel onMouseDown otherwise shift-clicking highlights text onMouseDown={(ev) => {ev.preventDefault();}} + className="mx_RoomDirectory_roomAvatar" > - - - - -
{ name }
  -
{ ev.stopPropagation(); } } - dangerouslySetInnerHTML={{ __html: topic }} /> -
{ get_display_alias_for_room(room) }
- - - { room.num_joined_members } - - {previewButton} - {joinOrViewButton} - - ); + +
, +
this.onRoomClicked(room, ev)} + // cancel onMouseDown otherwise shift-clicking highlights text + onMouseDown={(ev) => {ev.preventDefault();}} + className="mx_RoomDirectory_roomDescription" + > +
{ name }
  +
{ ev.stopPropagation(); } } + dangerouslySetInnerHTML={{ __html: topic }} + /> +
{ get_display_alias_for_room(room) }
+
, +
this.onRoomClicked(room, ev)} + // cancel onMouseDown otherwise shift-clicking highlights text + onMouseDown={(ev) => {ev.preventDefault();}} + className="mx_RoomDirectory_roomMemberCount" + > + { room.num_joined_members } +
, +
this.onRoomClicked(room, ev)} + // cancel onMouseDown otherwise shift-clicking highlights text + onMouseDown={(ev) => {ev.preventDefault();}} + className="mx_RoomDirectory_preview" + > + {previewButton} +
, +
this.onRoomClicked(room, ev)} + // cancel onMouseDown otherwise shift-clicking highlights text + onMouseDown={(ev) => {ev.preventDefault();}} + className="mx_RoomDirectory_join" + > + {joinOrViewButton} +
, + ]; } collectScrollPanel = (element) => { @@ -606,7 +631,8 @@ export default class RoomDirectory extends React.Component { } else if (this.state.protocolsLoading) { content = ; } else { - const rows = (this.state.publicRooms || []).map(room => this.getRow(room)); + const cells = (this.state.publicRooms || []) + .reduce((cells, room) => cells.concat(this.createRoomCells(room)), [],); // we still show the scrollpanel, at least for now, because // otherwise we don't fetch more because we don't get a fill // request from the scrollpanel because there isn't one @@ -617,14 +643,12 @@ export default class RoomDirectory extends React.Component { } let scrollpanel_content; - if (rows.length === 0 && !this.state.loading) { + if (cells.length === 0 && !this.state.loading) { scrollpanel_content = { _t('No rooms to show') }; } else { - scrollpanel_content = - - { rows } - -
; + scrollpanel_content =
+ { cells } +
; } const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); content = Date: Sat, 6 Feb 2021 16:38:07 +0100 Subject: [PATCH 56/62] Fixed jumpy UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/settings/_Notifications.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index e6d09b9a2a..c9b0e73125 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -68,6 +68,8 @@ limitations under the License. .mx_UserNotifSettings_notifTable .mx_Spinner { position: absolute; + left: 0; + top: 0; } .mx_NotificationSound_soundUpload { From cdb0d1c12eae29a172aae0a5ab6835cdd354ebd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 8 Feb 2021 15:22:10 +0100 Subject: [PATCH 57/62] Move and use icons from widget dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_LeftPanelWidget.scss | 2 +- res/css/views/messages/_ViewSourceEvent.scss | 4 ++-- res/img/feather-customised/{widget => }/maximise.svg | 0 res/img/feather-customised/{widget => }/minimise.svg | 0 res/themes/legacy-light/css/_legacy-light.scss | 4 ++-- res/themes/light/css/_light.scss | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) rename res/img/feather-customised/{widget => }/maximise.svg (100%) rename res/img/feather-customised/{widget => }/minimise.svg (100%) diff --git a/res/css/structures/_LeftPanelWidget.scss b/res/css/structures/_LeftPanelWidget.scss index 4df651d7b6..6e2d99bb37 100644 --- a/res/css/structures/_LeftPanelWidget.scss +++ b/res/css/structures/_LeftPanelWidget.scss @@ -134,7 +134,7 @@ limitations under the License. mask-position: center; mask-size: contain; mask-repeat: no-repeat; - mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); + mask-image: url('$(res)/img/feather-customised/maximise.svg'); background: $muted-fg-color; } } diff --git a/res/css/views/messages/_ViewSourceEvent.scss b/res/css/views/messages/_ViewSourceEvent.scss index 076932ee97..66825030e0 100644 --- a/res/css/views/messages/_ViewSourceEvent.scss +++ b/res/css/views/messages/_ViewSourceEvent.scss @@ -35,13 +35,13 @@ limitations under the License. mask-size: auto 12px; visibility: hidden; background-color: $accent-color; - mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); + mask-image: url('$(res)/img/feather-customised/maximise.svg'); } &.mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle { mask-position: 0 bottom; margin-bottom: 7px; - mask-image: url('$(res)/img/feather-customised/widget/minimise.svg'); + mask-image: url('$(res)/img/feather-customised/minimise.svg'); } &:hover .mx_ViewSourceEvent_toggle { diff --git a/res/img/feather-customised/widget/maximise.svg b/res/img/feather-customised/maximise.svg similarity index 100% rename from res/img/feather-customised/widget/maximise.svg rename to res/img/feather-customised/maximise.svg diff --git a/res/img/feather-customised/widget/minimise.svg b/res/img/feather-customised/minimise.svg similarity index 100% rename from res/img/feather-customised/widget/minimise.svg rename to res/img/feather-customised/minimise.svg diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index 6219d5a5bf..a740ba155c 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -237,8 +237,8 @@ $event-redacted-border-color: #cccccc; $event-timestamp-color: #acacac; $copy-button-url: "$(res)/img/feather-customised/clipboard.svg"; -$collapse-button-url: "$(res)/img/feather-customised/minimize.svg"; -$expand-button-url: "$(res)/img/feather-customised/maximize.svg"; +$collapse-button-url: "$(res)/img/feather-customised/minimise.svg"; +$expand-button-url: "$(res)/img/feather-customised/maximise.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 77c0e72726..1c89d83c01 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -237,8 +237,8 @@ $event-redacted-border-color: #cccccc; $event-timestamp-color: #acacac; $copy-button-url: "$(res)/img/feather-customised/clipboard.svg"; -$collapse-button-url: "$(res)/img/feather-customised/minimize.svg"; -$expand-button-url: "$(res)/img/feather-customised/maximize.svg"; +$collapse-button-url: "$(res)/img/feather-customised/minimise.svg"; +$expand-button-url: "$(res)/img/feather-customised/maximise.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color From 9a97b40ce93bd76f0864ca8a4d34ca8ea1e32e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 8 Feb 2021 15:22:17 +0100 Subject: [PATCH 58/62] Remove old icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/img/feather-customised/maximize.svg | 24 ------------------------ res/img/feather-customised/minimize.svg | 24 ------------------------ 2 files changed, 48 deletions(-) delete mode 100644 res/img/feather-customised/maximize.svg delete mode 100644 res/img/feather-customised/minimize.svg diff --git a/res/img/feather-customised/maximize.svg b/res/img/feather-customised/maximize.svg deleted file mode 100644 index ad74c8b1eb..0000000000 --- a/res/img/feather-customised/maximize.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/res/img/feather-customised/minimize.svg b/res/img/feather-customised/minimize.svg deleted file mode 100644 index 102af6df95..0000000000 --- a/res/img/feather-customised/minimize.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - From 257b8819b36895f5b36966ffc5f2ef8875980692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 8 Feb 2021 15:22:30 +0100 Subject: [PATCH 59/62] Change icon size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 187e207d15..5a69fa4b1a 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -546,9 +546,15 @@ $left-gutter: 64px; mask-image: url($copy-button-url); } .mx_EventTile_collapseButton { + mask-size: 75%; + mask-position: center; + mask-repeat: no-repeat; mask-image: url($collapse-button-url); } .mx_EventTile_expandButton { + mask-size: 75%; + mask-position: center; + mask-repeat: no-repeat; mask-image: url($expand-button-url); } From c4a390687199776da366c23435b384292f898161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 8 Feb 2021 15:38:43 +0100 Subject: [PATCH 60/62] Fix positioning of spinner in a smarter way MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/settings/_Notifications.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index c9b0e73125..77a7bc5b68 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -64,12 +64,11 @@ limitations under the License. .mx_UserNotifSettings_notifTable { display: table; + position: relative; } .mx_UserNotifSettings_notifTable .mx_Spinner { position: absolute; - left: 0; - top: 0; } .mx_NotificationSound_soundUpload { From c77f0bcfa452ac5e808b211d215e273e0e952295 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 8 Feb 2021 14:47:03 +0000 Subject: [PATCH 61/62] Fix the %s in logs Finally --- src/CallHandler.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index b5d4ed155f..a6d3534fa1 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -583,7 +583,7 @@ export default class CallHandler { return source; }); } else { - console.error("Unknown conf call type: %s", type); + console.error("Unknown conf call type: " + type); } } @@ -617,7 +617,7 @@ export default class CallHandler { const room = MatrixClientPeg.get().getRoom(payload.room_id); if (!room) { - console.error("Room %s does not exist.", payload.room_id); + console.error(`Room ${payload.room_id} does not exist.`); return; } @@ -628,7 +628,7 @@ export default class CallHandler { }); return; } else if (members.length === 2) { - console.info("Place %s call in %s", payload.type, payload.room_id); + console.info(`Place ${payload.type} call in ${payload.room_id}`); this.placeCall(payload.room_id, payload.type, payload.local_element, payload.remote_element); } else { // > 2 @@ -643,17 +643,17 @@ export default class CallHandler { } break; case 'place_conference_call': - console.info("Place conference call in %s", payload.room_id); + console.info("Place conference call in " + payload.room_id); Analytics.trackEvent('voip', 'placeConferenceCall'); CountlyAnalytics.instance.trackStartCall(payload.room_id, payload.type === PlaceCallType.Video, true); this.startCallApp(payload.room_id, payload.type); break; case 'end_conference': - console.info("Terminating conference call in %s", payload.room_id); + console.info("Terminating conference call in " + payload.room_id); this.terminateCallApp(payload.room_id); break; case 'hangup_conference': - console.info("Leaving conference call in %s", payload.room_id); + console.info("Leaving conference call in "+ payload.room_id); this.hangupCallApp(payload.room_id); break; case 'incoming_call': From 890bb5b9eb850c43e69185f490a24c762eb22019 Mon Sep 17 00:00:00 2001 From: Marco Zehe Date: Mon, 8 Feb 2021 16:39:21 +0100 Subject: [PATCH 62/62] Give breadcrumbs toolbar an accessibility label. Fixes vector-im/element-web#16406. Signed-off-by: Marco Zehe --- src/components/views/rooms/RoomBreadcrumbs.tsx | 2 +- src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomBreadcrumbs.tsx b/src/components/views/rooms/RoomBreadcrumbs.tsx index 7725ce456e..ff60ab7779 100644 --- a/src/components/views/rooms/RoomBreadcrumbs.tsx +++ b/src/components/views/rooms/RoomBreadcrumbs.tsx @@ -111,7 +111,7 @@ export default class RoomBreadcrumbs extends React.PureComponent appear={true} in={this.state.doAnimation} timeout={640} classNames='mx_RoomBreadcrumbs' > - + {tiles.slice(this.state.skipFirst ? 1 : 0)} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7986184d97..d5eb4de126 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1465,6 +1465,7 @@ "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Seen by %(displayName)s (%(userName)s) at %(dateTime)s", "Replying": "Replying", "Room %(name)s": "Room %(name)s", + "Recently visited rooms": "Recently visited rooms", "No recently visited rooms": "No recently visited rooms", "No rooms to show": "No rooms to show", "Unnamed room": "Unnamed room",