Merge pull request #1954 from matrix-org/t3chguy/hide_empty_sublist

hide empty roomsublists when filtering via search/tagpanel
This commit is contained in:
Michael Telatynski 2018-06-25 11:30:03 +01:00 committed by GitHub
commit 301b8b8c56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 95 deletions

View file

@ -2,7 +2,6 @@
src/autocomplete/AutocompleteProvider.js src/autocomplete/AutocompleteProvider.js
src/autocomplete/Autocompleter.js src/autocomplete/Autocompleter.js
src/autocomplete/EmojiProvider.js
src/autocomplete/UserProvider.js src/autocomplete/UserProvider.js
src/component-index.js src/component-index.js
src/components/structures/BottomLeftMenu.js src/components/structures/BottomLeftMenu.js
@ -17,7 +16,6 @@ src/components/structures/MessagePanel.js
src/components/structures/NotificationPanel.js src/components/structures/NotificationPanel.js
src/components/structures/RoomDirectory.js src/components/structures/RoomDirectory.js
src/components/structures/RoomStatusBar.js src/components/structures/RoomStatusBar.js
src/components/structures/RoomSubList.js
src/components/structures/RoomView.js src/components/structures/RoomView.js
src/components/structures/ScrollPanel.js src/components/structures/ScrollPanel.js
src/components/structures/SearchBox.js src/components/structures/SearchBox.js
@ -29,7 +27,6 @@ src/components/views/avatars/BaseAvatar.js
src/components/views/avatars/MemberAvatar.js src/components/views/avatars/MemberAvatar.js
src/components/views/create_room/RoomAlias.js src/components/views/create_room/RoomAlias.js
src/components/views/dialogs/ChangelogDialog.js src/components/views/dialogs/ChangelogDialog.js
src/components/views/dialogs/ChatCreateOrReuseDialog.js
src/components/views/dialogs/DeactivateAccountDialog.js src/components/views/dialogs/DeactivateAccountDialog.js
src/components/views/dialogs/SetPasswordDialog.js src/components/views/dialogs/SetPasswordDialog.js
src/components/views/dialogs/UnknownDeviceDialog.js src/components/views/dialogs/UnknownDeviceDialog.js
@ -37,7 +34,6 @@ src/components/views/directory/NetworkDropdown.js
src/components/views/elements/AddressSelector.js src/components/views/elements/AddressSelector.js
src/components/views/elements/DeviceVerifyButtons.js src/components/views/elements/DeviceVerifyButtons.js
src/components/views/elements/DirectorySearchBox.js src/components/views/elements/DirectorySearchBox.js
src/components/views/elements/EditableText.js
src/components/views/elements/ImageView.js src/components/views/elements/ImageView.js
src/components/views/elements/InlineSpinner.js src/components/views/elements/InlineSpinner.js
src/components/views/elements/MemberEventListSummary.js src/components/views/elements/MemberEventListSummary.js
@ -81,7 +77,6 @@ src/components/views/rooms/TopUnreadMessagesBar.js
src/components/views/rooms/UserTile.js src/components/views/rooms/UserTile.js
src/components/views/settings/AddPhoneNumber.js src/components/views/settings/AddPhoneNumber.js
src/components/views/settings/ChangeAvatar.js src/components/views/settings/ChangeAvatar.js
src/components/views/settings/ChangeDisplayName.js
src/components/views/settings/ChangePassword.js src/components/views/settings/ChangePassword.js
src/components/views/settings/DevicesPanel.js src/components/views/settings/DevicesPanel.js
src/components/views/settings/IntegrationsManager.js src/components/views/settings/IntegrationsManager.js

View file

