diff --git a/code_style.md b/code_style.md index 3ad0d38873..01c1f37146 100644 --- a/code_style.md +++ b/code_style.md @@ -151,6 +151,7 @@ General Style Don't set things to undefined. Reserve that value to mean "not yet set to anything." Boolean objects are verboten. - Use JSDoc +- Use switch-case statements where there are 5 or more branches running against the same variable. ECMAScript ---------- diff --git a/package.json b/package.json index 7c008d5ccc..193fb86218 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "@babel/runtime": "^7.8.3", + "await-lock": "^2.0.1", "blueimp-canvas-to-blob": "^3.5.0", "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", diff --git a/res/css/_components.scss b/res/css/_components.scss index 1faf78ebd9..3a6a3257a3 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -115,6 +115,7 @@ @import "./views/elements/_RichText.scss"; @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_RoomAliasField.scss"; +@import "./views/elements/_Slider.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; @import "./views/elements/_TextWithTooltip.scss"; @@ -162,6 +163,8 @@ @import "./views/rooms/_EditMessageComposer.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; +@import "./views/rooms/_GroupLayout.scss"; +@import "./views/rooms/_IRCLayout.scss"; @import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; @@ -204,6 +207,7 @@ @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss"; +@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss"; @import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss"; @import "./views/settings/tabs/user/_HelpUserSettingsTab.scss"; @import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss"; diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 4a78c8df92..1f8443e395 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -69,7 +69,7 @@ limitations under the License. height: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer > div { - height: $font-40px; + height: 40px; padding: 10px 0 9px 0; } @@ -116,7 +116,7 @@ limitations under the License. position: absolute; left: -15px; border-radius: 0 3px 3px 0; - top: -8px; // (16px / 2) + top: -8px; // (16px from height / 2) } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { diff --git a/res/css/structures/_TopLeftMenuButton.scss b/res/css/structures/_TopLeftMenuButton.scss index 53d44e7c24..8d2e36bcd6 100644 --- a/res/css/structures/_TopLeftMenuButton.scss +++ b/res/css/structures/_TopLeftMenuButton.scss @@ -43,7 +43,7 @@ limitations under the License. margin: 0 7px; mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); mask-repeat: no-repeat; - width: 10px; + width: $font-22px; height: 6px; background-color: $roomsublist-label-fg-color; } diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 4d831d7858..7adcc58c4e 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -21,6 +21,10 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/settings.svg'); } +.mx_UserSettingsDialog_appearanceIcon::before { + mask-image: url('$(res)/img/feather-customised/brush.svg'); +} + .mx_UserSettingsDialog_voiceIcon::before { mask-image: url('$(res)/img/feather-customised/phone.svg'); } diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss new file mode 100644 index 0000000000..58ba2813b4 --- /dev/null +++ b/res/css/views/elements/_Slider.scss @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_Slider { + position: relative; + margin: 0px; + flex-grow: 1; +} + +.mx_Slider_dotContainer { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.mx_Slider_bar { + display: flex; + box-sizing: border-box; + position: absolute; + height: 1em; + width: 100%; + padding: 0 0.5em; // half the width of a dot. + align-items: center; +} + +.mx_Slider_bar > hr { + width: 100%; + height: 0.4em; + background-color: $slider-background-color; + border: 0; +} + +.mx_Slider_selection { + display: flex; + align-items: center; + width: calc(100% - 1em); // 2 * half the width of a dot + height: 1em; + position: absolute; + pointer-events: none; +} + +.mx_Slider_selectionDot { + position: absolute; + width: 1.1em; + height: 1.1em; + background-color: $slider-selection-color; + border-radius: 50%; + box-shadow: 0 0 6px lightgrey; + z-index: 10; +} + +.mx_Slider_selection > hr { + margin: 0; + border: 0.2em solid $slider-selection-color; +} + +.mx_Slider_dot { + height: 1em; + width: 1em; + border-radius: 50%; + background-color: $slider-background-color; + z-index: 0; +} + +.mx_Slider_dotActive { + background-color: $slider-selection-color; +} + +.mx_Slider_dotValue { + display: flex; + flex-direction: column; + align-items: center; + color: $slider-background-color; +} + +// The following is a hack to center the labels without adding +// any width to the slider's dots. +.mx_Slider_labelContainer { + width: 1em; +} + +.mx_Slider_label { + position: relative; + width: fit-content; + left: -50%; +} diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 1b1bab67bc..e4743f189e 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -96,6 +96,10 @@ $AppsDrawerBodyHeight: 273px; height: $AppsDrawerBodyHeight; } +.mx_AppTile_persistedWrapper > div { + height: 100%; +} + .mx_AppTile_mini .mx_AppTile_persistedWrapper { height: 114px; } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index d4e54f4473..40a80f17bb 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -37,7 +37,6 @@ limitations under the License. } .mx_EventTile_avatar { - position: absolute; top: 14px; left: 8px; cursor: pointer; @@ -68,11 +67,9 @@ limitations under the License. display: inline-block; /* anti-zalgo, with overflow hidden */ overflow: hidden; cursor: pointer; - padding-left: 65px; /* left gutter */ padding-bottom: 0px; padding-top: 0px; margin: 0px; - line-height: $font-17px; /* the next three lines, along with overflow hidden, truncate long display names */ white-space: nowrap; text-overflow: ellipsis; @@ -104,9 +101,7 @@ limitations under the License. visibility: hidden; white-space: nowrap; left: 0px; - width: 46px; /* 8 + 30 (avatar) + 8 */ text-align: center; - position: absolute; user-select: none; } @@ -117,10 +112,7 @@ limitations under the License. .mx_EventTile_line, .mx_EventTile_reply { position: relative; padding-left: 65px; /* left gutter */ - padding-top: 3px; - padding-bottom: 3px; border-radius: 4px; - line-height: $font-22px; } .mx_RoomView_timeline_rr_enabled, @@ -151,10 +143,6 @@ limitations under the License. margin-right: 10px; } -.mx_EventTile_info .mx_EventTile_line { - padding-left: 83px; -} - /* HACK to override line-height which is already marked important elsewhere */ .mx_EventTile_bigEmoji.mx_EventTile_bigEmoji { font-size: 48px !important; @@ -171,10 +159,15 @@ limitations under the License. } // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) +// The first set is to handle the 'group layout' (default) and the second for the IRC layout .mx_EventTile_last > div > a > .mx_MessageTimestamp, .mx_EventTile:hover > div > a > .mx_MessageTimestamp, .mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp, -.mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp { +.mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile_last > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile:hover > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile.mx_EventTile_actionBarFocused > a > .mx_MessageTimestamp, +.mx_IRCLayout .mx_EventTile.focus-visible:focus-within > a > .mx_MessageTimestamp { visibility: visible; } @@ -560,84 +553,6 @@ limitations under the License. /* end of overrides */ -.mx_MatrixChat_useCompactLayout { - .mx_EventTile { - padding-top: 4px; - } - - .mx_EventTile.mx_EventTile_info { - // same as the padding for non-compact .mx_EventTile.mx_EventTile_info - padding-top: 0px; - font-size: $font-13px; - .mx_EventTile_line, .mx_EventTile_reply { - line-height: $font-20px; - } - .mx_EventTile_avatar { - top: 4px; - } - } - - .mx_EventTile .mx_SenderProfile { - font-size: $font-13px; - } - - .mx_EventTile.mx_EventTile_emote { - // add a bit more space for emotes so that avatars don't collide - padding-top: 8px; - .mx_EventTile_avatar { - top: 2px; - } - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 1px; - } - } - - .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { - padding-top: 0; - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - } - - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - - .mx_EventTile_avatar { - top: 2px; - } - - .mx_EventTile_e2eIcon { - top: 3px; - } - - .mx_EventTile_readAvatars { - top: 27px; - } - - .mx_EventTile_continuation .mx_EventTile_readAvatars, - .mx_EventTile_emote .mx_EventTile_readAvatars { - top: 5px; - } - - .mx_EventTile_info .mx_EventTile_readAvatars { - top: 4px; - } - - .mx_RoomView_MessageList h2 { - margin-top: 6px; - } - - .mx_EventTile_content .markdown-body { - p, ul, ol, dl, blockquote, pre, table { - margin-bottom: 4px; // 1/4 of the non-compact margin-bottom - } - } -} - .mx_EventTile_tileError { color: red; text-align: center; diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss new file mode 100644 index 0000000000..928ea75a79 --- /dev/null +++ b/res/css/views/rooms/_GroupLayout.scss @@ -0,0 +1,131 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +$left-gutter: 65px; + +.mx_GroupLayout { + + .mx_EventTile { + > .mx_SenderProfile { + line-height: $font-17px; + padding-left: $left-gutter; + } + + > .mx_EventTile_line { + padding-left: $left-gutter; + } + + > .mx_EventTile_avatar { + position: absolute; + } + + .mx_MessageTimestamp { + position: absolute; + width: 46px; /* 8 + 30 (avatar) + 8 */ + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 3px; + padding-bottom: 3px; + line-height: $font-22px; + } + } + + .mx_EventTile_info .mx_EventTile_line { + padding-left: calc($left-gutter + 18px); + } +} + +/* Compact layout overrides */ + +.mx_MatrixChat_useCompactLayout { + .mx_EventTile { + padding-top: 4px; + } + + .mx_EventTile.mx_EventTile_info { + // same as the padding for non-compact .mx_EventTile.mx_EventTile_info + padding-top: 0px; + font-size: $font-13px; + .mx_EventTile_line, .mx_EventTile_reply { + line-height: $font-20px; + } + .mx_EventTile_avatar { + top: 4px; + } + } + + .mx_EventTile .mx_SenderProfile { + font-size: $font-13px; + } + + .mx_EventTile.mx_EventTile_emote { + // add a bit more space for emotes so that avatars don't collide + padding-top: 8px; + .mx_EventTile_avatar { + top: 2px; + } + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 1px; + } + } + + .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { + padding-top: 0; + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + + .mx_EventTile_avatar { + top: 2px; + } + + .mx_EventTile_e2eIcon { + top: 3px; + } + + .mx_EventTile_readAvatars { + top: 27px; + } + + .mx_EventTile_continuation .mx_EventTile_readAvatars, + .mx_EventTile_emote .mx_EventTile_readAvatars { + top: 5px; + } + + .mx_EventTile_info .mx_EventTile_readAvatars { + top: 4px; + } + + .mx_RoomView_MessageList h2 { + margin-top: 6px; + } + + .mx_EventTile_content .markdown-body { + p, ul, ol, dl, blockquote, pre, table { + margin-bottom: 4px; // 1/4 of the non-compact margin-bottom + } + } +} diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss new file mode 100644 index 0000000000..5f88473c5f --- /dev/null +++ b/res/css/views/rooms/_IRCLayout.scss @@ -0,0 +1,214 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +$icon-width: 14px; +$timestamp-width: 45px; +$right-padding: 5px; +$irc-line-height: $font-18px; + +.mx_IRCLayout { + --name-width: 70px; + + line-height: $irc-line-height !important; + + .mx_EventTile { + + // timestamps are links which shouldn't be underlined + > a { + text-decoration: none; + } + + display: flex; + flex-direction: row; + align-items: flex-start; + padding-top: 0; + + > * { + margin-right: $right-padding; + } + + > .mx_EventTile_msgOption { + order: 4; + flex-shrink: 0; + } + + > .mx_SenderProfile { + order: 2; + flex-shrink: 0; + width: var(--name-width); + text-overflow: ellipsis; + text-align: right; + display: flex; + align-items: center; + overflow: visible; + justify-content: flex-end; + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding: 0; + display: flex; + flex-direction: column; + order: 3; + flex-grow: 1; + } + + > .mx_EventTile_avatar { + order: 1; + position: relative; + top: 0; + left: 0; + flex-shrink: 0; + height: $irc-line-height; + display: flex; + align-items: center; + + // Need to use important to override the js provided height and width values. + > .mx_BaseAvatar, .mx_BaseAvatar > * { + height: $font-14px !important; + width: $font-14px !important; + font-size: $font-10px !important; + line-height: $font-15px !important; + } + } + + .mx_MessageTimestamp { + font-size: $font-10px; + width: $timestamp-width; + text-align: right; + } + + .mx_EventTile_e2eIcon { + position: relative; + right: unset; + left: unset; + top: -2px; + padding: 0; + } + + .mx_EventTile_line { + .mx_EventTile_e2eIcon, + .mx_TextualEvent, + .mx_MTextBody, + .mx_ReplyThread_wrapper_empty { + display: inline-block; + } + } + + .mx_EvenTile_line .mx_MessageActionBar, + .mx_EvenTile_line .mx_ReplyThread_wrapper { + display: block; + } + + .mx_EventTile_reply { + order: 3; + } + + .mx_EditMessageComposer_buttons { + position: relative; + } + } + + .mx_EventTile_emote { + > .mx_EventTile_avatar { + margin-left: calc(var(--name-width) + $icon-width + $right-padding); + } + } + + blockquote { + margin: 0; + } + + .mx_EventListSummary { + > .mx_EventTile_line { + padding-left: calc(var(--name-width) + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding + } + + .mx_EventListSummary_avatars { + padding: 0; + margin: 0 9px 0 0; + } + } + + .mx_EventTile.mx_EventTile_info { + .mx_EventTile_avatar { + left: calc(var(--name-width) + 10px + $icon-width); + top: 0; + } + + .mx_EventTile_line { + left: calc(var(--name-width) + 10px + $icon-width); + } + + .mx_TextualEvent { + line-height: $irc-line-height; + } + } + + // Suppress highlight thing from the normal Layout. + .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, + .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line, + .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line { + padding-left: 0; + border-left: 0; + } + + .mx_SenderProfile_hover { + background-color: $primary-bg-color; + overflow: hidden; + + > span { + display: flex; + + > .mx_SenderProfile_name { + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + .mx_SenderProfile:hover { + justify-content: flex-start; + } + + .mx_SenderProfile_hover:hover { + overflow: visible; + width: max(auto, 100%); + z-index: 10; + } + + .mx_ReplyThread { + margin: 0; + .mx_SenderProfile { + width: unset; + max-width: var(--name-width); + } + } + + .mx_ProfileResizer { + position: absolute; + height: 100%; + width: 15px; + left: calc(80px + var(--name-width)); + cursor: col-resize; + z-index: 100; + } + + // Need to use important to override the js provided height and width values. + .mx_Flair > img { + height: $font-14px !important; + width: $font-14px !important; + } +} diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 5bc7d5624d..759dce5afa 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. flex-direction: row; align-items: center; cursor: pointer; - height: $font-34px; + height: 32px; margin: 0; padding: 0 8px 0 10px; position: relative; @@ -81,6 +81,7 @@ limitations under the License. .mx_RoomTile_avatar_container { position: relative; + display: flex; } .mx_RoomTile_avatar { diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss new file mode 100644 index 0000000000..e82ae3c575 --- /dev/null +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -0,0 +1,45 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_AppearanceUserSettingsTab_fontSlider, +.mx_AppearanceUserSettingsTab_themeSection .mx_Field, +.mx_AppearanceUserSettingsTab_fontScaling .mx_Field { + @mixin mx_Settings_fullWidthField; +} + +.mx_AppearanceUserSettingsTab_fontSlider { + display: flex; + flex-direction: row; + align-items: center; + padding: 15px; + background: $font-slider-bg-color; + border-radius: 10px; + font-size: 10px; + margin-top: 24px; + margin-bottom: 24px; +} + +.mx_AppearanceUserSettingsTab_fontSlider_smallText { + font-size: 15px; + padding-right: 20px; + padding-left: 5px; +} + +.mx_AppearanceUserSettingsTab_fontSlider_largeText { + font-size: 18px; + padding-left: 20px; + padding-right: 5px; +} diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 95a46b51ee..6c9b89cf5a 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_GeneralUserSettingsTab_changePassword .mx_Field, -.mx_GeneralUserSettingsTab_themeSection .mx_Field { +.mx_GeneralUserSettingsTab_changePassword .mx_Field { @mixin mx_Settings_fullWidthField; } diff --git a/res/img/feather-customised/brush.svg b/res/img/feather-customised/brush.svg new file mode 100644 index 0000000000..d7f2738629 --- /dev/null +++ b/res/img/feather-customised/brush.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 6224c0820f..9fb36ef1a3 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -180,6 +180,9 @@ $breadcrumb-placeholder-bg-color: #272c35; $user-tile-hover-bg-color: $header-panel-bg-color; +// FontSlider colors +$font-slider-bg-color: $room-highlight-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index f5f3013354..78fe2a74c5 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -262,6 +262,10 @@ $togglesw-off-color: #c1c9d6; $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; +// Slider +$slider-selection-color: $accent-color; +$slider-background-color: #c1c9d6; + $progressbar-color: #000; $room-warning-bg-color: $yellow-background; @@ -302,6 +306,9 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; +// FontSlider colors +$font-slider-bg-color: rgba($input-darker-bg-color, 0.2); + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/src/FontWatcher.js b/src/FontWatcher.js new file mode 100644 index 0000000000..006df202ad --- /dev/null +++ b/src/FontWatcher.js @@ -0,0 +1,51 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import dis from './dispatcher/dispatcher'; +import SettingsStore, {SettingLevel} from './settings/SettingsStore'; + +export class FontWatcher { + static MIN_SIZE = 13; + static MAX_SIZE = 20; + + constructor() { + this._dispatcherRef = null; + } + + start() { + this._setRootFontSize(SettingsStore.getValue("fontSize")); + this._dispatcherRef = dis.register(this._onAction); + } + + stop() { + dis.unregister(this._dispatcherRef); + } + + _onAction = (payload) => { + if (payload.action === 'update-font-size') { + this._setRootFontSize(payload.size); + } + }; + + _setRootFontSize = (size) => { + const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE); + + if (fontSize != size) { + SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); + } + document.querySelector(":root").style.fontSize = fontSize + "px"; + }; +} diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts index eb9831ec47..e15e1b0c65 100644 --- a/src/actions/RoomListActions.ts +++ b/src/actions/RoomListActions.ts @@ -16,7 +16,7 @@ limitations under the License. */ import { asyncAction } from './actionCreators'; -import RoomListStore, { TAG_DM } from '../stores/RoomListStore'; +import { TAG_DM } from '../stores/RoomListStore'; import Modal from '../Modal'; import * as Rooms from '../Rooms'; import { _t } from '../languageHandler'; @@ -24,6 +24,7 @@ import * as sdk from '../index'; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import { AsyncActionPayload } from "../dispatcher/payloads"; +import { RoomListStoreTempProxy } from "../stores/room-list/RoomListStoreTempProxy"; export default class RoomListActions { /** @@ -51,7 +52,7 @@ export default class RoomListActions { // Is the tag ordered manually? if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { - const lists = RoomListStore.getRoomLists(); + const lists = RoomListStoreTempProxy.getRoomLists(); const newList = [...lists[newTag]]; newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index adba858fa3..a1b4f49c56 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -26,6 +26,7 @@ import * as VectorConferenceHandler from '../../VectorConferenceHandler'; import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import Analytics from "../../Analytics"; +import RoomList2 from "../views/rooms/RoomList2"; const LeftPanel = createReactClass({ @@ -273,6 +274,29 @@ const LeftPanel = createReactClass({ breadcrumbs = (); } + let roomList = null; + if (SettingsStore.isFeatureEnabled("feature_new_room_list")) { + roomList = ; + } else { + roomList = ; + } + return (
{ tagPanelContainer } @@ -284,15 +308,7 @@ const LeftPanel = createReactClass({ { exploreButton } { searchBox }
- + {roomList} ); diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index e2aa523b8c..148d10fe8d 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -31,7 +31,6 @@ import dis from '../../dispatcher/dispatcher'; import sessionStore from '../../stores/SessionStore'; import {MatrixClientPeg, MatrixClientCreds} from '../../MatrixClientPeg'; import SettingsStore from "../../settings/SettingsStore"; -import RoomListStore from "../../stores/RoomListStore"; import TagOrderActions from '../../actions/TagOrderActions'; import RoomListActions from '../../actions/RoomListActions'; @@ -42,6 +41,8 @@ import * as KeyboardShortcuts from "../../accessibility/KeyboardShortcuts"; import HomePage from "./HomePage"; import ResizeNotifier from "../../utils/ResizeNotifier"; import PlatformPeg from "../../PlatformPeg"; +import { RoomListStoreTempProxy } from "../../stores/room-list/RoomListStoreTempProxy"; +import { DefaultTagID } from "../../stores/room-list/models"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. // NB. this is just for server notices rather than pinned messages in general. @@ -297,18 +298,18 @@ class LoggedInView extends React.PureComponent { }; onRoomStateEvents = (ev, state) => { - const roomLists = RoomListStore.getRoomLists(); - if (roomLists['m.server_notice'] && roomLists['m.server_notice'].some(r => r.roomId === ev.getRoomId())) { + const roomLists = RoomListStoreTempProxy.getRoomLists(); + if (roomLists[DefaultTagID.ServerNotice] && roomLists[DefaultTagID.ServerNotice].some(r => r.roomId === ev.getRoomId())) { this._updateServerNoticeEvents(); } }; _updateServerNoticeEvents = async () => { - const roomLists = RoomListStore.getRoomLists(); - if (!roomLists['m.server_notice']) return []; + const roomLists = RoomListStoreTempProxy.getRoomLists(); + if (!roomLists[DefaultTagID.ServerNotice]) return []; const pinnedEvents = []; - for (const room of roomLists['m.server_notice']) { + for (const room of roomLists[DefaultTagID.ServerNotice]) { const pinStateEvent = room.currentState.getStateEvents("m.room.pinned_events", ""); if (!pinStateEvent || !pinStateEvent.getContent().pinned) continue; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 670a017934..48dc72f4fa 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -60,6 +60,7 @@ import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDisco import DMRoomMap from '../../utils/DMRoomMap'; import { countRoomsWithNotif } from '../../RoomNotifs'; import { ThemeWatcher } from "../../theme"; +import { FontWatcher } from '../../FontWatcher'; import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer, IDeferred } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; @@ -216,6 +217,7 @@ export default class MatrixChat extends React.PureComponent { private readonly loggedInView: React.RefObject; private readonly dispatcherRef: any; private readonly themeWatcher: ThemeWatcher; + private readonly fontWatcher: FontWatcher; constructor(props, context) { super(props, context); @@ -283,8 +285,11 @@ export default class MatrixChat extends React.PureComponent { this.accountPasswordTimer = null; this.dispatcherRef = dis.register(this.onAction); + this.themeWatcher = new ThemeWatcher(); + this.fontWatcher = new FontWatcher(); this.themeWatcher.start(); + this.fontWatcher.start(); this.focusComposer = false; @@ -367,6 +372,7 @@ export default class MatrixChat extends React.PureComponent { Lifecycle.stopMatrixClient(); dis.unregister(this.dispatcherRef); this.themeWatcher.stop(); + this.fontWatcher.stop(); window.removeEventListener('resize', this.handleResize); this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize); diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6fbfdb504b..93e4668f66 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -29,6 +29,7 @@ import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; import {textForEvent} from "../../TextForEvent"; +import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -109,14 +110,16 @@ export default class MessagePanel extends React.Component { showReactions: PropTypes.bool, }; - constructor() { - super(); + // Force props to be loaded for useIRCLayout + constructor(props) { + super(props); this.state = { // previous positions the read marker has been in, so we can // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), + useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -169,6 +172,8 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); + + this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); } componentDidMount() { @@ -178,6 +183,7 @@ export default class MessagePanel extends React.Component { componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); + SettingsStore.unwatchSetting(this._layoutWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -196,6 +202,17 @@ export default class MessagePanel extends React.Component { }); }; + onLayoutChange = () => { + this.setState({ + useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), + }); + } + + useIRCLayout(ircLayoutSelected) { + // if room is null we are not in a normal room list + return ircLayoutSelected && this.props.room; + } + /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -597,6 +614,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} + useIRCLayout={this.state.useIRCLayout} /> , @@ -779,6 +797,8 @@ export default class MessagePanel extends React.Component { this.props.className, { "mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps, + "mx_IRCLayout": this.state.useIRCLayout, + "mx_GroupLayout": !this.state.useIRCLayout, }, ); @@ -792,6 +812,15 @@ export default class MessagePanel extends React.Component { ); } + let ircResizer = null; + if (this.state.useIRCLayout) { + ircResizer = ; + } + return ( { topSpinner } { this._getEventTiles() } diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index d1abacd2d8..090f3de22a 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,7 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; -import toRem from "../../utils/rem"; +import {toPx} from "../../utils/units"; // turn this on for drop & drag console debugging galore const debug = false; @@ -420,7 +420,7 @@ export default class RoomSubList extends React.PureComponent { setHeight = (height) => { if (this._subList.current) { - this._subList.current.style.height = toRem(height); + this._subList.current.style.height = toPx(height); } this._updateLazyRenderHeight(height); }; diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 4f44c1a169..cb0114b243 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -144,6 +144,11 @@ export default createReactClass({ /* resizeNotifier: ResizeNotifier to know when middle column has changed size */ resizeNotifier: PropTypes.object, + + /* fixedChildren: allows for children to be passed which are rendered outside + * of the wrapper + */ + fixedChildren: PropTypes.node, }, getDefaultProps: function() { @@ -881,6 +886,7 @@ export default createReactClass({ return ( + { this.props.fixedChildren }
    { this.props.children } diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 81fbc08c0f..704e6438c8 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,7 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import toRem from "../../../utils/rem"; +import {toPx} from "../../../utils/units"; export default createReactClass({ displayName: 'BaseAvatar', @@ -166,9 +166,9 @@ export default createReactClass({ const textNode = (