2018-04-12 01:58:04 +03:00
|
|
|
/*
|
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2018-06-25 11:41:28 +03:00
|
|
|
Copyright 2017 Vector Creations Ltd
|
2019-04-02 01:06:33 +03:00
|
|
|
Copyright 2018, 2019 New Vector Ltd
|
2019-10-17 17:53:39 +03:00
|
|
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2019-10-17 17:53:39 +03:00
|
|
|
import React, {createRef} from 'react';
|
2018-06-25 11:51:23 +03:00
|
|
|
import classNames from 'classnames';
|
2019-12-20 04:19:56 +03:00
|
|
|
import * as sdk from '../../index';
|
2018-06-25 11:51:23 +03:00
|
|
|
import dis from '../../dispatcher';
|
2019-12-20 04:19:56 +03:00
|
|
|
import * as Unread from '../../Unread';
|
2018-06-25 11:51:23 +03:00
|
|
|
import * as RoomNotifs from '../../RoomNotifs';
|
|
|
|
import * as FormattingUtils from '../../utils/FormattingUtils';
|
2018-11-12 14:57:21 +03:00
|
|
|
import IndicatorScrollbar from './IndicatorScrollbar';
|
2019-12-16 20:14:03 +03:00
|
|
|
import {Key} from '../../Keyboard';
|
2018-06-27 11:49:58 +03:00
|
|
|
import { Group } from 'matrix-js-sdk';
|
2018-06-30 19:06:33 +03:00
|
|
|
import PropTypes from 'prop-types';
|
2019-02-13 16:51:09 +03:00
|
|
|
import RoomTile from "../views/rooms/RoomTile";
|
|
|
|
import LazyRenderList from "../views/elements/LazyRenderList";
|
2019-05-21 21:16:34 +03:00
|
|
|
import {_t} from "../../languageHandler";
|
2020-01-16 04:45:16 +03:00
|
|
|
import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex";
|
2020-04-06 22:52:36 +03:00
|
|
|
import toRem from "../../utils/rem";
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
// turn this on for drop & drag console debugging galore
|
2018-06-25 11:51:23 +03:00
|
|
|
const debug = false;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2020-05-05 17:39:37 +03:00
|
|
|
class RoomTileErrorBoundary extends React.PureComponent {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
error: null,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static getDerivedStateFromError(error) {
|
|
|
|
// Side effects are not permitted here, so we only update the state so
|
|
|
|
// that the next render shows an error message.
|
|
|
|
return { error };
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidCatch(error, { componentStack }) {
|
|
|
|
// Browser consoles are better at formatting output when native errors are passed
|
|
|
|
// in their own `console.error` invocation.
|
|
|
|
console.error(error);
|
|
|
|
console.error(
|
|
|
|
"The above error occured while React was rendering the following components:",
|
|
|
|
componentStack,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
if (this.state.error) {
|
|
|
|
return (<div className="mx_RoomTile mx_RoomTileError">
|
|
|
|
{this.props.roomId}
|
|
|
|
</div>);
|
|
|
|
} else {
|
|
|
|
return this.props.children;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
export default class RoomSubList extends React.PureComponent {
|
|
|
|
static displayName = 'RoomSubList';
|
|
|
|
static debug = debug;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
static propTypes = {
|
2018-06-30 19:06:33 +03:00
|
|
|
list: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
|
|
label: PropTypes.string.isRequired,
|
|
|
|
tagName: PropTypes.string,
|
2019-05-21 21:16:34 +03:00
|
|
|
addRoomLabel: PropTypes.string,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
// passed through to RoomTile and used to highlight room with `!` regardless of notifications count
|
2018-06-30 19:06:33 +03:00
|
|
|
isInvite: PropTypes.bool,
|
|
|
|
|
|
|
|
startAsHidden: PropTypes.bool,
|
|
|
|
showSpinner: PropTypes.bool, // true to show a spinner if 0 elements when expanded
|
|
|
|
collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed?
|
|
|
|
onHeaderClick: PropTypes.func,
|
|
|
|
incomingCall: PropTypes.object,
|
|
|
|
extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
|
2019-10-14 18:08:56 +03:00
|
|
|
forceExpand: PropTypes.bool,
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static defaultProps = {
|
|
|
|
onHeaderClick: function() {
|
|
|
|
}, // NOP
|
|
|
|
extraTiles: [],
|
|
|
|
isInvite: false,
|
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
static getDerivedStateFromProps(props, state) {
|
2018-04-12 01:58:04 +03:00
|
|
|
return {
|
2019-12-08 15:43:06 +03:00
|
|
|
listLength: props.list.length,
|
|
|
|
scrollTop: props.list.length === state.listLength ? state.scrollTop : 0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
2018-04-12 01:58:04 +03:00
|
|
|
hidden: this.props.startAsHidden || false,
|
2019-02-13 16:51:09 +03:00
|
|
|
// some values to get LazyRenderList starting
|
|
|
|
scrollerHeight: 800,
|
|
|
|
scrollTop: 0,
|
2019-10-31 22:42:41 +03:00
|
|
|
// React 16's getDerivedStateFromProps(props, state) doesn't give the previous props so
|
|
|
|
// we have to store the length of the list here so we can see if it's changed or not...
|
|
|
|
listLength: null,
|
2018-04-12 01:58:04 +03:00
|
|
|
};
|
|
|
|
|
2019-12-08 15:16:17 +03:00
|
|
|
this._header = createRef();
|
|
|
|
this._subList = createRef();
|
|
|
|
this._scroller = createRef();
|
2019-10-17 17:53:39 +03:00
|
|
|
this._headerButton = createRef();
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
2019-12-08 15:16:17 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
componentDidMount() {
|
2018-04-12 01:58:04 +03:00
|
|
|
this.dispatcherRef = dis.register(this.onAction);
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
2018-04-12 01:58:04 +03:00
|
|
|
dis.unregister(this.dispatcherRef);
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-10-17 17:53:39 +03:00
|
|
|
// The header is collapsible if it is hidden or not stuck
|
2018-04-12 01:58:04 +03:00
|
|
|
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
2019-12-08 15:43:06 +03:00
|
|
|
isCollapsibleOnClick() {
|
2019-12-08 15:16:17 +03:00
|
|
|
const stuck = this._header.current.dataset.stuck;
|
2019-01-28 20:28:04 +03:00
|
|
|
if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
|
2018-04-12 01:58:04 +03:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
onAction = (payload) => {
|
2020-03-20 01:07:22 +03:00
|
|
|
switch (payload.action) {
|
|
|
|
case 'on_room_read':
|
|
|
|
// XXX: Previously RoomList would forceUpdate whenever on_room_read is dispatched,
|
|
|
|
// but this is no longer true, so we must do it here (and can apply the small
|
|
|
|
// optimisation of checking that we care about the room being read).
|
|
|
|
//
|
|
|
|
// Ultimately we need to transition to a state pushing flow where something
|
|
|
|
// explicitly notifies the components concerned that the notif count for a room
|
|
|
|
// has change (e.g. a Flux store).
|
|
|
|
if (this.props.list.some((r) => r.roomId === payload.roomId)) {
|
|
|
|
this.forceUpdate();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'view_room':
|
2020-04-01 12:08:26 +03:00
|
|
|
if (this.state.hidden && !this.props.forceExpand && payload.show_room_tile &&
|
2020-03-20 01:07:22 +03:00
|
|
|
this.props.list.some((r) => r.roomId === payload.room_id)
|
|
|
|
) {
|
2020-03-20 19:00:50 +03:00
|
|
|
this.toggle();
|
2020-03-20 01:07:22 +03:00
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2020-03-20 19:00:50 +03:00
|
|
|
toggle = () => {
|
2019-10-17 17:53:39 +03:00
|
|
|
if (this.isCollapsibleOnClick()) {
|
|
|
|
// The header isCollapsible, so the click is to be interpreted as collapse and truncation logic
|
2018-06-25 11:51:23 +03:00
|
|
|
const isHidden = !this.state.hidden;
|
2018-12-18 12:56:00 +03:00
|
|
|
this.setState({hidden: isHidden}, () => {
|
|
|
|
this.props.onHeaderClick(isHidden);
|
|
|
|
});
|
2018-04-12 01:58:04 +03:00
|
|
|
} else {
|
|
|
|
// The header is stuck, so the click is to be interpreted as a scroll to the header
|
2019-12-08 15:16:17 +03:00
|
|
|
this.props.onHeaderClick(this.state.hidden, this._header.current.dataset.originalPosition);
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2019-10-17 17:53:39 +03:00
|
|
|
|
2020-03-20 19:00:50 +03:00
|
|
|
onClick = (ev) => {
|
|
|
|
this.toggle();
|
|
|
|
};
|
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
onHeaderKeyDown = (ev) => {
|
2019-10-17 17:53:39 +03:00
|
|
|
switch (ev.key) {
|
2019-10-17 19:49:28 +03:00
|
|
|
case Key.ARROW_LEFT:
|
|
|
|
// On ARROW_LEFT collapse the room sublist
|
|
|
|
if (!this.state.hidden && !this.props.forceExpand) {
|
|
|
|
this.onClick();
|
|
|
|
}
|
|
|
|
ev.stopPropagation();
|
|
|
|
break;
|
2019-10-17 19:30:37 +03:00
|
|
|
case Key.ARROW_RIGHT: {
|
2019-10-17 17:53:39 +03:00
|
|
|
ev.stopPropagation();
|
|
|
|
if (this.state.hidden && !this.props.forceExpand) {
|
2019-10-17 19:30:37 +03:00
|
|
|
// sublist is collapsed, expand it
|
2019-10-17 17:53:39 +03:00
|
|
|
this.onClick();
|
2019-10-17 19:30:37 +03:00
|
|
|
} else if (!this.props.forceExpand) {
|
|
|
|
// sublist is expanded, go to first room
|
2019-12-08 15:16:17 +03:00
|
|
|
const element = this._subList.current && this._subList.current.querySelector(".mx_RoomTile");
|
2019-10-17 19:30:37 +03:00
|
|
|
if (element) {
|
|
|
|
element.focus();
|
|
|
|
}
|
2019-10-17 17:53:39 +03:00
|
|
|
}
|
|
|
|
break;
|
2019-10-17 19:30:37 +03:00
|
|
|
}
|
2019-10-17 17:53:39 +03:00
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
onKeyDown = (ev) => {
|
2019-10-22 13:10:25 +03:00
|
|
|
switch (ev.key) {
|
|
|
|
// On ARROW_LEFT go to the sublist header
|
|
|
|
case Key.ARROW_LEFT:
|
|
|
|
ev.stopPropagation();
|
|
|
|
this._headerButton.current.focus();
|
|
|
|
break;
|
|
|
|
// Consume ARROW_RIGHT so it doesn't cause focus to get sent to composer
|
|
|
|
case Key.ARROW_RIGHT:
|
|
|
|
ev.stopPropagation();
|
2019-10-17 19:14:00 +03:00
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2019-10-17 19:14:00 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
onRoomTileClick = (roomId, ev) => {
|
2018-04-12 01:58:04 +03:00
|
|
|
dis.dispatch({
|
|
|
|
action: 'view_room',
|
2020-04-01 12:08:26 +03:00
|
|
|
show_room_tile: true, // to make sure the room gets scrolled into view
|
2018-04-12 01:58:04 +03:00
|
|
|
room_id: roomId,
|
2019-12-16 20:14:03 +03:00
|
|
|
clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)),
|
2018-04-12 01:58:04 +03:00
|
|
|
});
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
_updateSubListCount = () => {
|
2018-04-12 01:58:04 +03:00
|
|
|
// Force an update by setting the state to the current state
|
|
|
|
// Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
|
|
|
|
// method is honoured
|
|
|
|
this.setState(this.state);
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
makeRoomTile = (room) => {
|
2020-05-05 17:39:37 +03:00
|
|
|
return <RoomTileErrorBoundary roomId={room.roomId}><RoomTile
|
2019-02-13 16:51:09 +03:00
|
|
|
room={room}
|
|
|
|
roomSubList={this}
|
|
|
|
tagName={this.props.tagName}
|
|
|
|
key={room.roomId}
|
|
|
|
collapsed={this.props.collapsed || false}
|
|
|
|
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
2019-04-02 01:06:33 +03:00
|
|
|
highlight={this.props.isInvite || RoomNotifs.getUnreadNotificationCount(room, 'highlight') > 0}
|
|
|
|
notificationCount={RoomNotifs.getUnreadNotificationCount(room)}
|
2019-02-13 16:51:09 +03:00
|
|
|
isInvite={this.props.isInvite}
|
|
|
|
refreshSubList={this._updateSubListCount}
|
|
|
|
incomingCall={null}
|
|
|
|
onClick={this.onRoomTileClick}
|
2020-05-05 17:39:37 +03:00
|
|
|
/></RoomTileErrorBoundary>;
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
_onNotifBadgeClick = (e) => {
|
2018-06-27 11:16:37 +03:00
|
|
|
// prevent the roomsublist collapsing
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2019-02-07 16:40:29 +03:00
|
|
|
const room = this.props.list.find(room => RoomNotifs.getRoomHasBadge(room));
|
2019-02-06 13:51:29 +03:00
|
|
|
if (room) {
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'view_room',
|
|
|
|
room_id: room.roomId,
|
|
|
|
});
|
2018-06-27 11:16:37 +03:00
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-06-27 11:16:37 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
_onInviteBadgeClick = (e) => {
|
2018-06-27 11:49:58 +03:00
|
|
|
// prevent the roomsublist collapsing
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
// switch to first room in sortedList as that'll be the top of the list for the user
|
2018-11-02 17:27:17 +03:00
|
|
|
if (this.props.list && this.props.list.length > 0) {
|
2018-06-27 11:49:58 +03:00
|
|
|
dis.dispatch({
|
|
|
|
action: 'view_room',
|
2018-11-02 17:27:17 +03:00
|
|
|
room_id: this.props.list[0].roomId,
|
2018-06-27 11:49:58 +03:00
|
|
|
});
|
|
|
|
} else if (this.props.extraTiles && this.props.extraTiles.length > 0) {
|
|
|
|
// Group Invites are different in that they are all extra tiles and not rooms
|
|
|
|
// XXX: this is a horrible special case because Group Invite sublist is a hack
|
|
|
|
if (this.props.extraTiles[0].props && this.props.extraTiles[0].props.group instanceof Group) {
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'view_group',
|
|
|
|
group_id: this.props.extraTiles[0].props.group.groupId,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-06-27 11:49:58 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
onAddRoom = (e) => {
|
2019-10-17 17:53:39 +03:00
|
|
|
e.stopPropagation();
|
|
|
|
if (this.props.onAddRoom) this.props.onAddRoom();
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2019-10-17 17:53:39 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
_getHeaderJsx(isCollapsed) {
|
2018-10-19 13:07:36 +03:00
|
|
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
2019-06-30 12:58:59 +03:00
|
|
|
const AccessibleTooltipButton = sdk.getComponent('elements.AccessibleTooltipButton');
|
2019-02-06 13:51:29 +03:00
|
|
|
const subListNotifications = !this.props.isInvite ?
|
|
|
|
RoomNotifs.aggregateNotificationCount(this.props.list) :
|
|
|
|
{count: 0, highlight: true};
|
|
|
|
const subListNotifCount = subListNotifications.count;
|
|
|
|
const subListNotifHighlight = subListNotifications.highlight;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
// When collapsed, allow a long hover on the header to show user
|
|
|
|
// the full tag name and room count
|
2018-06-25 11:51:23 +03:00
|
|
|
let title;
|
2018-04-12 01:58:04 +03:00
|
|
|
if (this.props.collapsed) {
|
|
|
|
title = this.props.label;
|
|
|
|
}
|
|
|
|
|
2018-06-25 11:51:23 +03:00
|
|
|
let incomingCall;
|
2018-04-12 01:58:04 +03:00
|
|
|
if (this.props.incomingCall) {
|
2018-12-06 21:45:58 +03:00
|
|
|
// We can assume that if we have an incoming call then it is for this list
|
|
|
|
const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
|
|
|
incomingCall =
|
|
|
|
<IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={this.props.incomingCall} />;
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
|
|
|
|
2018-11-02 17:27:17 +03:00
|
|
|
const len = this.props.list.length + this.props.extraTiles.length;
|
2018-10-19 15:33:51 +03:00
|
|
|
let chevron;
|
|
|
|
if (len) {
|
|
|
|
const chevronClasses = classNames({
|
|
|
|
'mx_RoomSubList_chevron': true,
|
2019-01-28 20:28:04 +03:00
|
|
|
'mx_RoomSubList_chevronRight': isCollapsed,
|
|
|
|
'mx_RoomSubList_chevronDown': !isCollapsed,
|
2018-10-19 15:33:51 +03:00
|
|
|
});
|
2019-06-30 12:58:59 +03:00
|
|
|
chevron = (<div className={chevronClasses} />);
|
2018-10-19 15:33:51 +03:00
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2020-01-15 14:37:14 +03:00
|
|
|
return <RovingTabIndexWrapper inputRef={this._headerButton}>
|
|
|
|
{({onFocus, isActive, ref}) => {
|
|
|
|
const tabIndex = isActive ? 0 : -1;
|
|
|
|
|
|
|
|
let badge;
|
|
|
|
if (!this.props.collapsed) {
|
|
|
|
const badgeClasses = classNames({
|
|
|
|
'mx_RoomSubList_badge': true,
|
|
|
|
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
|
|
|
});
|
|
|
|
// Wrap the contents in a div and apply styles to the child div so that the browser default outline works
|
|
|
|
if (subListNotifCount > 0) {
|
|
|
|
badge = (
|
|
|
|
<AccessibleButton
|
|
|
|
tabIndex={tabIndex}
|
|
|
|
className={badgeClasses}
|
|
|
|
onClick={this._onNotifBadgeClick}
|
|
|
|
aria-label={_t("Jump to first unread room.")}
|
|
|
|
>
|
|
|
|
<div>
|
|
|
|
{ FormattingUtils.formatCount(subListNotifCount) }
|
|
|
|
</div>
|
|
|
|
</AccessibleButton>
|
|
|
|
);
|
|
|
|
} else if (this.props.isInvite && this.props.list.length) {
|
|
|
|
// no notifications but highlight anyway because this is an invite badge
|
|
|
|
badge = (
|
|
|
|
<AccessibleButton
|
|
|
|
tabIndex={tabIndex}
|
|
|
|
className={badgeClasses}
|
|
|
|
onClick={this._onInviteBadgeClick}
|
|
|
|
aria-label={_t("Jump to first invite.")}
|
|
|
|
>
|
|
|
|
<div>
|
|
|
|
{ this.props.list.length }
|
|
|
|
</div>
|
|
|
|
</AccessibleButton>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let addRoomButton;
|
|
|
|
if (this.props.onAddRoom) {
|
|
|
|
addRoomButton = (
|
|
|
|
<AccessibleTooltipButton
|
|
|
|
tabIndex={tabIndex}
|
|
|
|
onClick={this.onAddRoom}
|
|
|
|
className="mx_RoomSubList_addRoom"
|
|
|
|
title={this.props.addRoomLabel || _t("Add room")}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="mx_RoomSubList_labelContainer" title={title} ref={this._header} onKeyDown={this.onHeaderKeyDown}>
|
|
|
|
<AccessibleButton
|
|
|
|
onFocus={onFocus}
|
|
|
|
tabIndex={tabIndex}
|
|
|
|
inputRef={ref}
|
|
|
|
onClick={this.onClick}
|
|
|
|
className="mx_RoomSubList_label"
|
|
|
|
aria-expanded={!isCollapsed}
|
|
|
|
role="treeitem"
|
|
|
|
aria-level="1"
|
|
|
|
>
|
|
|
|
{ chevron }
|
|
|
|
<span>{this.props.label}</span>
|
|
|
|
{ incomingCall }
|
|
|
|
</AccessibleButton>
|
|
|
|
{ badge }
|
|
|
|
{ addRoomButton }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
} }
|
|
|
|
</RovingTabIndexWrapper>;
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
checkOverflow = () => {
|
2019-12-08 15:16:17 +03:00
|
|
|
if (this._scroller.current) {
|
|
|
|
this._scroller.current.checkOverflow();
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
setHeight = (height) => {
|
2019-12-08 15:16:17 +03:00
|
|
|
if (this._subList.current) {
|
2020-04-06 22:52:36 +03:00
|
|
|
this._subList.current.style.height = toRem(height);
|
2019-01-24 17:43:49 +03:00
|
|
|
}
|
2019-02-13 16:51:09 +03:00
|
|
|
this._updateLazyRenderHeight(height);
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2019-02-13 16:51:09 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
_updateLazyRenderHeight(height) {
|
2019-02-13 16:51:09 +03:00
|
|
|
this.setState({scrollerHeight: height});
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
2019-02-13 16:51:09 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
_onScroll = () => {
|
2019-12-08 15:16:17 +03:00
|
|
|
this.setState({scrollTop: this._scroller.current.getScrollTop()});
|
2019-12-08 15:43:06 +03:00
|
|
|
};
|
2019-01-24 17:43:49 +03:00
|
|
|
|
2019-02-15 14:36:54 +03:00
|
|
|
_canUseLazyListRendering() {
|
|
|
|
// for now disable lazy rendering as they are already rendered tiles
|
|
|
|
// not rooms like props.list we pass to LazyRenderList
|
|
|
|
return !this.props.extraTiles || !this.props.extraTiles.length;
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
2019-02-15 14:36:54 +03:00
|
|
|
|
2019-12-08 15:43:06 +03:00
|
|
|
render() {
|
2018-11-02 17:27:17 +03:00
|
|
|
const len = this.props.list.length + this.props.extraTiles.length;
|
2019-01-28 20:28:04 +03:00
|
|
|
const isCollapsed = this.state.hidden && !this.props.forceExpand;
|
|
|
|
|
2019-10-14 18:08:56 +03:00
|
|
|
const subListClasses = classNames({
|
|
|
|
"mx_RoomSubList": true,
|
|
|
|
"mx_RoomSubList_hidden": len && isCollapsed,
|
|
|
|
"mx_RoomSubList_nonEmpty": len && !isCollapsed,
|
|
|
|
});
|
|
|
|
|
|
|
|
let content;
|
|
|
|
if (len) {
|
2019-01-28 20:28:04 +03:00
|
|
|
if (isCollapsed) {
|
2019-10-14 18:08:56 +03:00
|
|
|
// no body
|
2019-02-15 14:36:54 +03:00
|
|
|
} else if (this._canUseLazyListRendering()) {
|
2019-10-14 18:08:56 +03:00
|
|
|
content = (
|
2019-12-08 15:16:17 +03:00
|
|
|
<IndicatorScrollbar ref={this._scroller} className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
|
2019-02-13 16:51:09 +03:00
|
|
|
<LazyRenderList
|
|
|
|
scrollTop={this.state.scrollTop }
|
|
|
|
height={ this.state.scrollerHeight }
|
|
|
|
renderItem={ this.makeRoomTile }
|
|
|
|
itemHeight={34}
|
2019-02-15 14:36:54 +03:00
|
|
|
items={ this.props.list } />
|
|
|
|
</IndicatorScrollbar>
|
2019-10-14 18:08:56 +03:00
|
|
|
);
|
2019-02-15 14:36:54 +03:00
|
|
|
} else {
|
|
|
|
const roomTiles = this.props.list.map(r => this.makeRoomTile(r));
|
|
|
|
const tiles = roomTiles.concat(this.props.extraTiles);
|
2019-10-14 18:08:56 +03:00
|
|
|
content = (
|
2019-12-08 15:16:17 +03:00
|
|
|
<IndicatorScrollbar ref={this._scroller} className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
|
2019-02-15 14:36:54 +03:00
|
|
|
{ tiles }
|
2018-11-12 14:57:21 +03:00
|
|
|
</IndicatorScrollbar>
|
2019-10-14 18:08:56 +03:00
|
|
|
);
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
2018-06-25 11:51:23 +03:00
|
|
|
} else {
|
2019-01-28 20:28:04 +03:00
|
|
|
if (this.props.showSpinner && !isCollapsed) {
|
2019-10-14 18:08:56 +03:00
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
2018-06-26 09:46:33 +03:00
|
|
|
content = <Loader />;
|
2018-06-26 09:54:38 +03:00
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
}
|
2019-10-14 18:08:56 +03:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
2019-12-08 15:16:17 +03:00
|
|
|
ref={this._subList}
|
2019-10-14 18:08:56 +03:00
|
|
|
className={subListClasses}
|
|
|
|
role="group"
|
|
|
|
aria-label={this.props.label}
|
2019-10-17 19:14:00 +03:00
|
|
|
onKeyDown={this.onKeyDown}
|
2019-10-14 18:08:56 +03:00
|
|
|
>
|
|
|
|
{ this._getHeaderJsx(isCollapsed) }
|
|
|
|
{ content }
|
|
|
|
</div>
|
|
|
|
);
|
2019-12-08 15:43:06 +03:00
|
|
|
}
|
|
|
|
}
|