@ -1,6 +1,7 @@
/* /*
Copyright 2017 Vector Creations Ltd
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,30 +16,24 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
'use strict'; import React from 'react';
import classNames from 'classnames';
var React = require('react'); import sdk from '../../index';
var ReactDOM = require('react-dom');
var classNames = require('classnames');
var sdk = require('../../index');
import { Droppable } from 'react-beautiful-dnd'; import { Droppable } from 'react-beautiful-dnd';
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
var dis = require('../../dispatcher'); import dis from '../../dispatcher';
var Unread = require('../../Unread'); import Unread from '../../Unread';
var MatrixClientPeg = require('../../MatrixClientPeg'); import * as RoomNotifs from '../../RoomNotifs';
var RoomNotifs = require('../../RoomNotifs'); import * as FormattingUtils from '../../utils/FormattingUtils';
var FormattingUtils = require('../../utils/FormattingUtils');
var AccessibleButton = require('../../components/views/elements/AccessibleButton');
import Modal from '../../Modal';
import { KeyCode } from '../../Keyboard'; import { KeyCode } from '../../Keyboard';
// turn this on for drop & drag console debugging galore // turn this on for drop & drag console debugging galore
var debug = false; const debug = false;
const TRUNCATE_AT = 10; const TRUNCATE_AT = 10;
var RoomSubList = React.createClass({ const RoomSubList = React.createClass({
displayName: 'RoomSubList', displayName: 'RoomSubList',
debug: debug, debug: debug,
@ -77,8 +72,10 @@ var RoomSubList = React.createClass({
getDefaultProps: function() { getDefaultProps: function() {
return { return {
onHeaderClick: function() {}, // NOP onHeaderClick: function() {
onShowMoreRooms: function() {}, // NOP }, // NOP
onShowMoreRooms: function() {
}, // NOP
extraTiles: [], extraTiles: [],
isInvite: false, isInvite: false,
}; };
@ -115,7 +112,7 @@ var RoomSubList = React.createClass({
// The header is collapsable if it is hidden or not stuck // The header is collapsable if it is hidden or not stuck
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
isCollapsableOnClick: function() { isCollapsableOnClick: function() {
var stuck = this.refs.header.dataset.stuck; const stuck = this.refs.header.dataset.stuck;
if (this.state.hidden || stuck === undefined || stuck === "none") { if (this.state.hidden || stuck === undefined || stuck === "none") {
return true; return true;
} else { } else {
@ -141,12 +138,12 @@ var RoomSubList = React.createClass({
onClick: function(ev) { onClick: function(ev) {
if (this.isCollapsableOnClick()) { if (this.isCollapsableOnClick()) {
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic // The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
var isHidden = !this.state.hidden; const isHidden = !this.state.hidden;
this.setState({ hidden : isHidden }); this.setState({hidden: isHidden});
if (isHidden) { if (isHidden) {
// as good a way as any to reset the truncate state // as good a way as any to reset the truncate state
this.setState({ truncateAt : TRUNCATE_AT }); this.setState({truncateAt: TRUNCATE_AT});
} }
this.props.onShowMoreRooms(); this.props.onShowMoreRooms();
@ -161,7 +158,7 @@ var RoomSubList = React.createClass({
dis.dispatch({ dis.dispatch({
action: 'view_room', action: 'view_room',
room_id: roomId, room_id: roomId,
clear_search: (ev && (ev.keyCode == KeyCode.ENTER || ev.keyCode == KeyCode.SPACE)), clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)),
}); });
}, },
@ -171,17 +168,17 @@ var RoomSubList = React.createClass({
}, },
_shouldShowMentionBadge: function(roomNotifState) { _shouldShowMentionBadge: function(roomNotifState) {
return roomNotifState != RoomNotifs.MUTE; return roomNotifState !== RoomNotifs.MUTE;
}, },
/** /**
* Total up all the notification counts from the rooms * Total up all the notification counts from the rooms
* *
* @param {Number} If supplied will only total notifications for rooms outside the truncation number * @param {Number} truncateAt If supplied will only total notifications for rooms outside the truncation number
* @returns {Array} The array takes the form [total, highlight] where highlight is a bool * @returns {Array} The array takes the form [total, highlight] where highlight is a bool
*/ */
roomNotificationCount: function(truncateAt) { roomNotificationCount: function(truncateAt) {
var self = this; const self = this;
if (this.props.isInvite) { if (this.props.isInvite) {
return [0, true]; return [0, true];
@ -189,9 +186,9 @@ var RoomSubList = React.createClass({
return this.props.list.reduce(function(result, room, index) { return this.props.list.reduce(function(result, room, index) {
if (truncateAt === undefined || index >= truncateAt) { if (truncateAt === undefined || index >= truncateAt) {
var roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId); const roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
var highlight = room.getUnreadNotificationCount('highlight') > 0; const highlight = room.getUnreadNotificationCount('highlight') > 0;
var notificationCount = room.getUnreadNotificationCount(); const notificationCount = room.getUnreadNotificationCount();
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState); const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
const mentionBadges = highlight && self._shouldShowMentionBadge(roomNotifState); const mentionBadges = highlight && self._shouldShowMentionBadge(roomNotifState);
@ -241,29 +238,27 @@ var RoomSubList = React.createClass({
}, },
_getHeaderJsx: function() { _getHeaderJsx: function() {
var TintableSvg = sdk.getComponent("elements.TintableSvg"); const subListNotifications = this.roomNotificationCount();
const subListNotifCount = subListNotifications[0];
const subListNotifHighlight = subListNotifications[1];
var subListNotifications = this.roomNotificationCount(); const totalTiles = this.props.list.length + (this.props.extraTiles || []).length;
var subListNotifCount = subListNotifications[0]; const roomCount = totalTiles > 0 ? totalTiles : '';
var subListNotifHighlight = subListNotifications[1];
var totalTiles = this.props.list.length + (this.props.extraTiles || []).length; const chevronClasses = classNames({
var roomCount = totalTiles > 0 ? totalTiles : '';
var chevronClasses = classNames({
'mx_RoomSubList_chevron': true, 'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronRight': this.state.hidden, 'mx_RoomSubList_chevronRight': this.state.hidden,
'mx_RoomSubList_chevronDown': !this.state.hidden, 'mx_RoomSubList_chevronDown': !this.state.hidden,
}); });
var badgeClasses = classNames({ const badgeClasses = classNames({
'mx_RoomSubList_badge': true, 'mx_RoomSubList_badge': true,
'mx_RoomSubList_badgeHighlight': subListNotifHighlight, 'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
}); });
var badge; let badge;
if (subListNotifCount > 0) { if (subListNotifCount > 0) {
badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>; badge = <div className={badgeClasses}>{FormattingUtils.formatCount(subListNotifCount)}</div>;
} else if (this.props.isInvite) { } else if (this.props.isInvite) {
// no notifications but highlight anyway because this is an invite badge // no notifications but highlight anyway because this is an invite badge
badge = <div className={badgeClasses}>!</div>; badge = <div className={badgeClasses}>!</div>;
@ -271,7 +266,7 @@ var RoomSubList = React.createClass({
// When collapsed, allow a long hover on the header to show user // When collapsed, allow a long hover on the header to show user
// the full tag name and room count // the full tag name and room count
var title; let title;
if (this.props.collapsed) { if (this.props.collapsed) {
title = this.props.label; title = this.props.label;
if (roomCount !== '') { if (roomCount !== '') {
@ -279,63 +274,66 @@ var RoomSubList = React.createClass({
} }
} }
var incomingCall; let incomingCall;
if (this.props.incomingCall) { if (this.props.incomingCall) {
var self = this; const self = this;
// Check if the incoming call is for this section // Check if the incoming call is for this section
var incomingCallRoom = this.props.list.filter(function(room) { const incomingCallRoom = this.props.list.filter(function(room) {
return self.props.incomingCall.roomId === room.roomId; return self.props.incomingCall.roomId === room.roomId;
}); });
if (incomingCallRoom.length === 1) { if (incomingCallRoom.length === 1) {
var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox"); const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>; incomingCall =
<IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={this.props.incomingCall} />;
} }
} }
var tabindex = this.props.searchFilter === "" ? "0" : "-1"; const tabindex = this.props.searchFilter === "" ? "0" : "-1";
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return ( return (
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header"> <div className="mx_RoomSubList_labelContainer" title={title} ref="header">
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}> <AccessibleButton onClick={this.onClick} className="mx_RoomSubList_label" tabIndex={tabindex}>
{ this.props.collapsed ? '' : this.props.label } {this.props.collapsed ? '' : this.props.label}
<div className="mx_RoomSubList_roomCount">{ roomCount }</div> <div className="mx_RoomSubList_roomCount">{roomCount}</div>
<div className={chevronClasses}></div> <div className={chevronClasses} />
{ badge } {badge}
{ incomingCall } {incomingCall}
</AccessibleButton> </AccessibleButton>
</div> </div>
); );
}, },
_createOverflowTile: function(overflowCount, totalCount) { _createOverflowTile: function(overflowCount, totalCount) {
var content = <div className="mx_RoomSubList_chevronDown"></div>; let content = <div className="mx_RoomSubList_chevronDown" />;
var overflowNotifications = this.roomNotificationCount(TRUNCATE_AT); const overflowNotifications = this.roomNotificationCount(TRUNCATE_AT);
var overflowNotifCount = overflowNotifications[0]; const overflowNotifCount = overflowNotifications[0];
var overflowNotifHighlight = overflowNotifications[1]; const overflowNotifHighlight = overflowNotifications[1];
if (overflowNotifCount && !this.props.collapsed) { if (overflowNotifCount && !this.props.collapsed) {
content = FormattingUtils.formatCount(overflowNotifCount); content = FormattingUtils.formatCount(overflowNotifCount);
} }
var badgeClasses = classNames({ const badgeClasses = classNames({
'mx_RoomSubList_moreBadge': true, 'mx_RoomSubList_moreBadge': true,
'mx_RoomSubList_moreBadgeNotify': overflowNotifCount && !this.props.collapsed, 'mx_RoomSubList_moreBadgeNotify': overflowNotifCount && !this.props.collapsed,
'mx_RoomSubList_moreBadgeHighlight': overflowNotifHighlight && !this.props.collapsed, 'mx_RoomSubList_moreBadgeHighlight': overflowNotifHighlight && !this.props.collapsed,
}); });
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return ( return (
<AccessibleButton className="mx_RoomSubList_ellipsis" onClick={this._showFullMemberList}> <AccessibleButton className="mx_RoomSubList_ellipsis" onClick={this._showFullMemberList}>
<div className="mx_RoomSubList_line"></div> <div className="mx_RoomSubList_line" />
<div className="mx_RoomSubList_more">{ _t("more") }</div> <div className="mx_RoomSubList_more">{_t("more")}</div>
<div className={ badgeClasses }>{ content }</div> <div className={badgeClasses}>{content}</div>
</AccessibleButton> </AccessibleButton>
); );
}, },
_showFullMemberList: function() { _showFullMemberList: function() {
this.setState({ this.setState({
truncateAt: -1 truncateAt: -1,
}); });
this.props.onShowMoreRooms(); this.props.onShowMoreRooms();
@ -343,37 +341,39 @@ var RoomSubList = React.createClass({
}, },
render: function() { render: function() {
var connectDropTarget = this.props.connectDropTarget; const TruncatedList = sdk.getComponent('elements.TruncatedList');
var TruncatedList = sdk.getComponent('elements.TruncatedList');
var label = this.props.collapsed ? null : this.props.label;
let content; let content;
if (this.state.sortedList.length === 0 && !this.props.searchFilter && this.props.extraTiles.length === 0) { if (this.state.sortedList.length === 0 && this.props.extraTiles.length === 0) {
content = this.props.emptyContent; // 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 { } else {
content = this.makeRoomTiles(); content = this.makeRoomTiles();
content.push(...this.props.extraTiles); content.push(...this.props.extraTiles);
} }
if (this.state.sortedList.length > 0 || this.props.extraTiles.length > 0 || this.props.editable) { if (this.state.sortedList.length > 0 || this.props.extraTiles.length > 0 || this.props.editable) {
var subList; let subList;
var classes = "mx_RoomSubList"; const classes = "mx_RoomSubList";
if (!this.state.hidden) { if (!this.state.hidden) {
subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt} subList = <TruncatedList className={classes} truncateAt={this.state.truncateAt}
createOverflowElement={this._createOverflowTile} > createOverflowElement={this._createOverflowTile}>
{ content } {content}
</TruncatedList>; </TruncatedList>;
} } else {
else { subList = <TruncatedList className={classes}>
subList = <TruncatedList className={ classes }> </TruncatedList>;
</TruncatedList>;
} }
const subListContent = <div> const subListContent = <div>
{ this._getHeaderJsx() } {this._getHeaderJsx()}
{ subList } {subList}
</div>; </div>;
return this.props.editable ? return this.props.editable ?
@ -381,23 +381,22 @@ var RoomSubList = React.createClass({
droppableId={"room-sub-list-droppable_" + this.props.tagName} droppableId={"room-sub-list-droppable_" + this.props.tagName}
type="draggable-RoomTile" type="draggable-RoomTile"
> >
{ (provided, snapshot) => ( {(provided, snapshot) => (
<div ref={provided.innerRef}> <div ref={provided.innerRef}>
{ subListContent } {subListContent}
</div> </div>
) } )}
</Droppable> : subListContent; </Droppable> : subListContent;
} } else {
else { const Loader = sdk.getComponent("elements.Spinner");
var Loader = sdk.getComponent("elements.Spinner");
return ( return (
<div className="mx_RoomSubList"> <div className="mx_RoomSubList">
{ this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined } {this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined}
{ (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined } {(this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined}
</div> </div>
); );
} }
} },
}); });
module.exports = RoomSubList; module.exports = RoomSubList;