From 4c1bc50649afcf31f403b7b887ecbdc95ba31772 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Thu, 4 Jun 2020 16:34:04 -0600
Subject: [PATCH 1/8] Initial styling for new room list

This is a work in progress, but covers the coarse areas. This uses all-new classes to better describe what everything is, and to reduce the number of selectors we keep track of.

This is primarily layout for the list and not actually the final structure. For example, some buttons are missing and other areas are not styled correctly - the idea in this commit was to get things roughly in the right place and work on it.
---
 res/css/_components.scss                    |  3 +
 res/css/structures/_LeftPanel.scss          |  8 --
 res/css/structures/_LeftPanel2.scss         | 91 +++++++++++++++++++++
 res/css/structures/_MatrixChat.scss         |  2 +-
 res/css/views/rooms/_RoomList2.scss         | 23 ++++++
 res/css/views/rooms/_RoomSublist2.scss      |  2 +-
 res/css/views/rooms/_RoomTile2.scss         | 18 ++++
 src/components/structures/LeftPanel2.tsx    | 63 +++++++++-----
 src/components/views/rooms/RoomList2.tsx    |  2 +-
 src/components/views/rooms/RoomSublist2.tsx | 21 +++--
 src/components/views/rooms/RoomTile2.tsx    | 32 ++++----
 11 files changed, 205 insertions(+), 60 deletions(-)
 create mode 100644 res/css/structures/_LeftPanel2.scss
 create mode 100644 res/css/views/rooms/_RoomList2.scss
 create mode 100644 res/css/views/rooms/_RoomTile2.scss

diff --git a/res/css/_components.scss b/res/css/_components.scss
index b047519d99..62bec5ad62 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -12,6 +12,7 @@
 @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";
@@ -177,10 +178,12 @@
 @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/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..52ee4f16ac
--- /dev/null
+++ b/res/css/structures/_LeftPanel2.scss
@@ -0,0 +1,91 @@
+/*
+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.
+*/
+
+$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;
+            }
+
+            .mx_LeftPanel2_breadcrumbsContainer {
+                // TODO: Improve CSS for breadcrumbs (currently shoved into the view rather than placed)
+                width: 100%;
+                overflow: hidden;
+            }
+        }
+
+        .mx_LeftPanel2_filterContainer {
+            // TODO: Improve CSS for filtering and its input
+        }
+
+        .mx_LeftPanel2_actualRoomListContainer {
+            flex-grow: 1; // fill the available space
+            overflow-y: auto;
+        }
+    }
+}
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/views/rooms/_RoomList2.scss b/res/css/views/rooms/_RoomList2.scss
new file mode 100644
index 0000000000..add7214468
--- /dev/null
+++ b/res/css/views/rooms/_RoomList2.scss
@@ -0,0 +1,23 @@
+/*
+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_RoomList2 {
+    // 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: wrap;
+}
diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss
index abc3133fc1..55b16843b3 100644
--- a/res/css/views/rooms/_RoomSublist2.scss
+++ b/res/css/views/rooms/_RoomSublist2.scss
@@ -16,7 +16,7 @@ limitations under the License.
 
 @import "../../../../node_modules/react-resizable/css/styles.css";
 
-.mx_RoomList2 .mx_RoomSubList_labelContainer {
+.mx_RoomList2 .mx_RoomSubList2_labelContainer {
   z-index: 12;
   background-color: purple;
 }
diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss
new file mode 100644
index 0000000000..6577f1ce25
--- /dev/null
+++ b/res/css/views/rooms/_RoomTile2.scss
@@ -0,0 +1,18 @@
+/*
+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_RoomTile2 {
+}
diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx
index c9a4948539..bf0e72beeb 100644
--- a/src/components/structures/LeftPanel2.tsx
+++ b/src/components/structures/LeftPanel2.tsx
@@ -24,6 +24,9 @@ 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 RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs";
 
 /*******************************************************************
  *   CAUTION                                                       *
@@ -82,24 +85,44 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
         }
     }
 
+    private renderHeader(): React.ReactNode {
+        // TODO: Use real profile info
+        // TODO: Presence
+        // TODO: Breadcrumbs toggle
+        // TODO: Menu button
+        const avatarSize = 32;
+        return (
+            <div className="mx_LeftPanel2_userHeader">
+                <div className="mx_LeftPanel2_headerRow">
+                    <span className="mx_LeftPanel2_userAvatarContainer">
+                        <BaseAvatar
+                            idName={MatrixClientPeg.get().getUserId()}
+                            name={"TODO: Display Name"}
+                            url={null}
+                            width={avatarSize}
+                            height={avatarSize}
+                            resizeMethod="crop"
+                            className="mx_LeftPanel2_userAvatar"
+                        />
+                    </span>
+                    <span className="mx_LeftPanel2_userName">Irene</span>
+                </div>
+                <div className="mx_LeftPanel2_headerRow mx_LeftPanel2_breadcrumbsContainer">
+                    <RoomBreadcrumbs />
+                </div>
+            </div>
+        );
+    }
+
     public render(): React.ReactNode {
         const tagPanel = (
-            <div className="mx_LeftPanel_tagPanelContainer">
+            <div className="mx_LeftPanel2_tagPanelContainer">
                 <TagPanel/>
             </div>
         );
 
-        const exploreButton = (
-            <div
-                className={classNames("mx_LeftPanel_explore", {"mx_LeftPanel_explore_hidden": this.state.searchExpanded})}>
-                <AccessibleButton onClick={() => dis.dispatch({action: 'view_room_directory'})}>
-                    {_t("Explore")}
-                </AccessibleButton>
-            </div>
-        );
-
         const searchBox = (<SearchBox
-            className="mx_LeftPanel_filterRooms"
+            className="mx_LeftPanel2_TODO_filterRooms"
             enableRoomSearchFocus={true}
             blurredPlaceholder={_t('Filter')}
             placeholder={_t('Filter rooms…')}
@@ -124,29 +147,25 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
         // 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 (
             <div className={containerClasses}>
                 {tagPanel}
-                <aside className="mx_LeftPanel dark-panel">
-                    <TopLeftMenuButton collapsed={false}/>
+                <aside className="mx_LeftPanel2_roomListContainer">
+                    {this.renderHeader()}
                     <div
-                        className="mx_LeftPanel_exploreAndFilterRow"
+                        className="mx_LeftPanel2_filterContainer"
                         onKeyDown={() => {/*TODO*/}}
                         onFocus={() => {/*TODO*/}}
                         onBlur={() => {/*TODO*/}}
                     >
