diff --git a/package.json b/package.json index 93d59a4fa6..966119d1eb 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "react-dom": "^16.9.0", "react-focus-lock": "^2.2.1", "react-resizable": "^1.10.1", + "react-transition-group": "^4.4.1", "resize-observer-polyfill": "^1.5.0", "sanitize-html": "^1.18.4", "text-encoding-utf-8": "^1.0.1", @@ -126,6 +127,7 @@ "@types/qrcode": "^1.3.4", "@types/react": "^16.9", "@types/react-dom": "^16.9.8", + "@types/react-transition-group": "^4.4.0", "@types/zxcvbn": "^4.4.0", "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", diff --git a/res/css/_common.scss b/res/css/_common.scss index ebeeb381e6..e83c6aaeda 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,7 +19,7 @@ limitations under the License. @import "./_font-sizes.scss"; :root { - font-size: 15px; + font-size: 10px; } html { @@ -581,3 +581,118 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { // So it fits in the space provided by the page max-width: 120px; } + +// A context menu that largely fits the | [icon] [label] | format. +.mx_IconizedContextMenu { + // Put 20px of padding around the whole menu. We do this instead of a + // simple `padding: 20px` rule so the horizontal rules added by the + // optionLists is rendered correctly (full width). + > * { + padding-left: 20px; + padding-right: 20px; + + &:first-child { + padding-top: 20px; + } + + &:last-child { + padding-bottom: 20px; + } + } + + .mx_IconizedContextMenu_optionList { + // the notFirst class is for cases where the optionList might be under a header of sorts. + &:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst { + margin-top: 20px; + + // This is a bit of a hack when we could just use a simple border-top property, + // however we have a (kinda) good reason for doing it this way: we need opacity. + // To get the right color, we need an opacity modifier which means we have to work + // around the problem. PostCSS doesn't support the opacity() function, and if we + // use something like postcss-functions we quickly run into an issue where the + // function we would define gets passed a CSS variable for custom themes, which + // can't be converted easily even when considering https://stackoverflow.com/a/41265350/7037379 + // + // Therefore, we just hack in a line and border the thing ourselves + &::before { + border-top: 1px solid $primary-fg-color; + opacity: 0.1; + content: ''; + + // Counteract the padding problems (width: 100% ignores the 40px padding, + // unless we position it absolutely then it does the right thing). + width: 100%; + position: absolute; + left: 0; + } + } + + ul { + list-style: none; + margin: 0; + padding: 0; + + li { + margin: 0; + padding: 20px 0 0; + + .mx_AccessibleButton { + text-decoration: none; + color: $primary-fg-color; + font-size: $font-15px; + line-height: $font-24px; + + // Create a flexbox to more easily define the list items + display: flex; + align-items: center; + + img, .mx_IconizedContextMenu_icon { // icons + width: 16px; + min-width: 16px; + max-width: 16px; + } + + span:last-child { // labels + padding-left: 14px; + width: 100%; + flex: 1; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } + } + + &.mx_IconizedContextMenu_compact { + > * { + padding-left: 11px; + padding-right: 16px; + + &:first-child { + padding-top: 13px; + } + + &:last-child { + padding-bottom: 13px; + } + } + + .mx_IconizedContextMenu_optionList { + &:nth-child(n + 2), .mx_IconizedContextMenu_optionList_notFirst { + margin-top: 10px; + + li:first-child { + padding-top: 10px; + } + } + + li:first-child { + padding-top: 0; + } + } + } +} diff --git a/res/css/_components.scss b/res/css/_components.scss index b1a05b1389..31f319e76f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -12,12 +12,14 @@ @import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; +@import "./structures/_LeftPanel2.scss"; @import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; @import "./structures/_NotificationPanel.scss"; @import "./structures/_RightPanel.scss"; @import "./structures/_RoomDirectory.scss"; +@import "./structures/_RoomSearch.scss"; @import "./structures/_RoomStatusBar.scss"; @import "./structures/_RoomSubList.scss"; @import "./structures/_RoomView.scss"; @@ -28,6 +30,7 @@ @import "./structures/_ToastContainer.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; +@import "./structures/_UserMenuButton.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_CompleteSecurity.scss"; @import "./structures/auth/_Login.scss"; @@ -170,18 +173,22 @@ @import "./views/rooms/_MemberList.scss"; @import "./views/rooms/_MessageComposer.scss"; @import "./views/rooms/_MessageComposerFormatBar.scss"; +@import "./views/rooms/_NotificationBadge.scss"; @import "./views/rooms/_PinnedEventTile.scss"; @import "./views/rooms/_PinnedEventsPanel.scss"; @import "./views/rooms/_PresenceLabel.scss"; @import "./views/rooms/_ReplyPreview.scss"; @import "./views/rooms/_RoomBreadcrumbs.scss"; +@import "./views/rooms/_RoomBreadcrumbs2.scss"; @import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; @import "./views/rooms/_RoomList.scss"; +@import "./views/rooms/_RoomList2.scss"; @import "./views/rooms/_RoomPreviewBar.scss"; @import "./views/rooms/_RoomRecoveryReminder.scss"; @import "./views/rooms/_RoomSublist2.scss"; @import "./views/rooms/_RoomTile.scss"; +@import "./views/rooms/_RoomTile2.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SendMessageComposer.scss"; diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index 2d7ab67e40..5b876ab11d 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -14,59 +14,59 @@ See the License for the specific language governing permissions and limitations under the License. */ -$font-1px: 0.067rem; -$font-1-5px: 0.100rem; -$font-2px: 0.133rem; -$font-3px: 0.200rem; -$font-4px: 0.267rem; -$font-5px: 0.333rem; -$font-6px: 0.400rem; -$font-7px: 0.467rem; -$font-8px: 0.533rem; -$font-9px: 0.600rem; -$font-10px: 0.667rem; -$font-10-4px: 0.693rem; -$font-11px: 0.733rem; -$font-12px: 0.800rem; -$font-13px: 0.867rem; -$font-14px: 0.933rem; -$font-15px: 1.000rem; -$font-16px: 1.067rem; -$font-17px: 1.133rem; -$font-18px: 1.200rem; -$font-19px: 1.267rem; -$font-20px: 1.3333333rem; -$font-21px: 1.400rem; -$font-22px: 1.467rem; -$font-23px: 1.533rem; -$font-24px: 1.600rem; -$font-25px: 1.667rem; -$font-26px: 1.733rem; -$font-27px: 1.800rem; -$font-28px: 1.867rem; -$font-29px: 1.933rem; -$font-30px: 2.000rem; -$font-31px: 2.067rem; -$font-32px: 2.133rem; -$font-33px: 2.200rem; -$font-34px: 2.267rem; -$font-35px: 2.333rem; -$font-36px: 2.400rem; -$font-37px: 2.467rem; -$font-38px: 2.533rem; -$font-39px: 2.600rem; -$font-40px: 2.667rem; -$font-41px: 2.733rem; -$font-42px: 2.800rem; -$font-43px: 2.867rem; -$font-44px: 2.933rem; -$font-45px: 3.000rem; -$font-46px: 3.067rem; -$font-47px: 3.133rem; -$font-48px: 3.200rem; -$font-49px: 3.267rem; -$font-50px: 3.333rem; -$font-51px: 3.400rem; -$font-52px: 3.467rem; -$font-88px: 5.887rem; -$font-400px: 26.667rem; +$font-1px: 0.1rem; +$font-1-5px: 0.15rem; +$font-2px: 0.2rem; +$font-3px: 0.3rem; +$font-4px: 0.4rem; +$font-5px: 0.5rem; +$font-6px: 0.6rem; +$font-7px: 0.7rem; +$font-8px: 0.8rem; +$font-9px: 0.9rem; +$font-10px: 1.0rem; +$font-10-4px: 1.04rem; +$font-11px: 1.1rem; +$font-12px: 1.2rem; +$font-13px: 1.3rem; +$font-14px: 1.4rem; +$font-15px: 1.5rem; +$font-16px: 1.6rem; +$font-17px: 1.7rem; +$font-18px: 1.8rem; +$font-19px: 1.9rem; +$font-20px: 2.0rem; +$font-21px: 2.1rem; +$font-22px: 2.2rem; +$font-23px: 2.3rem; +$font-24px: 2.4rem; +$font-25px: 2.5rem; +$font-26px: 2.6rem; +$font-27px: 2.7rem; +$font-28px: 2.8rem; +$font-29px: 2.9rem; +$font-30px: 3.0rem; +$font-31px: 3.1rem; +$font-32px: 3.2rem; +$font-33px: 3.3rem; +$font-34px: 3.4rem; +$font-35px: 3.5rem; +$font-36px: 3.6rem; +$font-37px: 3.7rem; +$font-38px: 3.8rem; +$font-39px: 3.9rem; +$font-40px: 4.0rem; +$font-41px: 4.1rem; +$font-42px: 4.2rem; +$font-43px: 4.3rem; +$font-44px: 4.4rem; +$font-45px: 4.5rem; +$font-46px: 4.6rem; +$font-47px: 4.7rem; +$font-48px: 4.8rem; +$font-49px: 4.9rem; +$font-50px: 5.0rem; +$font-51px: 5.1rem; +$font-52px: 5.2rem; +$font-88px: 8.8rem; +$font-400px: 40rem; diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 899824bc57..35d9f0e7da 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -23,14 +23,6 @@ limitations under the License. flex: 0 0 auto; } -// TODO: Remove temporary indicator of new room list implementation. -// This border is meant to visually distinguish between the two components when the -// user has turned on the new room list implementation, at least until the designs -// themselves give it away. -.mx_LeftPanel2 .mx_LeftPanel { - border-left: 5px #e26dff solid; -} - .mx_LeftPanel_container.collapsed { min-width: unset; /* Collapsed LeftPanel 50px */ diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss new file mode 100644 index 0000000000..bd2a3ba96e --- /dev/null +++ b/res/css/structures/_LeftPanel2.scss @@ -0,0 +1,141 @@ +/* +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. +*/ + +// TODO: Rename to mx_LeftPanel during replacement of old component + +// TODO: Put these variables in the right place, or namespace them. +$tagPanelWidth: 70px; +$roomListMinimizedWidth: 50px; + +.mx_LeftPanel2 { + background-color: $header-panel-bg-color; + min-width: 260px; + max-width: 50%; + + // Create a row-based flexbox for the TagPanel and the room list + display: flex; + + .mx_LeftPanel2_tagPanelContainer { + flex-grow: 0; + flex-shrink: 0; + flex-basis: $tagPanelWidth; + height: 100%; + + // Create another flexbox so the TagPanel fills the container + display: flex; + + // TagPanel handles its own CSS + } + + // Note: The 'room list' in this context is actually everything that isn't the tag + // panel, such as the menu options, breadcrumbs, filtering, etc + .mx_LeftPanel2_roomListContainer { + width: calc(100% - $tagPanelWidth); + + // Create another flexbox (this time a column) for the room list components + display: flex; + flex-direction: column; + + .mx_LeftPanel2_userHeader { + padding: 14px 12px 20px; // 14px top, 12px sides, 20px bottom + + // Create another flexbox column for the rows to stack within + display: flex; + flex-direction: column; + + // There's 2 rows when breadcrumbs are present: the top bit and the breadcrumbs + .mx_LeftPanel2_headerRow { + // Create yet another flexbox, this time within the row, to ensure items stay + // aligned correctly. This is also a row-based flexbox. + display: flex; + align-items: center; + } + + .mx_LeftPanel2_userAvatarContainer { + position: relative; // to make default avatars work + margin-right: 8px; + } + + .mx_LeftPanel2_userName { + font-weight: 600; + font-size: $font-15px; + line-height: $font-20px; + flex: 1; + } + + .mx_LeftPanel2_headerButtons { + // No special styles: the rest of the layout happens to make it work. + } + + .mx_LeftPanel2_breadcrumbsContainer { + width: 100%; + overflow: hidden; + margin-top: 8px; + } + } + + .mx_LeftPanel2_filterContainer { + margin-left: 12px; + margin-right: 12px; + + // Create a flexbox to organize the inputs + display: flex; + align-items: center; + + .mx_RoomSearch_expanded + .mx_LeftPanel2_exploreButton { + // Cheaty way to return the occupied space to the filter input + margin: 0; + width: 0; + + // Don't forget to hide the masked ::before icon + visibility: hidden; + } + + .mx_LeftPanel2_exploreButton { + width: 28px; + height: 28px; + border-radius: 20px; + background-color: #fff; // TODO: Variable and theme + position: relative; + margin-left: 8px; + + &::before { + content: ''; + position: absolute; + top: 6px; + left: 6px; + width: 16px; + height: 16px; + mask-image: url('$(res)/img/feather-customised/compass.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } + } + + .mx_LeftPanel2_actualRoomListContainer { + flex-grow: 1; // fill the available space + overflow-y: auto; + width: 100%; + max-width: 100%; + + // Create a flexbox to trick the layout engine + display: flex; + } + } +} diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index 05c703ab6d..08ed9e5559 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -66,7 +66,7 @@ limitations under the License. } /* not the left panel, and not the resize handle, so the roomview/groupview/... */ -.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_ResizeHandle) { +.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_LeftPanel2):not(.mx_ResizeHandle) { background-color: $primary-bg-color; flex: 1 1 0; diff --git a/res/css/structures/_RoomSearch.scss b/res/css/structures/_RoomSearch.scss new file mode 100644 index 0000000000..d078031090 --- /dev/null +++ b/res/css/structures/_RoomSearch.scss @@ -0,0 +1,70 @@ +/* +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. +*/ + +// Note: this component expects to be contained within a flexbox +.mx_RoomSearch { + flex: 1; + border-radius: 20px; + background-color: #fff; // TODO: Variable & theme + height: 26px; + padding: 2px; + + // Create a flexbox for the icons (easier to manage) + display: flex; + align-items: center; + + .mx_RoomSearch_icon { + width: 16px; + height: 16px; + mask: url('$(res)/img/feather-customised/search-input.svg'); + mask-repeat: no-repeat; + background: $primary-fg-color; + margin-left: 7px; + } + + .mx_RoomSearch_input { + border: none !important; // !important to override default app-wide styles + flex: 1 !important; // !important to override default app-wide styles + color: $primary-fg-color !important; // !important to override default app-wide styles + padding: 0; + height: 100%; + width: 100%; + font-size: $font-12px; + line-height: $font-16px; + + &:not(.mx_RoomSearch_inputExpanded)::placeholder { + color: $primary-fg-color !important; // !important to override default app-wide styles + } + } + + &.mx_RoomSearch_expanded { + .mx_RoomSearch_clearButton { + width: 16px; + height: 16px; + mask-image: url('$(res)/img/feather-customised/x.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + margin-right: 8px; + } + } + + .mx_RoomSearch_clearButton { + width: 0; + height: 0; + } +} diff --git a/res/css/structures/_UserMenuButton.scss b/res/css/structures/_UserMenuButton.scss new file mode 100644 index 0000000000..a173e468e1 --- /dev/null +++ b/res/css/structures/_UserMenuButton.scss @@ -0,0 +1,82 @@ +/* +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_UserMenuButton { + // No special styles on the button itself +} + +.mx_UserMenuButton_contextMenu { + width: 231px; + + .mx_UserMenuButton_contextMenu_header { + // Create a flexbox to organize the header a bit easier + display: flex; + align-items: center; + + &:nth-child(n + 1) { + // The first header will have appropriate padding, subsequent ones need a margin. + margin-top: 10px; + } + + .mx_UserMenuButton_contextMenu_name { + // Create another flexbox of columns to handle large user IDs + display: flex; + flex-direction: column; + + // fit the container + flex: 1; + width: calc(100% - 40px); // 40px = 32px theme button + 8px margin to theme button + + * { + // Automatically grow all subelements to fit the container + flex: 1; + width: 100%; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .mx_UserMenuButton_contextMenu_displayName { + font-weight: bold; + font-size: $font-15px; + line-height: $font-20px; + } + + .mx_UserMenuButton_contextMenu_userId { + font-size: $font-15px; + line-height: $font-24px; + } + } + + .mx_UserMenuButton_contextMenu_themeButton { + min-width: 32px; + max-width: 32px; + width: 32px; + height: 32px; + margin-left: 8px; + border-radius: 32px; + background-color: $theme-button-bg-color; + cursor: pointer; + + // to make alignment easier, create flexbox for the image + display: flex; + align-items: center; + justify-content: center; + } + } +} diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss index 14081f1e99..df0b8c6d94 100644 --- a/res/css/views/elements/_StyledCheckbox.scss +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -24,7 +24,7 @@ limitations under the License. align-items: flex-start; input[type=checkbox] { - display: none; + appearance: none; & + label { display: flex; @@ -48,6 +48,8 @@ limitations under the License. border-radius: $border-radius; img { + display: none; + height: 100%; width: 100%; filter: invert(100%); @@ -57,6 +59,10 @@ limitations under the License. &:checked + label > .mx_Checkbox_background { background: $accent-color; border-color: $accent-color; + + img { + display: block; + } } & + label > *:not(.mx_Checkbox_background) { diff --git a/res/css/views/elements/_StyledRadioButton.scss b/res/css/views/elements/_StyledRadioButton.scss index 03a4cf88eb..a3ae823079 100644 --- a/res/css/views/elements/_StyledRadioButton.scss +++ b/res/css/views/elements/_StyledRadioButton.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, @@ -23,8 +23,6 @@ limitations under the License. $radio-circle-color: $muted-fg-color; $active-radio-circle-color: $accent-color; - // We need to make this element positioned - // so that the radio circle can be absolute position: relative; display: flex; @@ -35,18 +33,28 @@ limitations under the License. flex-grow: 1; display: flex; - justify-content: center; + + margin-left: 8px; + margin-right: 8px; + } + + .mx_RadioButton_spacer { + flex-shrink: 0; + flex-grow: 0; + + height: $font-16px; + width: $font-16px; } > input[type=radio] { // Remove the OS's representation margin: 0; padding: 0; - display: none; + appearance: none; + div { - // Necessary to center the following span - position: absolute; + flex-shrink: 0; + flex-grow: 0; display: flex; align-items: center; @@ -72,8 +80,10 @@ limitations under the License. > input[type=radio]:checked { + div { + border-color: $active-radio-circle-color; + > div { - background: $radio-circle-color; + background: $active-radio-circle-color; } } } diff --git a/res/css/views/rooms/_NotificationBadge.scss b/res/css/views/rooms/_NotificationBadge.scss new file mode 100644 index 0000000000..9f9698bac5 --- /dev/null +++ b/res/css/views/rooms/_NotificationBadge.scss @@ -0,0 +1,70 @@ +/* +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_NotificationBadge { + &:not(.mx_NotificationBadge_visible) { + display: none; + } + + // Badges are structured a bit weirdly to work around issues with non-monospace + // font styles. The badge pill is actually a background div and the count floats + // within that. For example: + // + // ( 99+ ) <-- Rounded pill is a _bg class. + // ^- The count is an element floating within that. + + &.mx_NotificationBadge_visible { + background-color: $roomtile2-badge-color; + + // Create a flexbox to order the count a bit easier + display: flex; + align-items: center; + justify-content: center; + + &.mx_NotificationBadge_highlighted { + // TODO: Use a more specific variable + background-color: $warning-color; + } + + // These are the 3 background types + + &.mx_NotificationBadge_dot { + width: 6px; + height: 6px; + border-radius: 6px; + } + + &.mx_NotificationBadge_2char { + width: 16px; + height: 16px; + border-radius: 16px; + } + + &.mx_NotificationBadge_3char { + width: 26px; + height: 16px; + border-radius: 16px; + } + + // The following is the floating badge + + .mx_NotificationBadge_count { + font-size: $font-10px; + line-height: $font-14px; + color: #fff; // TODO: Variable + } + } +} diff --git a/res/css/views/rooms/_RoomBreadcrumbs2.scss b/res/css/views/rooms/_RoomBreadcrumbs2.scss new file mode 100644 index 0000000000..ac5a9fc34e --- /dev/null +++ b/res/css/views/rooms/_RoomBreadcrumbs2.scss @@ -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. +*/ + +.mx_RoomBreadcrumbs2 { + width: 100%; + + // Create a flexbox for the crumbs + display: flex; + flex-direction: row; + align-items: flex-start; + + .mx_RoomBreadcrumbs2_crumb { + margin-right: 8px; + width: 32px; + } + + // These classes come from the CSSTransition component. There's many more classes we + // could care about, but this is all we worried about for now. The animation works by + // first triggering the enter state with the newest breadcrumb off screen (-40px) then + // sliding it into view. + &.mx_RoomBreadcrumbs2-enter { + margin-left: -40px; // 32px for the avatar, 8px for the margin + } + &.mx_RoomBreadcrumbs2-enter-active { + margin-left: 0; + + // Timing function is as-requested by design. + // NOTE: The transition time MUST match the value passed to CSSTransition! + transition: margin-left 640ms cubic-bezier(0.66, 0.02, 0.36, 1); + } + + .mx_RoomBreadcrumbs2_placeholder { + font-weight: 600; + font-size: $font-14px; + line-height: 32px; // specifically to match the height this is not scaled + height: 32px; + } +} diff --git a/res/css/views/rooms/_RoomList2.scss b/res/css/views/rooms/_RoomList2.scss new file mode 100644 index 0000000000..5b78020626 --- /dev/null +++ b/res/css/views/rooms/_RoomList2.scss @@ -0,0 +1,27 @@ +/* +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. +*/ + +// TODO: Rename to mx_RoomList during replacement of old component + +.mx_RoomList2 { + width: calc(100% - 16px); // 16px of artificial right-side margin (8px is overflowed from the sublists) + + // Create a column-based flexbox for the sublists. That's pretty much all we have to + // worry about in this stylesheet. + display: flex; + flex-direction: column; + flex-wrap: nowrap; // let the column overflow +} diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 9ab1785566..ed17c071b6 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -14,8 +14,232 @@ See the License for the specific language governing permissions and limitations under the License. */ -@import "../../../../node_modules/react-resizable/css/styles.css"; +// TODO: Rename to mx_RoomSublist during replacement of old component -.mx_RoomList2 .mx_RoomSubList_labelContainer { - z-index: 12; +.mx_RoomSublist2 { + // The sublist is a column of rows, essentially + display: flex; + flex-direction: column; + + margin-left: 8px; + margin-top: 12px; + margin-bottom: 12px; + width: 100%; + + .mx_RoomSublist2_headerContainer { + // Create a flexbox to make ordering easy + display: flex; + align-items: center; + padding-bottom: 8px; + height: 24px; + + .mx_RoomSublist2_badgeContainer { + opacity: 0.8; + width: 16px; + margin-right: 5px; // aligns with the room tile's badge + + // Create another flexbox row because it's super easy to position the badge this way. + display: flex; + align-items: center; + justify-content: center; + } + + // Both of these buttons are hidden by default until the list is hovered + .mx_RoomSublist2_auxButton, + .mx_RoomSublist2_menuButton { + width: 0; + margin: 0; + visibility: hidden; + position: relative; + + &::before { + content: ''; + width: 16px; + height: 16px; + position: absolute; + top: 4px; + left: 4px; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $muted-fg-color; + } + } + + .mx_RoomSublist2_auxButton::before { + mask-image: url('$(res)/img/feather-customised/plus.svg'); + } + + .mx_RoomSublist2_menuButton::before { + mask-image: url('$(res)/img/feather-customised/more-horizontal.svg'); + } + + .mx_RoomSublist2_headerText { + text-transform: uppercase; + opacity: 0.5; + line-height: $font-16px; + font-size: $font-12px; + + flex: 1; + max-width: calc(100% - 16px); // 16px is the badge width + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + + .mx_RoomSublist2_resizeBox { + margin-bottom: 4px; // for the resize handle + position: relative; + + // Create another flexbox column for the tiles + display: flex; + flex-direction: column; + overflow: hidden; + + .mx_RoomSublist2_showMoreButton { + cursor: pointer; + font-size: $font-13px; + line-height: $font-18px; + color: $roomtile2-preview-color; + + // This is the same color as the left panel background because it needs + // to occlude the lastmost tile in the list. + background-color: $header-panel-bg-color; + + // Update the render() function for RoomSublist2 if these change + // Update the ListLayout class for minVisibleTiles if these change. + // + // At 24px high and 8px padding on the top this equates to 0.65 of + // a tile due to how the padding calculations work. + height: 24px; + padding-top: 8px; + + // We force this to the bottom so it will overlap rooms as needed. + // We account for the space it takes up (24px) in the code through padding. + position: absolute; + bottom: 4px; // the height of the resize handle + left: 0; + right: 0; + + // We create a flexbox to cheat at alignment + display: flex; + align-items: center; + + .mx_RoomSublist2_showMoreButtonChevron { + position: relative; + width: 16px; + height: 16px; + margin-left: 12px; + margin-right: 18px; + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $roomtile2-preview-color; + } + } + + // Class name comes from the ResizableBox component + // The hover state needs to use the whole sublist, not just the resizable box, + // so that selector is below and one level higher. + .react-resizable-handle { + cursor: ns-resize; + border-radius: 2px; + + // This is positioned directly below the 'show more' button. + position: absolute; + bottom: 0; + left: 0; + right: 0; + + // This is to visually align the bar in the list. Should be 12px from + // either side of the list. We define this after the positioning to + // trick the browser. + margin-left: 4px; + margin-right: 4px; + } + } + + // The aforementioned selector for the hover state. + &:hover, &.mx_RoomSublist2_hasMenuOpen { + .react-resizable-handle { + opacity: 0.2; + + // Update the render() function for RoomSublist2 if this changes + border: 2px solid $primary-fg-color; + } + + .mx_RoomSublist2_headerContainer { + // If the header doesn't have an aux button we still need to hide the badge for + // the menu button. + .mx_RoomSublist2_badgeContainer { + // Completely hide the badge + width: 0; + margin: 0; + visibility: hidden; + } + + &:not(.mx_RoomSublist2_headerContainer_withAux) { + // The menu button will be the rightmost button, so make it correctly aligned. + .mx_RoomSublist2_menuButton { + margin-right: 1px; // line it up with the badges on the room tiles + } + } + + // Both of these buttons have circled backgrounds and are visible at this point, + // so make them so. + .mx_RoomSublist2_auxButton, + .mx_RoomSublist2_menuButton { + width: 24px; + height: 24px; + border-radius: 32px; + margin-left: 16px; + background-color: #fff; // TODO: Variable and theme + visibility: visible; + } + } + } +} + +// We have a hover style on the room list with no specific list hovered, so account for that +.mx_RoomList2:hover .mx_RoomSublist2, +.mx_RoomSublist2_hasMenuOpen { + .mx_RoomSublist2_headerContainer_withAux { + .mx_RoomSublist2_badgeContainer { + // Completely hide the badge + width: 0; + margin: 0; + visibility: hidden; + } + + .mx_RoomSublist2_auxButton { + // Show the aux button, but not the list button + width: 24px; + height: 24px; + margin-right: 1px; // line it up with the badges on the room tiles + visibility: visible; + } + } +} + +.mx_RoomSublist2_contextMenu { + padding: 20px 16px; + width: 250px; + + hr { + margin-top: 16px; + margin-bottom: 16px; + margin-right: 16px; // additional 16px + border: 1px solid $roomsublist2-divider-color; + } + + .mx_RoomSublist2_contextMenu_title { + font-size: $font-15px; + line-height: $font-20px; + font-weight: 600; + margin-bottom: 12px; + } } diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss new file mode 100644 index 0000000000..67b1470550 --- /dev/null +++ b/res/css/views/rooms/_RoomTile2.scss @@ -0,0 +1,181 @@ +/* +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. +*/ + +// TODO: Rename to mx_RoomTile during replacement of old component + +// Note: the room tile expects to be in a flexbox column container +.mx_RoomTile2 { + margin-bottom: 4px; + padding: 4px; + + // The tile is also a flexbox row itself + display: flex; + flex-wrap: wrap; + + &.mx_RoomTile2_selected, &:hover, &.mx_RoomTile2_hasMenuOpen { + background-color: $roomtile2-selected-bg-color; + border-radius: 32px; + } + + .mx_RoomTile2_avatarContainer { + margin-right: 8px; + } + + .mx_RoomTile2_nameContainer { + flex-grow: 1; + max-width: calc(100% - 58px); // 32px avatar, 18px badge area, 8px margin on avatar + + // Create a new column layout flexbox for the name parts + display: flex; + flex-direction: column; + justify-content: center; + + .mx_RoomTile2_name, + .mx_RoomTile2_messagePreview { + margin: 0 2px; + width: 100%; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .mx_RoomTile2_name { + font-size: $font-14px; + line-height: $font-18px; + } + + .mx_RoomTile2_name.mx_RoomTile2_nameHasUnreadEvents { + font-weight: 600; + } + + .mx_RoomTile2_messagePreview { + font-size: $font-13px; + line-height: $font-18px; + color: $roomtile2-preview-color; + } + + .mx_RoomTile2_nameWithPreview { + margin-top: -4px; // shift the name up a bit more + } + } + + .mx_RoomTile2_badgeContainer { + width: 18px; + height: 32px; + + // Create another flexbox row because it's super easy to position the badge at + // the end this way. + display: flex; + align-items: center; + justify-content: center; + } + + // The menu button is hidden by default + // TODO: [Notifications] Use mx_RoomTile2_notificationsButton, similar to the following approach: + // https://github.com/matrix-org/matrix-react-sdk/blob/2180a56074f3698fc0241c309a72ba6cad802d1c/res/css/views/rooms/_RoomSublist2.scss#L48-L76 + // You'll need to do the same down below on the &:hover selector for the tile. + // ... also remove this 4 line TODO comment. + .mx_RoomTile2_menuButton, + .mx_RoomTile2_notificationsButton { + width: 0; + height: 0; + visibility: hidden; + position: relative; + + &::before { + content: ''; + width: 16px; + height: 16px; + position: absolute; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } + + .mx_RoomTile2_menuButton::before { + top: 8px; + left: -1px; // this is off-center to align it with the badges + mask-image: url('$(res)/img/feather-customised/more-horizontal.svg'); + } + + &:hover, &.mx_RoomTile2_hasMenuOpen { + // Hide the badge container on hover because it'll be a menu button + .mx_RoomTile2_badgeContainer { + width: 0; + height: 0; + visibility: hidden; + } + + .mx_RoomTile2_menuButton { + width: 18px; + height: 32px; + visibility: visible; + } + } +} + +.mx_RoomTile2_contextMenu { + .mx_RoomTile2_contextMenu_redRow { + .mx_AccessibleButton { + color: $warning-color !important; // !important to override styles from context menu + } + + .mx_IconizedContextMenu_icon::before { + background-color: $warning-color; + } + } + + .mx_IconizedContextMenu_icon { + position: relative; + width: 16px; + height: 16px; + + &::before { + content: ''; + width: 16px; + height: 16px; + position: absolute; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } + + .mx_RoomTile2_iconStar::before { + mask-image: url('$(res)/img/feather-customised/star.svg'); + } + + .mx_RoomTile2_iconArrowDown::before { + mask-image: url('$(res)/img/feather-customised/arrow-down.svg'); + } + + .mx_RoomTile2_iconUser::before { + mask-image: url('$(res)/img/feather-customised/user.svg'); + } + + .mx_RoomTile2_iconSettings::before { + mask-image: url('$(res)/img/feather-customised/settings.svg'); + } + + .mx_RoomTile2_iconSignOut::before { + mask-image: url('$(res)/img/feather-customised/sign-out.svg'); + } +} diff --git a/res/img/feather-customised/archive.svg b/res/img/feather-customised/archive.svg new file mode 100644 index 0000000000..428882c87b --- /dev/null +++ b/res/img/feather-customised/archive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/arrow-down.svg b/res/img/feather-customised/arrow-down.svg new file mode 100644 index 0000000000..4f84f627bd --- /dev/null +++ b/res/img/feather-customised/arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/compass.svg b/res/img/feather-customised/compass.svg new file mode 100644 index 0000000000..3296260803 --- /dev/null +++ b/res/img/feather-customised/compass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/more-horizontal.svg b/res/img/feather-customised/more-horizontal.svg new file mode 100644 index 0000000000..dc6a85564e --- /dev/null +++ b/res/img/feather-customised/more-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/star.svg b/res/img/feather-customised/star.svg new file mode 100644 index 0000000000..bcdc31aa47 --- /dev/null +++ b/res/img/feather-customised/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-customised/sun.svg b/res/img/feather-customised/sun.svg new file mode 100644 index 0000000000..7f51b94d1c --- /dev/null +++ b/res/img/feather-customised/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 78fe2a74c5..3c6f6aa5a4 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -172,6 +172,14 @@ $header-divider-color: #91A1C0; // ******************** +// TODO: Update variables for new room list +// TODO: Dark theme +$roomtile2-preview-color: #9e9e9e; +$roomtile2-badge-color: #61708b; +$roomtile2-selected-bg-color: #FFF; +$theme-button-bg-color: #e3e8f0; +$roomsublist2-divider-color: #e9eaeb; + $roomtile-name-color: #61708b; $roomtile-badge-fg-color: $accent-fg-color; $roomtile-selected-color: #212121; diff --git a/src/ActiveRoomObserver.js b/src/ActiveRoomObserver.js index d6fbb460b5..b7695d401d 100644 --- a/src/ActiveRoomObserver.js +++ b/src/ActiveRoomObserver.js @@ -27,7 +27,7 @@ import RoomViewStore from './stores/RoomViewStore'; */ class ActiveRoomObserver { constructor() { - this._listeners = {}; + this._listeners = {}; // key=roomId, value=function(isActive:boolean) this._activeRoomId = RoomViewStore.getRoomId(); // TODO: We could self-destruct when the last listener goes away, or at least @@ -35,6 +35,10 @@ class ActiveRoomObserver { this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate.bind(this)); } + get activeRoomId(): string { + return this._activeRoomId; + } + addListener(roomId, listener) { if (!this._listeners[roomId]) this._listeners[roomId] = []; this._listeners[roomId].push(listener); @@ -51,23 +55,23 @@ class ActiveRoomObserver { } } - _emit(roomId) { + _emit(roomId, isActive: boolean) { if (!this._listeners[roomId]) return; for (const l of this._listeners[roomId]) { - l.call(); + l.call(null, isActive); } } _onRoomViewStoreUpdate() { // emit for the old room ID - if (this._activeRoomId) this._emit(this._activeRoomId); + if (this._activeRoomId) this._emit(this._activeRoomId, false); // update our cache this._activeRoomId = RoomViewStore.getRoomId(); // and emit for the new one - if (this._activeRoomId) this._emit(this._activeRoomId); + if (this._activeRoomId) this._emit(this._activeRoomId, true); } } diff --git a/src/Searching.js b/src/Searching.js index 663328fe41..9631afc36b 100644 --- a/src/Searching.js +++ b/src/Searching.js @@ -107,6 +107,29 @@ async function localSearch(searchTerm, roomId = undefined) { const result = MatrixClientPeg.get()._processRoomEventsSearch( emptyResult, response); + // Restore our encryption info so we can properly re-verify the events. + for (let i = 0; i < result.results.length; i++) { + const timeline = result.results[i].context.getTimeline(); + + for (let j = 0; j < timeline.length; j++) { + const ev = timeline[j]; + if (ev.event.curve25519Key) { + ev.makeEncrypted( + "m.room.encrypted", + { algorithm: ev.event.algorithm }, + ev.event.curve25519Key, + ev.event.ed25519Key, + ); + ev._forwardingCurve25519KeyChain = ev.event.forwardingCurve25519KeyChain; + + delete ev.event.curve25519Key; + delete ev.event.ed25519Key; + delete ev.event.algorithm; + delete ev.event.forwardingCurve25519KeyChain; + } + } + } + return result; } diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 3607d7a676..09cfb67de7 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -265,13 +265,22 @@ function textForServerACLEvent(ev) { return text + changes.join(" "); } -function textForMessageEvent(ev) { +function textForMessageEvent(ev, skipUserPrefix) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); let message = senderDisplayName + ': ' + ev.getContent().body; - if (ev.getContent().msgtype === "m.emote") { - message = "* " + senderDisplayName + " " + message; - } else if (ev.getContent().msgtype === "m.image") { - message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName}); + if (skipUserPrefix) { + message = ev.getContent().body; + if (ev.getContent().msgtype === "m.emote") { + message = senderDisplayName + " " + message; + } else if (ev.getContent().msgtype === "m.image") { + message = _t('sent an image.'); + } + } else { + if (ev.getContent().msgtype === "m.emote") { + message = "* " + senderDisplayName + " " + message; + } else if (ev.getContent().msgtype === "m.image") { + message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName}); + } } return message; } @@ -612,8 +621,8 @@ for (const evType of ALL_RULE_TYPES) { stateHandlers[evType] = textForMjolnirEvent; } -export function textForEvent(ev) { +export function textForEvent(ev, skipUserPrefix) { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; - if (handler) return handler(ev); + if (handler) return handler(ev, skipUserPrefix); return ''; } diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index ff8d35a114..209f219598 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -22,9 +22,10 @@ import { _t } from "../../languageHandler"; import SdkConfig from "../../SdkConfig"; import * as sdk from "../../index"; import dis from "../../dispatcher/dispatcher"; +import { Action } from "../../dispatcher/actions"; const onClickSendDm = () => dis.dispatch({action: 'view_create_chat'}); -const onClickExplore = () => dis.dispatch({action: 'view_room_directory'}); +const onClickExplore = () => dis.fire(Action.ViewRoomDirectory); const onClickNewRoom = () => dis.dispatch({action: 'view_create_room'}); const HomePage = () => { diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index 05cd97df2a..bae69b5631 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -252,7 +252,7 @@ const LeftPanel = createReactClass({ if (!this.props.collapsed) { exploreButton = (
- dis.dispatch({action: 'view_room_directory'})}>{_t("Explore")} + dis.fire(Action.ViewRoomDirectory)}>{_t("Explore")}
); } diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index c9a4948539..302d71afa8 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -18,12 +18,18 @@ import * as React from "react"; import TagPanel from "./TagPanel"; import classNames from "classnames"; import dis from "../../dispatcher/dispatcher"; -import AccessibleButton from "../views/elements/AccessibleButton"; import { _t } from "../../languageHandler"; import SearchBox from "./SearchBox"; import RoomList2 from "../views/rooms/RoomList2"; -import TopLeftMenuButton from "./TopLeftMenuButton"; import { Action } from "../../dispatcher/actions"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; +import BaseAvatar from '../views/avatars/BaseAvatar'; +import UserMenuButton from "./UserMenuButton"; +import RoomSearch from "./RoomSearch"; +import AccessibleButton from "../views/elements/AccessibleButton"; +import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2"; +import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore"; +import { UPDATE_EVENT } from "../../stores/AsyncStore"; /******************************************************************* * CAUTION * @@ -38,15 +44,14 @@ interface IProps { } interface IState { - searchExpanded: boolean; searchFilter: string; // TODO: Move search into room list? + showBreadcrumbs: boolean; } export default class LeftPanel2 extends React.Component { // TODO: Properly support TagPanel // TODO: Properly support searching/filtering // TODO: Properly support breadcrumbs - // TODO: Properly support TopLeftMenu (User Settings) // TODO: a11y // TODO: actually make this useful in general (match design proposals) // TODO: Fadable support (is this still needed?) @@ -55,61 +60,104 @@ export default class LeftPanel2 extends React.Component { super(props); this.state = { - searchExpanded: false, searchFilter: "", + showBreadcrumbs: BreadcrumbsStore.instance.visible, }; + + BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate); + } + + public componentWillUnmount() { + BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); } private onSearch = (term: string): void => { this.setState({searchFilter: term}); }; - private onSearchCleared = (source: string): void => { - if (source === "keyboard") { - dis.fire(Action.FocusComposer); - } - this.setState({searchExpanded: false}); - } - - private onSearchFocus = (): void => { - this.setState({searchExpanded: true}); + private onExplore = () => { + dis.fire(Action.ViewRoomDirectory); }; - private onSearchBlur = (event: FocusEvent): void => { - const target = event.target as HTMLInputElement; - if (target.value.length === 0) { - this.setState({searchExpanded: false}); + private onBreadcrumbsUpdate = () => { + const newVal = BreadcrumbsStore.instance.visible; + if (newVal !== this.state.showBreadcrumbs) { + this.setState({showBreadcrumbs: newVal}); } + }; + + private renderHeader(): React.ReactNode { + // TODO: Update when profile info changes + // TODO: Presence + // TODO: Breadcrumbs toggle + // TODO: Menu button + const avatarSize = 32; + // TODO: Don't do this profile lookup in render() + const client = MatrixClientPeg.get(); + let displayName = client.getUserId(); + let avatarUrl: string = null; + const myUser = client.getUser(client.getUserId()); + if (myUser) { + displayName = myUser.rawDisplayName; + avatarUrl = myUser.avatarUrl; + } + + let breadcrumbs; + if (this.state.showBreadcrumbs) { + breadcrumbs = ( +
+ +
+ ); + } + + return ( +
+
+ + + + {displayName} + + + +
+ {breadcrumbs} +
+ ); + } + + private renderSearchExplore(): React.ReactNode { + // TODO: Collapsed support + + return ( +
+ + +
+ ); } public render(): React.ReactNode { const tagPanel = ( -
+
); - const exploreButton = ( -
- dis.dispatch({action: 'view_room_directory'})}> - {_t("Explore")} - -
- ); - - const searchBox = ( {/*TODO*/}} - onSearch={this.onSearch} - onCleared={this.onSearchCleared} - onFocus={this.onSearchFocus} - onBlur={this.onSearchBlur} - collapsed={false}/>); // TODO: Collapsed support - // TODO: Improve props for RoomList2 const roomList = {/*TODO*/}} @@ -120,33 +168,21 @@ export default class LeftPanel2 extends React.Component { onBlur={() => {/*TODO*/}} />; - // TODO: Breadcrumbs // TODO: Conference handling / calls const containerClasses = classNames({ - "mx_LeftPanel_container": true, - "mx_fadable": true, - "collapsed": false, // TODO: Collapsed support - "mx_LeftPanel_container_hasTagPanel": true, // TODO: TagPanel support - "mx_fadable_faded": false, - "mx_LeftPanel2": true, // TODO: Remove flag when RoomList2 ships (used as an indicator) + "mx_LeftPanel2": true, }); return (
{tagPanel} -