diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 4fea201e9a..31996f23a2 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -15,25 +15,21 @@ limitations under the License. */ .mx_RoomSubList { - min-height: 80px; - flex: 0; + min-height: 31px; + flex: 0 0 auto; display: flex; flex-direction: column; } -.mx_RoomSubList_hidden { - min-height: unset; -} - -.mx_RoomSubList_resizer { - width: 100%; - height: 3px; - background-color: $roomsublist-background; +.mx_RoomSubList_nonEmpty { + min-height: 80px; + flex: 1; } .mx_RoomSubList_labelContainer { display: flex; flex-direction: row; + flex: 0 0 auto; } .mx_RoomSubList_label { diff --git a/res/css/views/rooms/_RoomList.scss b/res/css/views/rooms/_RoomList.scss index 3ce47a4bc6..3cb5be1952 100644 --- a/res/css/views/rooms/_RoomList.scss +++ b/res/css/views/rooms/_RoomList.scss @@ -23,6 +23,11 @@ limitations under the License. flex-direction: column; } +/* hide resize handles next to collapsed / empty sublists */ +.mx_RoomList .mx_RoomSubList:not(.mx_RoomSubList_nonEmpty) + .mx_ResizeHandle { + display: none; +} + .mx_RoomList_expandButton { margin-left: 8px; cursor: pointer; diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index a6117b1a00..e00f0f2d03 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -50,14 +50,10 @@ const RoomSubList = React.createClass({ showSpinner: PropTypes.bool, // true to show a spinner if 0 elements when expanded collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed? onHeaderClick: PropTypes.func, - alwaysShowHeader: PropTypes.bool, incomingCall: PropTypes.object, - onShowMoreRooms: PropTypes.func, searchFilter: PropTypes.string, - emptyContent: PropTypes.node, // content shown if the list is empty headerItems: PropTypes.node, // content shown in the sublist header extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles - showEmpty: PropTypes.bool, }, getInitialState: function() { @@ -71,11 +67,8 @@ const RoomSubList = React.createClass({ return { onHeaderClick: function() { }, // NOP - onShowMoreRooms: function() { - }, // NOP extraTiles: [], isInvite: false, - showEmpty: true, }; }, @@ -138,7 +131,6 @@ const RoomSubList = React.createClass({ // The header isCollapsable, so the click is to be interpreted as collapse and truncation logic const isHidden = !this.state.hidden; this.setState({hidden: isHidden}); - this.props.onShowMoreRooms(); this.props.onHeaderClick(isHidden); } else { // The header is stuck, so the click is to be interpreted as a scroll to the header @@ -271,25 +263,21 @@ const RoomSubList = React.createClass({ const subListNotifCount = subListNotifications[0]; const subListNotifHighlight = subListNotifications[1]; - const chevronClasses = classNames({ - 'mx_RoomSubList_chevron': true, - 'mx_RoomSubList_chevronRight': this.state.hidden, - 'mx_RoomSubList_chevronDown': !this.state.hidden, - }); - - const badgeClasses = classNames({ - 'mx_RoomSubList_badge': true, - 'mx_RoomSubList_badgeHighlight': subListNotifHighlight, - }); let badge; - if (subListNotifCount > 0) { - badge =
- { FormattingUtils.formatCount(subListNotifCount) } -
; - } else if (this.props.isInvite) { - // no notifications but highlight anyway because this is an invite badge - badge =
!
; + if (this.state.hidden) { + const badgeClasses = classNames({ + 'mx_RoomSubList_badge': true, + 'mx_RoomSubList_badgeHighlight': subListNotifHighlight, + }); + if (subListNotifCount > 0) { + badge =
+ { FormattingUtils.formatCount(subListNotifCount) } +
; + } else if (this.props.isInvite) { + // no notifications but highlight anyway because this is an invite badge + badge =
!
; + } } // When collapsed, allow a long hover on the header to show user @@ -323,12 +311,22 @@ const RoomSubList = React.createClass({ ); } - const tabindex = this.props.searchFilter === "" ? "0" : "-1"; + const len = this.state.sortedList.length + this.props.extraTiles.length; + let chevron; + if (len) { + const chevronClasses = classNames({ + 'mx_RoomSubList_chevron': true, + 'mx_RoomSubList_chevronRight': this.state.hidden, + 'mx_RoomSubList_chevronDown': !this.state.hidden, + }); + chevron = (
); + } + const tabindex = this.props.searchFilter === "" ? "0" : "-1"; return (
-
+ { chevron } { this.props.collapsed ? '' : this.props.label } { badge } { incomingCall } @@ -339,64 +337,43 @@ const RoomSubList = React.createClass({ }, render: function() { - let content; - - if (this.props.showEmpty) { - // this is new behaviour with still controversial UX in that in hiding RoomSubLists the drop zones for DnD - // are also gone so when filtering users can't DnD rooms to some tags but is a lot cleaner otherwise. - if (this.state.sortedList.length === 0 && !this.props.searchFilter && this.props.extraTiles.length === 0) { - content = this.props.emptyContent; - } else { - content = this.makeRoomTiles(); - content.push(...this.props.extraTiles); - } - } else { - if (this.state.sortedList.length === 0 && this.props.extraTiles.length === 0) { - // if no search filter is applied and there is a placeholder defined then show it, otherwise show nothing - if (!this.props.searchFilter && this.props.emptyContent) { - content = this.props.emptyContent; - } else { - // don't show an empty sublist - return null; - } - } else { - content = this.makeRoomTiles(); - content.push(...this.props.extraTiles); - } - } - const len = this.state.sortedList.length + this.props.extraTiles.length; - if (len) { + const subListClasses = classNames({ + "mx_RoomSubList": true, + "mx_RoomSubList_nonEmpty": len && !this.state.hidden, + }); if (this.state.hidden) { - return
+ return
{this._getHeaderJsx()}
; } else { const heightEstimation = (len * 40) + 31; const style = { - flexBasis: `${heightEstimation}px`, + flexGrow: `${heightEstimation}`, maxHeight: `${heightEstimation}px`, }; const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); - return
+ const tiles = this.makeRoomTiles(); + tiles.push(...this.props.extraTiles); + return
{this._getHeaderJsx()} - { content } + { tiles }
; } } else { const Loader = sdk.getComponent("elements.Spinner"); - if (this.props.showSpinner) { + let content; + if (this.props.showSpinner && !this.state.hidden) { content = ; } return (
- {this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined} - { this.state.hidden ? undefined : content } -
+ { this._getHeaderJsx() } + { content }
); } diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index ef82075e89..c2baccc676 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -406,71 +406,6 @@ module.exports = React.createClass({ } }, - onShowMoreRooms: function() { - // kick gemini in the balls to get it to wake up - // XXX: uuuuuuugh. - if (!this._gemScroll) return; - this._gemScroll.forceUpdate(); - }, - - _getEmptyContent: function(section) { - if (this.state.selectedTags.length > 0) { - return null; - } - - const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); - - if (this.props.collapsed) { - return ; - } - - const StartChatButton = sdk.getComponent('elements.StartChatButton'); - const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); - const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); - - let tip = null; - - switch (section) { - case 'im.vector.fake.direct': - tip =
- { _t( - "Press to start a chat with someone", - {}, - { 'StartChatButton': }, - ) } -
; - break; - case 'im.vector.fake.recent': - tip =
- { _t( - "You're not in any rooms yet! Press to make a room or"+ - " to browse the directory", - {}, - { - 'CreateRoomButton': , - 'RoomDirectoryButton': , - }, - ) } -
; - break; - } - - if (tip) { - return
- { tip } -
; - } - - // We don't want to display drop targets if there are no room tiles to drag'n'drop - if (this.state.totalRoomCount === 0) { - return null; - } - - const labelText = phraseForSection(section); - - return ; - }, - _getHeaderItems: function(section) { const StartChatButton = sdk.getComponent('elements.StartChatButton'); const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); @@ -508,28 +443,21 @@ module.exports = React.createClass({ render: function() { const RoomSubList = sdk.getComponent('structures.RoomSubList'); - // XXX: we can't detect device-level (localStorage) settings onChange as the SettingsStore does not notify - // so checking on every render is the sanest thing at this time. - const showEmpty = SettingsStore.getValue('RoomSubList.showEmpty'); - - const self = this; - - function mapProps(subListsProps) { + const mapProps = (subListsProps) => { const defaultProps = { - collapsed: self.props.collapsed, - searchFilter: self.props.searchFilter, - onShowMoreRooms: self.onShowMoreRooms, - showEmpty: showEmpty, - incomingCall: self.state.incomingCall, + collapsed: this.props.collapsed, + searchFilter: this.props.searchFilter, + incomingCall: this.state.incomingCall, }; + + subListsProps = subListsProps.filter((props => { + const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0); + return len !== 0 || props.onAddRoom; + })); + return subListsProps.reduce((components, props, i) => { props = Object.assign({}, defaultProps, props); const isLast = i === subListsProps.length - 1; - const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0); - // empty and no add button? dont render - if (!len && !props.onAddRoom) { - return components; - } const {key, label, ... otherProps} = props; const chosenKey = key || label; @@ -548,83 +476,69 @@ module.exports = React.createClass({ let subLists = [ { list: [], - extraTiles: this._makeGroupInviteTiles(self.props.searchFilter), + extraTiles: this._makeGroupInviteTiles(this.props.searchFilter), label: _t('Community Invites'), order: "recent", isInvite: true, }, { - list: self.state.lists['im.vector.fake.invite'], + list: this.state.lists['im.vector.fake.invite'], label: _t('Invites'), order: "recent", isInvite: true, }, { - list: self.state.lists['m.favourite'], + list: this.state.lists['m.favourite'], label: _t('Favourites'), tagName: "m.favourite", - emptyContent: this._getEmptyContent('m.favourite'), order: "manual", }, { - list: self.state.lists['im.vector.fake.direct'], + list: this.state.lists['im.vector.fake.direct'], label: _t('People'), tagName: "im.vector.fake.direct", - emptyContent: this._getEmptyContent('im.vector.fake.direct'), headerItems: this._getHeaderItems('im.vector.fake.direct'), order: "recent", - alwaysShowHeader: true, onAddRoom: () => {dis.dispatch({action: 'view_create_chat'})}, }, { - list: self.state.lists['im.vector.fake.recent'], + list: this.state.lists['im.vector.fake.recent'], label: _t('Rooms'), - emptyContent: this._getEmptyContent('im.vector.fake.recent'), headerItems: this._getHeaderItems('im.vector.fake.recent'), order: "recent", onAddRoom: () => {dis.dispatch({action: 'view_create_room'})}, }, ]; - const tagSubLists = Object.keys(self.state.lists) + const tagSubLists = Object.keys(this.state.lists) .filter((tagName) => { return !tagName.match(STANDARD_TAGS_REGEX); }).map((tagName) => { return { - list: self.state.lists[tagName], + list: this.state.lists[tagName], key: tagName, label: labelForTagName(tagName), tagName: tagName, - emptyContent: this._getEmptyContent(tagName), order: "manual", }; }); subLists = subLists.concat(tagSubLists); subLists = subLists.concat([ { - list: self.state.lists['m.lowpriority'], + list: this.state.lists['m.lowpriority'], label: _t('Low priority'), tagName: "m.lowpriority", - emptyContent: this._getEmptyContent('m.lowpriority'), order: "recent", }, { - list: self.state.lists['im.vector.fake.archived'], - emptyContent: self.props.collapsed ? - null : -
-
- { _t('You have no historical rooms') } -
-
, + list: this.state.lists['im.vector.fake.archived'], label: _t('Historical'), order: "recent", - alwaysShowHeader: true, startAsHidden: true, - showSpinner: self.state.isLoadingLeftRooms, - onHeaderClick: self.onArchivedHeaderClick, + showSpinner: this.state.isLoadingLeftRooms, + onHeaderClick: this.onArchivedHeaderClick, }, { - list: self.state.lists['m.server_notice'], + list: this.state.lists['m.server_notice'], label: _t('System Alerts'), tagName: "m.lowpriority", order: "recent",