-                        {exploreButton}
                         {searchBox}
                     </div>
-                    {roomList}
+                    <div className="mx_LeftPanel2_actualRoomListContainer">
+                        {roomList}
+                    </div>
                 </aside>
             </div>
         );
diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx
index 15aa880109..af366e9685 100644
--- a/src/components/views/rooms/RoomList2.tsx
+++ b/src/components/views/rooms/RoomList2.tsx
@@ -216,7 +216,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
                         onFocus={this.props.onFocus}
                         onBlur={this.props.onBlur}
                         onKeyDown={onKeyDownHandler}
-                        className="mx_RoomList mx_RoomList2"
+                        className="mx_RoomList2"
                         role="tree"
                         aria-label={_t("Rooms")}
                         // Firefox sometimes makes this element focusable due to
diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx
index cc56f92769..e7682a9bb4 100644
--- a/src/components/views/rooms/RoomSublist2.tsx
+++ b/src/components/views/rooms/RoomSublist2.tsx
@@ -113,9 +113,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
         let chevron = null;
         if (this.hasTiles()) {
             const chevronClasses = classNames({
-                'mx_RoomSubList_chevron': true,
-                'mx_RoomSubList_chevronRight': false, // isCollapsed
-                'mx_RoomSubList_chevronDown': true, // !isCollapsed
+                'mx_RoomSublist2_chevron': true,
+                'mx_RoomSublist2_chevronRight': false, // isCollapsed
+                'mx_RoomSublist2_chevronDown': true, // !isCollapsed
             });
             chevron = (<div className={chevronClasses}/>);
         }
@@ -130,8 +130,8 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
                     let badge;
                     if (true) { // !isCollapsed
                         const badgeClasses = classNames({
-                            'mx_RoomSubList_badge': true,
-                            'mx_RoomSubList_badgeHighlight': notifHighlight,
+                            'mx_RoomSublist2_badge': true,
+                            'mx_RoomSublist2_badgeHighlight': notifHighlight,
                         });
                         // Wrap the contents in a div and apply styles to the child div so that the browser default outline works
                         if (notifCount > 0) {
@@ -168,7 +168,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
                             <AccessibleTooltipButton
                                 tabIndex={tabIndex}
                                 onClick={this.onAddRoom}
-                                className="mx_RoomSubList_addRoom"
+                                className="mx_RoomSublist2_addButton"
                                 title={this.props.addRoomLabel || _t("Add room")}
                             />
                         );
@@ -176,11 +176,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 
                     // TODO: a11y (see old component)
                     return (
-                        <div className={"mx_RoomSubList_labelContainer"}>
+                        <div className={"mx_RoomSublist2_headerContainer"}>
                             <AccessibleButton
                                 inputRef={ref}
                                 tabIndex={tabIndex}
-                                className={"mx_RoomSubList_label"}
+                                className={"mx_RoomSublist2_headerText"}
                                 role="treeitem"
                                 aria-level="1"
                             >
@@ -204,9 +204,8 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 
         const classes = classNames({
             // TODO: Proper collapse support
-            'mx_RoomSubList': true,
-            'mx_RoomSubList_hidden': false, // len && isCollapsed
-            'mx_RoomSubList_nonEmpty': this.hasTiles(), // len && !isCollapsed
+            'mx_RoomSublist2': true,
+            'mx_RoomSublist2_collapsed': false, // len && isCollapsed
         });
 
         let content = null;
diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx
index c95cd108dc..40c7bcbc12 100644
--- a/src/components/views/rooms/RoomTile2.tsx
+++ b/src/components/views/rooms/RoomTile2.tsx
@@ -195,28 +195,28 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
         const hasBadge = this.state.notificationState.color > NotificationColor.Bold;
         const isUnread = this.state.notificationState.color > NotificationColor.None;
         const classes = classNames({
-            'mx_RoomTile': true,
+            'mx_RoomTile2': true,
             // 'mx_RoomTile_selected': this.state.selected,
-            'mx_RoomTile_unread': isUnread,
-            'mx_RoomTile_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey,
-            'mx_RoomTile_highlight': this.state.notificationState.color >= NotificationColor.Red,
-            'mx_RoomTile_invited': this.roomIsInvite,
+            'mx_RoomTile2_unread': isUnread,
+            'mx_RoomTile2_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey,
+            'mx_RoomTile2_highlight': this.state.notificationState.color >= NotificationColor.Red,
+            'mx_RoomTile2_invited': this.roomIsInvite,
             // 'mx_RoomTile_menuDisplayed': isMenuDisplayed,
-            'mx_RoomTile_noBadges': !hasBadge,
+            'mx_RoomTile2_noBadges': !hasBadge,
             // 'mx_RoomTile_transparent': this.props.transparent,
             // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed,
         });
 
         const avatarClasses = classNames({
-            'mx_RoomTile_avatar': true,
+            'mx_RoomTile2_avatar': true,
         });
 
 
         let badge;
         if (hasBadge) {
             const badgeClasses = classNames({
-                'mx_RoomTile_badge': true,
-                'mx_RoomTile_badgeButton': false, // this.state.badgeHover || isMenuDisplayed
+                'mx_RoomTile2_badge': true,
+                'mx_RoomTile2_badgeButton': false, // this.state.badgeHover || isMenuDisplayed
             });
             badge = <div className={badgeClasses}>{this.state.notificationState.symbol}</div>;
         }
@@ -227,16 +227,16 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
         name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
 
         const nameClasses = classNames({
-            'mx_RoomTile_name': true,
-            'mx_RoomTile_invite': this.roomIsInvite,
-            'mx_RoomTile_badgeShown': hasBadge,
+            'mx_RoomTile2_name': true,
+            'mx_RoomTile2_invite': this.roomIsInvite,
+            'mx_RoomTile2_badgeShown': hasBadge,
         });
 
         // TODO: Support collapsed state properly
         let tooltip = null;
         if (false) { // isCollapsed
             if (this.state.hover) {
-                tooltip = <Tooltip className="mx_RoomTile_tooltip" label={this.props.room.name} />
+                tooltip = <Tooltip className="mx_RoomTile2_tooltip" label={this.props.room.name} />
             }
         }
 
@@ -255,12 +255,12 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
                             role="treeitem"
                         >
                             <div className={avatarClasses}>
-                                <div className="mx_RoomTile_avatar_container">
+                                <div className="mx_RoomTile2_avatarContainer">
                                     <RoomAvatar room={this.props.room} width={24} height={24}/>
                                 </div>
                             </div>
-                            <div className="mx_RoomTile_nameContainer">
-                                <div className="mx_RoomTile_labelContainer">
+                            <div className="mx_RoomTile2_nameContainer">
+                                <div className="mx_RoomTile2_labelContainer">
                                     <div title={name} className={nameClasses} tabIndex={-1} dir="auto">
                                         {name}
                                     </div>

From 0c15b2bdb6c332713b8e95cb907c6b50adeaeeda Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Thu, 4 Jun 2020 21:21:04 -0600
Subject: [PATCH 2/8] Simple structuring of the room list itself

This covers the larger parts of the design, but doesn't deal with the nuances of hover states, badge sizing, etc.
---
 res/css/views/rooms/_RoomSublist2.scss      |  26 +++-
 res/css/views/rooms/_RoomTile2.scss         |  76 +++++++++++
 res/themes/light/css/_light.scss            |   5 +
 src/components/views/rooms/RoomList2.tsx    |   3 +-
 src/components/views/rooms/RoomSublist2.tsx | 135 ++++++++++----------
 src/components/views/rooms/RoomTile2.tsx    |  65 ++++------
 src/i18n/strings/en_EN.json                 |   9 +-
 src/stores/room-list/ListLayout.ts          |   2 +-
 8 files changed, 205 insertions(+), 116 deletions(-)

diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss
index 55b16843b3..4d8c3fffff 100644
--- a/res/css/views/rooms/_RoomSublist2.scss
+++ b/res/css/views/rooms/_RoomSublist2.scss
@@ -16,7 +16,27 @@ limitations under the License.
 
 @import "../../../../node_modules/react-resizable/css/styles.css";
 
-.mx_RoomList2 .mx_RoomSubList2_labelContainer {
-  z-index: 12;
-  background-color: purple;
+.mx_RoomSublist2 {
+    // The sublist is a column of rows, essentially
+    display: flex;
+    flex-direction: column;
+
+    margin-left: 8px;
+    margin-top: 12px;
+    margin-bottom: 12px;
+
+    .mx_RoomSublist2_headerContainer {
+        text-transform: uppercase;
+        opacity: 0.5;
+        line-height: $font-16px;
+        font-size: $font-12px;
+        padding-bottom: 8px;
+    }
+
+    .mx_RoomSublist2_resizeBox {
+        // Create another flexbox column for the tiles
+        display: flex;
+        flex-direction: column;
+        overflow: hidden;
+    }
 }
diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss
index 6577f1ce25..bb27942f81 100644
--- a/res/css/views/rooms/_RoomTile2.scss
+++ b/res/css/views/rooms/_RoomTile2.scss
@@ -14,5 +14,81 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+// Note: the room tile expects to be in a flexbox column container
 .mx_RoomTile2 {
+    width: 100%;
+    padding-bottom: 12px;
+
+    // The tile is also a flexbox row itself
+    display: flex;
+    flex-wrap: wrap;
+
+    .mx_RoomTile2_avatarContainer {
+        margin-right: 8px;
+    }
+
+    .mx_RoomTile2_nameContainer {
+        // 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;
+        }
+
+        // TODO: Ellipsis on the name and preview
+
+        .mx_RoomTile2_name {
+            font-weight: 600;
+            font-size: $font-14px;
+            line-height: $font-19px;
+        }
+
+        .mx_RoomTile2_messagePreview {
+            font-size: $font-13px;
+            line-height: $font-18px;
+            color: $roomtile2-preview-color;
+        }
+    }
+
+    .mx_RoomTile2_badgeContainer {
+        flex-grow: 1;
+
+        // 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: flex-end;
+
+        .mx_RoomTile2_badge {
+            background-color: $roomtile2-badge-color;
+
+            &:not(.mx_RoomTile2_badgeEmpty) {
+                border-radius: 16px;
+                font-size: $font-10px;
+                line-height: $font-14px;
+                text-align: center;
+                font-weight: bold;
+                margin-right: 14px;
+                color: #fff; // TODO: Variable
+
+                // TODO: Confirm padding on counted badges
+                padding: 2px 5px;
+            }
+
+            &.mx_RoomTile2_badgeEmpty {
+                width: 6px;
+                height: 6px;
+                border-radius: 6px;
+                margin-right: 18px;
+            }
+
+            &.mx_RoomTile2_badgeHighlight {
+                // TODO: Use a more specific variable
+                background-color: $warning-color;
+            }
+        }
+    }
 }
diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss
index 78fe2a74c5..683c02528d 100644
--- a/res/themes/light/css/_light.scss
+++ b/res/themes/light/css/_light.scss
@@ -172,6 +172,11 @@ $header-divider-color: #91A1C0;
 
 // ********************
 
+// TODO: Update variables for new room list
+// TODO: Dark theme
+$roomtile2-preview-color: #9e9e9e;
+$roomtile2-badge-color: #61708b;
+
 $roomtile-name-color: #61708b;
 $roomtile-badge-fg-color: $accent-fg-color;
 $roomtile-selected-color: #212121;
diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx
index af366e9685..ce1956f68d 100644
--- a/src/components/views/rooms/RoomList2.tsx
+++ b/src/components/views/rooms/RoomList2.tsx
@@ -96,7 +96,7 @@ const TAG_AESTHETICS: {
         defaultHidden: false,
     },
     [DefaultTagID.DM]: {
-        sectionLabel: _td("Direct Messages"),
+        sectionLabel: _td("People"),
         isInvite: false,
         defaultHidden: false,
         addRoomLabel: _td("Start chat"),
@@ -200,6 +200,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
                     addRoomLabel={aesthetics.addRoomLabel}
                     isInvite={aesthetics.isInvite}
                     layout={this.state.layouts.get(orderedTagId)}
+                    showMessagePreviews={orderedTagId === DefaultTagID.DM}
                 />
             );
         }
diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx
index e7682a9bb4..84bcb1ea4d 100644
--- a/src/components/views/rooms/RoomSublist2.tsx
+++ b/src/components/views/rooms/RoomSublist2.tsx
@@ -20,15 +20,13 @@ import * as React from "react";
 import { createRef } from "react";
 import { Room } from "matrix-js-sdk/src/models/room";
 import classNames from 'classnames';
-import * as RoomNotifs from '../../../RoomNotifs';
 import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
 import { _t } from "../../../languageHandler";
 import AccessibleButton from "../../views/elements/AccessibleButton";
-import AccessibleTooltipButton from "../../views/elements/AccessibleTooltipButton";
-import * as FormattingUtils from '../../../utils/FormattingUtils';
 import RoomTile2 from "./RoomTile2";
 import { ResizableBox, ResizeCallbackData } from "react-resizable";
 import { ListLayout } from "../../../stores/room-list/ListLayout";
+import { DefaultTagID, TagID } from "../../../stores/room-list/models";
 
 /*******************************************************************
  *   CAUTION                                                       *
@@ -43,6 +41,7 @@ interface IProps {
     rooms?: Room[];
     startAsHidden: boolean;
     label: string;
+    showMessagePreviews: boolean;
     onAddRoom?: () => void;
     addRoomLabel: string;
     isInvite: boolean;
@@ -93,7 +92,13 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 
         if (this.props.rooms) {
             for (const room of this.props.rooms) {
-                tiles.push(<RoomTile2 room={room} key={`room-${room.roomId}`}/>);
+                tiles.push(
+                    <RoomTile2
+                        room={room}
+                        key={`room-${room.roomId}`}
+                        showMessagePreview={this.props.showMessagePreviews}
+                    />
+                );
             }
         }
 
@@ -101,25 +106,16 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
     }
 
     private renderHeader(): React.ReactElement {
-        const notifications = !this.props.isInvite
-            ? RoomNotifs.aggregateNotificationCount(this.props.rooms)
-            : {count: 0, highlight: true};
-        const notifCount = notifications.count;
-        const notifHighlight = notifications.highlight;
+        // TODO: Handle badge count
+        // const notifications = !this.props.isInvite
+        //     ? RoomNotifs.aggregateNotificationCount(this.props.rooms)
+        //     : {count: 0, highlight: true};
+        // const notifCount = notifications.count;
+        // const notifHighlight = notifications.highlight;
 
         // TODO: Title on collapsed
         // TODO: Incoming call box
 
-        let chevron = null;
-        if (this.hasTiles()) {
-            const chevronClasses = classNames({
-                'mx_RoomSublist2_chevron': true,
-                'mx_RoomSublist2_chevronRight': false, // isCollapsed
-                'mx_RoomSublist2_chevronDown': true, // !isCollapsed
-            });
-            chevron = (<div className={chevronClasses}/>);
-        }
-
         return (
             <RovingTabIndexWrapper inputRef={this.headerButton}>
                 {({onFocus, isActive, ref}) => {
@@ -127,52 +123,55 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
                     const tabIndex = isActive ? 0 : -1;
 
                     // TODO: Collapsed state
-                    let badge;
-                    if (true) { // !isCollapsed
-                        const badgeClasses = classNames({
-                            'mx_RoomSublist2_badge': true,
-                            'mx_RoomSublist2_badgeHighlight': notifHighlight,
-                        });
-                        // Wrap the contents in a div and apply styles to the child div so that the browser default outline works
-                        if (notifCount > 0) {
-                            badge = (
-                                <AccessibleButton
-                                    tabIndex={tabIndex}
-                                    className={badgeClasses}
-                                    aria-label={_t("Jump to first unread room.")}
-                                >
-                                    <div>
-                                        {FormattingUtils.formatCount(notifCount)}
-                                    </div>
-                                </AccessibleButton>
-                            );
-                        } else if (this.props.isInvite && this.hasTiles()) {
-                            // Render the `!` badge for invites
-                            badge = (
-                                <AccessibleButton
-                                    tabIndex={tabIndex}
-                                    className={badgeClasses}
-                                    aria-label={_t("Jump to first invite.")}
-                                >
-                                    <div>
-                                        {FormattingUtils.formatCount(this.numTiles)}
-                                    </div>
-                                </AccessibleButton>
-                            );
-                        }
-                    }
+                    // TODO: Handle badge count
+                    // let badge;
+                    // if (true) { // !isCollapsed
+                    //     const showCount = localStorage.getItem("mx_rls_count") || notifHighlight;
+                    //     const badgeClasses = classNames({
+                    //         'mx_RoomSublist2_badge': true,
+                    //         'mx_RoomSublist2_badgeHighlight': notifHighlight,
+                    //         'mx_RoomSublist2_badgeEmpty': !showCount,
+                    //     });
+                    //     // Wrap the contents in a div and apply styles to the child div so that the browser default outline works
+                    //     if (notifCount > 0) {
+                    //         const count = <div>{FormattingUtils.formatCount(notifCount)}</div>;
+                    //         badge = (
+                    //             <AccessibleButton
+                    //                 tabIndex={tabIndex}
+                    //                 className={badgeClasses}
+                    //                 aria-label={_t("Jump to first unread room.")}
+                    //             >
+                    //                 {showCount ? count : null}
+                    //             </AccessibleButton>
+                    //         );
+                    //     } else if (this.props.isInvite && this.hasTiles()) {
+                    //         // Render the `!` badge for invites
+                    //         badge = (
+                    //             <AccessibleButton
+                    //                 tabIndex={tabIndex}
+                    //                 className={badgeClasses}
+                    //                 aria-label={_t("Jump to first invite.")}
+                    //             >
+                    //                 <div>
+                    //                     {FormattingUtils.formatCount(this.numTiles)}
+                    //                 </div>
+                    //             </AccessibleButton>
+                    //         );
+                    //     }
+                    // }
 
-                    let addRoomButton = null;
-                    if (!!this.props.onAddRoom) {
-                        addRoomButton = (
-                            <AccessibleTooltipButton
-                                tabIndex={tabIndex}
-                                onClick={this.onAddRoom}
-                                className="mx_RoomSublist2_addButton"
-                                title={this.props.addRoomLabel || _t("Add room")}
-                            />
-                        );
-                    }
+                    // TODO: Aux button
+                    // let addRoomButton = null;
+                    // if (!!this.props.onAddRoom) {
+                    //     addRoomButton = (
+                    //         <AccessibleTooltipButton
+                    //             tabIndex={tabIndex}
+                    //             onClick={this.onAddRoom}
+                    //             className="mx_RoomSublist2_addButton"
+                    //             title={this.props.addRoomLabel || _t("Add room")}
+                    //         />
+                    //     );
+                    // }
 
                     // TODO: a11y (see old component)
                     return (
@@ -184,11 +183,8 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
                                 role="treeitem"
                                 aria-level="1"
                             >
-                                {chevron}
                                 <span>{this.props.label}</span>
                             </AccessibleButton>
-                            {badge}
-                            {addRoomButton}
                         </div>
                     );
                 }}
@@ -243,13 +239,14 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
                 // TODO: CSS TBD
                 // TODO: Show N more instead of infinity more?
                 // TODO: Safely use the same height of a tile, not hardcoded hacks
+                const moreTileHeightPx = `${layout.tileHeight}px`;
                 visibleTiles.splice(visibleTiles.length - 1, 1, (
                     <div
                         onClick={this.onShowAllClick}
-                        style={{height: '34px', lineHeight: '34px', backgroundColor: 'green', cursor: 'pointer'}}
+                        style={{height: moreTileHeightPx, lineHeight: moreTileHeightPx, backgroundColor: 'transparent', cursor: 'pointer'}}
                         key='showall'
                     >
-                        {_t("Show %(n)s more rooms", {n: numMissing})}
+                        {_t("Show %(n)s more", {n: numMissing})}
                     </div>
                 ));
             }
diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx
index 40c7bcbc12..8a51327ae2 100644
--- a/src/components/views/rooms/RoomTile2.tsx
+++ b/src/components/views/rooms/RoomTile2.tsx
@@ -51,6 +51,7 @@ enum NotificationColor {
 
 interface IProps {
     room: Room;
+    showMessagePreview: boolean;
 
     // TODO: Allow falsifying counts (for invites and stuff)
     // TODO: Transparency? Was this ever used?
@@ -192,33 +193,22 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
         // TODO: a11y proper
         // TODO: Render more than bare minimum
 
-        const hasBadge = this.state.notificationState.color > NotificationColor.Bold;
-        const isUnread = this.state.notificationState.color > NotificationColor.None;
         const classes = classNames({
             'mx_RoomTile2': true,
-            // 'mx_RoomTile_selected': this.state.selected,
-            'mx_RoomTile2_unread': isUnread,
-            'mx_RoomTile2_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey,
-            'mx_RoomTile2_highlight': this.state.notificationState.color >= NotificationColor.Red,
-            'mx_RoomTile2_invited': this.roomIsInvite,
-            // 'mx_RoomTile_menuDisplayed': isMenuDisplayed,
-            'mx_RoomTile2_noBadges': !hasBadge,
-            // 'mx_RoomTile_transparent': this.props.transparent,
-            // 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed,
         });
 
-        const avatarClasses = classNames({
-            'mx_RoomTile2_avatar': true,
-        });
-
-
         let badge;
+        const hasBadge = this.state.notificationState.color > NotificationColor.Bold;
         if (hasBadge) {
+            const hasNotif = this.state.notificationState.color >= NotificationColor.Red;
+            const isEmptyBadge = !localStorage.getItem("mx_rl_rt_badgeCount");
             const badgeClasses = classNames({
                 'mx_RoomTile2_badge': true,
-                'mx_RoomTile2_badgeButton': false, // this.state.badgeHover || isMenuDisplayed
+                'mx_RoomTile2_badgeHighlight': hasNotif,
+                'mx_RoomTile2_badgeEmpty': isEmptyBadge,
             });
-            badge = <div className={badgeClasses}>{this.state.notificationState.symbol}</div>;
+            const symbol = this.state.notificationState.symbol;
+            badge = <div className={badgeClasses}>{isEmptyBadge ? null : symbol}</div>;
         }
 
         // TODO: the original RoomTile uses state for the room name. Do we need to?
@@ -226,20 +216,21 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
         if (typeof name !== 'string') name = '';
         name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
 
-        const nameClasses = classNames({
-            'mx_RoomTile2_name': true,
-            'mx_RoomTile2_invite': this.roomIsInvite,
-            'mx_RoomTile2_badgeShown': hasBadge,
-        });
-
         // TODO: Support collapsed state properly
-        let tooltip = null;
-        if (false) { // isCollapsed
-            if (this.state.hover) {
-                tooltip = <Tooltip className="mx_RoomTile2_tooltip" label={this.props.room.name} />
-            }
+        // TODO: Tooltip?
+
+        let messagePreview = null;
+        if (this.props.showMessagePreview) {
+            // TODO: Actually get the real message preview from state
+            messagePreview = <div className="mx_RoomTile2_messagePreview">I just ate a pie.</div>;
         }
 
+        const nameClasses = classNames({
+            "mx_RoomTile2_name": true,
+            "mx_RoomTile2_nameWithPreview": !!messagePreview,
+        });
+
+        const avatarSize = 32;
         return (
             <React.Fragment>
                 <RovingTabIndexWrapper inputRef={this.roomTile}>
@@ -254,20 +245,18 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
                             onClick={this.onTileClick}
                             role="treeitem"
                         >
-                            <div className={avatarClasses}>
-                                <div className="mx_RoomTile2_avatarContainer">
-                                    <RoomAvatar room={this.props.room} width={24} height={24}/>
-                                </div>
+                            <div className="mx_RoomTile2_avatarContainer">
+                                <RoomAvatar room={this.props.room} width={avatarSize} height={avatarSize}/>
                             </div>
                             <div className="mx_RoomTile2_nameContainer">
-                                <div className="mx_RoomTile2_labelContainer">
-                                    <div title={name} className={nameClasses} tabIndex={-1} dir="auto">
-                                        {name}
-                                    </div>
+                                <div title={name} className={nameClasses} tabIndex={-1} dir="auto">
+                                    {name}
                                 </div>
+                                {messagePreview}
+                            </div>
+                            <div className="mx_RoomTile2_badgeContainer">
                                 {badge}
                             </div>
-                            {tooltip}
                         </AccessibleButton>
                     }
                 </RovingTabIndexWrapper>
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 884192b22a..cf6dc2431a 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1090,6 +1090,7 @@
     "Low priority": "Low priority",
     "Historical": "Historical",
     "System Alerts": "System Alerts",
+    "People": "People",
     "This room": "This room",
     "Joining room …": "Joining room …",
     "Loading …": "Loading …",
@@ -1133,10 +1134,7 @@
     "Securely back up your keys to avoid losing them. <a>Learn more.</a>": "Securely back up your keys to avoid losing them. <a>Learn more.</a>",
     "Not now": "Not now",
     "Don't ask me again": "Don't ask me again",
-    "Jump to first unread room.": "Jump to first unread room.",
-    "Jump to first invite.": "Jump to first invite.",
-    "Add room": "Add room",
-    "Show %(n)s more rooms": "Show %(n)s more rooms",
+    "Show %(n)s more": "Show %(n)s more",
     "Options": "Options",
     "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.",
     "%(count)s unread messages including mentions.|one": "1 unread mention.",
@@ -2017,6 +2015,9 @@
     "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
     "Active call": "Active call",
     "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?",
+    "Jump to first unread room.": "Jump to first unread room.",
+    "Jump to first invite.": "Jump to first invite.",
+    "Add room": "Add room",
     "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
     "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
     "Search failed": "Search failed",
diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts
index fd57a03ca1..ee51230a61 100644
--- a/src/stores/room-list/ListLayout.ts
+++ b/src/stores/room-list/ListLayout.ts
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-const TILE_HEIGHT_PX = 34;
+const TILE_HEIGHT_PX = 44;
 
 interface ISerializedListLayout {
     numTiles: number;

From 211ad66fea90e3c488a890fb9c7c71e3d24cf1ad Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 5 Jun 2020 08:39:38 -0600
Subject: [PATCH 3/8] Fix i18n

---
 src/i18n/strings/en_EN.json | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 1f70edc5d1..cf6dc2431a 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1134,9 +1134,6 @@
     "Securely back up your keys to avoid losing them. <a>Learn more.</a>": "Securely back up your keys to avoid losing them. <a>Learn more.</a>",
     "Not now": "Not now",
     "Don't ask me again": "Don't ask me again",
-    "Jump to first unread room.": "Jump to first unread room.",
-    "Jump to first invite.": "Jump to first invite.",
-    "Add room": "Add room",
     "Show %(n)s more": "Show %(n)s more",
     "Options": "Options",
     "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.",

From 6752c2832ee8a21e95ce105888b2257ec5bfce2a Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 5 Jun 2020 08:40:32 -0600
Subject: [PATCH 4/8] Add missing var

---
 src/components/views/rooms/RoomSublist2.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx
index 15a0ff801b..1a58b67377 100644
--- a/src/components/views/rooms/RoomSublist2.tsx
+++ b/src/components/views/rooms/RoomSublist2.tsx
@@ -237,6 +237,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 
                 // TODO: CSS TBD
                 // TODO: Make this an actual tile
+                const moreTileHeightPx = layout.tileHeight;
                 visibleTiles.splice(visibleTiles.length - 1, 1, (
                     <div
                         onClick={this.onShowAllClick}

From 475ba616710e4b32b41d1d96483326ec05c2778b Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 5 Jun 2020 08:48:23 -0600
Subject: [PATCH 5/8] Give the show more button some real CSS

This is still somewhat placeholder.
---
 res/css/views/rooms/_RoomSublist2.scss      | 9 +++++++++
 src/components/views/rooms/RoomSublist2.tsx | 3 +--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss
index 4d8c3fffff..da1c23b664 100644
--- a/res/css/views/rooms/_RoomSublist2.scss
+++ b/res/css/views/rooms/_RoomSublist2.scss
@@ -38,5 +38,14 @@ limitations under the License.
         display: flex;
         flex-direction: column;
         overflow: hidden;
+
+        .mx_RoomSublist2_showMoreButton {
+            height: 44px; // 1 room tile high
+            cursor: pointer;
+
+            // We create a flexbox to cheat at alignment
+            display: flex;
+            align-items: center;
+        }
     }
 }
diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx
index 1a58b67377..90231596fe 100644
--- a/src/components/views/rooms/RoomSublist2.tsx
+++ b/src/components/views/rooms/RoomSublist2.tsx
@@ -237,11 +237,10 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
 
                 // TODO: CSS TBD
                 // TODO: Make this an actual tile
-                const moreTileHeightPx = layout.tileHeight;
                 visibleTiles.splice(visibleTiles.length - 1, 1, (
                     <div
                         onClick={this.onShowAllClick}
-                        style={{height: moreTileHeightPx, lineHeight: moreTileHeightPx, cursor: 'pointer'}}
+                        className='mx_RoomSublist2_showMoreButton'
                         key='showall'
                     >
                         {_t("Show %(n)s more", {n: numMissing})}

From 1d8833e9f83a50e9223b04c59b8e216b86c56f04 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 5 Jun 2020 14:08:20 -0600
Subject: [PATCH 6/8] Selected state, cleanup, and profile display

---
 res/css/structures/_LeftPanel2.scss      |  3 +++
 res/css/views/rooms/_RoomList2.scss      |  2 ++
 res/css/views/rooms/_RoomSublist2.scss   |  2 ++
 res/css/views/rooms/_RoomTile2.scss      | 13 +++++++++++--
 res/themes/light/css/_light.scss         |  1 +
 src/ActiveRoomObserver.js                | 14 +++++++++-----
 src/components/structures/LeftPanel2.tsx | 17 +++++++++++++----
 src/components/views/rooms/RoomTile2.tsx | 11 ++++++++++-
 8 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss
index 52ee4f16ac..822a5ac399 100644
--- a/res/css/structures/_LeftPanel2.scss
+++ b/res/css/structures/_LeftPanel2.scss
@@ -14,6 +14,9 @@ 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;
 
diff --git a/res/css/views/rooms/_RoomList2.scss b/res/css/views/rooms/_RoomList2.scss
index add7214468..89760958f9 100644
--- a/res/css/views/rooms/_RoomList2.scss
+++ b/res/css/views/rooms/_RoomList2.scss
@@ -14,6 +14,8 @@ 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 {
     // Create a column-based flexbox for the sublists. That's pretty much all we have to
     // worry about in this stylesheet.
diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss
index da1c23b664..3b3eccfd60 100644
--- a/res/css/views/rooms/_RoomSublist2.scss
+++ b/res/css/views/rooms/_RoomSublist2.scss
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+// TODO: Rename to mx_RoomSublist during replacement of old component
+
 @import "../../../../node_modules/react-resizable/css/styles.css";
 
 .mx_RoomSublist2 {
diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss
index bb27942f81..3151bb8716 100644
--- a/res/css/views/rooms/_RoomTile2.scss
+++ b/res/css/views/rooms/_RoomTile2.scss
@@ -14,15 +14,24 @@ 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 {
-    width: 100%;
-    padding-bottom: 12px;
+    width: calc(100% - 11px); // 8px for padding (4px on either side), 3px for margin
+    margin-bottom: 4px;
+    margin-right: 3px;
+    padding: 4px;
 
     // The tile is also a flexbox row itself
     display: flex;
     flex-wrap: wrap;
 
+    &.mx_RoomTile2_selected {
+        background-color: $roomtile2-selected-bg-color;
+        border-radius: 32px;
+    }
+
     .mx_RoomTile2_avatarContainer {
         margin-right: 8px;
     }
diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss
index 683c02528d..5aeb125774 100644
--- a/res/themes/light/css/_light.scss
+++ b/res/themes/light/css/_light.scss
@@ -176,6 +176,7 @@ $header-divider-color: #91A1C0;
 // TODO: Dark theme
 $roomtile2-preview-color: #9e9e9e;
 $roomtile2-badge-color: #61708b;
+$roomtile2-selected-bg-color: #FFF;
 
 $roomtile-name-color: #61708b;
 $roomtile-badge-fg-color: $accent-fg-color;
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/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx
index bf0e72beeb..00419465eb 100644
--- a/src/components/structures/LeftPanel2.tsx
+++ b/src/components/structures/LeftPanel2.tsx
@@ -86,26 +86,35 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
     }
 
     private renderHeader(): React.ReactNode {
-        // TODO: Use real profile info
+        // 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;
+        }
         return (
             <div className="mx_LeftPanel2_userHeader">
                 <div className="mx_LeftPanel2_headerRow">
                     <span className="mx_LeftPanel2_userAvatarContainer">
                         <BaseAvatar
                             idName={MatrixClientPeg.get().getUserId()}
-                            name={"TODO: Display Name"}
-                            url={null}
+                            name={displayName}
+                            url={avatarUrl}
                             width={avatarSize}
                             height={avatarSize}
                             resizeMethod="crop"
                             className="mx_LeftPanel2_userAvatar"
                         />
                     </span>
-                    <span className="mx_LeftPanel2_userName">Irene</span>
+                    <span className="mx_LeftPanel2_userName">{displayName}</span>
                 </div>
                 <div className="mx_LeftPanel2_headerRow mx_LeftPanel2_breadcrumbsContainer">
                     <RoomBreadcrumbs />
diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx
index 8a51327ae2..09d7b46ba5 100644
--- a/src/components/views/rooms/RoomTile2.tsx
+++ b/src/components/views/rooms/RoomTile2.tsx
@@ -23,7 +23,6 @@ import classNames from "classnames";
 import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
 import AccessibleButton from "../../views/elements/AccessibleButton";
 import RoomAvatar from "../../views/avatars/RoomAvatar";
-import Tooltip from "../../views/elements/Tooltip";
 import dis from '../../../dispatcher/dispatcher';
 import { Key } from "../../../Keyboard";
 import * as RoomNotifs from '../../../RoomNotifs';
@@ -32,6 +31,7 @@ import * as Unread from '../../../Unread';
 import * as FormattingUtils from "../../../utils/FormattingUtils";
 import { MatrixClientPeg } from "../../../MatrixClientPeg";
 import { MatrixEvent } from "matrix-js-sdk/src/models/event";
+import ActiveRoomObserver from "../../../ActiveRoomObserver";
 
 /*******************************************************************
  *   CAUTION                                                       *
@@ -66,6 +66,7 @@ interface INotificationState {
 interface IState {
     hover: boolean;
     notificationState: INotificationState;
+    selected: boolean;
 }
 
 export default class RoomTile2 extends React.Component<IProps, IState> {
@@ -88,12 +89,14 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
         this.state = {
             hover: false,
             notificationState: this.getNotificationState(),
+            selected: ActiveRoomObserver.activeRoomId === this.props.room.roomId,
         };
 
         this.props.room.on("Room.receipt", this.handleRoomEventUpdate);
         this.props.room.on("Room.timeline", this.handleRoomEventUpdate);
         this.props.room.on("Room.redaction", this.handleRoomEventUpdate);
         MatrixClientPeg.get().on("Event.decrypted", this.handleRoomEventUpdate);
+        ActiveRoomObserver.addListener(this.props.room.roomId, this.onActiveRoomUpdate);
     }
 
     public componentWillUnmount() {
@@ -101,6 +104,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
             this.props.room.removeListener("Room.receipt", this.handleRoomEventUpdate);
             this.props.room.removeListener("Room.timeline", this.handleRoomEventUpdate);
             this.props.room.removeListener("Room.redaction", this.handleRoomEventUpdate);
+            ActiveRoomObserver.removeListener(this.props.room.roomId, this.onActiveRoomUpdate);
         }
         if (MatrixClientPeg.get()) {
             MatrixClientPeg.get().removeListener("Event.decrypted", this.handleRoomEventUpdate);
@@ -187,6 +191,10 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
         });
     };
 
+    private onActiveRoomUpdate = (isActive: boolean) => {
+        this.setState({selected: isActive});
+    };
+
     public render(): React.ReactElement {
         // TODO: Collapsed state
         // TODO: Invites
@@ -195,6 +203,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
 
         const classes = classNames({
             'mx_RoomTile2': true,
+            'mx_RoomTile2_selected': this.state.selected,
         });
 
         let badge;

From 829bf3c774498bacad2aadbaecc582821457ccab Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 5 Jun 2020 14:11:04 -0600
Subject: [PATCH 7/8] Add another TODO comment

---
 res/css/views/rooms/_RoomSublist2.scss | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss
index 3b3eccfd60..e6e5af3b48 100644
--- a/res/css/views/rooms/_RoomSublist2.scss
+++ b/res/css/views/rooms/_RoomSublist2.scss
@@ -16,6 +16,8 @@ limitations under the License.
 
 // TODO: Rename to mx_RoomSublist during replacement of old component
 
+// TODO: Just use the 3 selectors we need from this instead of importing it.
+// We're going to end up with heavy modifications anyways.
 @import "../../../../node_modules/react-resizable/css/styles.css";
 
 .mx_RoomSublist2 {

From 2806c8c18ba4f8bf864b981b54011c5211c333ab Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Fri, 5 Jun 2020 14:13:28 -0600
Subject: [PATCH 8/8] Fix temporary class

---
 src/components/structures/LeftPanel2.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx
index 00419465eb..c66c0a6799 100644
--- a/src/components/structures/LeftPanel2.tsx
+++ b/src/components/structures/LeftPanel2.tsx
@@ -131,7 +131,7 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
         );
 
         const searchBox = (<SearchBox
-            className="mx_LeftPanel2_TODO_filterRooms"
+            className="mx_LeftPanel2_filterRoomsSearch"
             enableRoomSearchFocus={true}
             blurredPlaceholder={_t('Filter')}
             placeholder={_t('Filter rooms…')}