Merge branches 'develop' and 't3chguy/room-list/3' of github.com:matrix-org/matrix-react-sdk into t3chguy/room-list/3

 Conflicts:
	src/components/structures/ContextMenu.tsx
	src/components/views/rooms/RoomSublist2.tsx
This commit is contained in:
Michael Telatynski 2020-07-02 23:46:12 +01:00
commit 404009c8cb
36 changed files with 998 additions and 457 deletions

View file

@ -17,21 +17,26 @@ limitations under the License.
*/
import * as React from "react";
import {Room} from "matrix-js-sdk/src/models/room";
import { createRef } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import classNames from 'classnames';
import {RovingTabIndexWrapper} from "../../../accessibility/RovingTabIndex";
import {_t} from "../../../languageHandler";
import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../../views/elements/AccessibleButton";
import RoomTile2 from "./RoomTile2";
import {ResizableBox, ResizeCallbackData} from "react-resizable";
import {ListLayout} from "../../../stores/room-list/ListLayout";
import NotificationBadge, {ListNotificationState} from "./NotificationBadge";
import {ChevronFace, ContextMenu, ContextMenuButton} from "../../structures/ContextMenu";
import { ResizableBox, ResizeCallbackData } from "react-resizable";
import { ListLayout } from "../../../stores/room-list/ListLayout";
import { ChevronFace, ContextMenu, ContextMenuButton } from "../../structures/ContextMenu";
import StyledCheckbox from "../elements/StyledCheckbox";
import StyledRadioButton from "../elements/StyledRadioButton";
import RoomListStore from "../../../stores/room-list/RoomListStore2";
import {ListAlgorithm, SortAlgorithm} from "../../../stores/room-list/algorithms/models";
import {DefaultTagID, TagID} from "../../../stores/room-list/models";
import { ListAlgorithm, SortAlgorithm } from "../../../stores/room-list/algorithms/models";
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import dis from "../../../dispatcher/dispatcher";
import NotificationBadge from "./NotificationBadge";
import { ListNotificationState } from "../../../stores/notifications/ListNotificationState";
import Tooltip from "../elements/Tooltip";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231
@ -61,6 +66,10 @@ interface IProps {
isMinimized: boolean;
tagId: TagID;
// TODO: Don't use this. It's for community invites, and community invites shouldn't be here.
// You should feel bad if you use this.
extraBadTilesThatShouldntExist?: React.ReactElement[];
// TODO: Account for https://github.com/vector-im/riot-web/issues/14179
}
@ -85,8 +94,13 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
}
private get numTiles(): number {
// TODO: Account for group invites: https://github.com/vector-im/riot-web/issues/14179
return (this.props.rooms || []).length;
return (this.props.rooms || []).length + (this.props.extraBadTilesThatShouldntExist || []).length;
}
private get numVisibleTiles(): number {
if (!this.props.layout) return 0;
const nVisible = Math.floor(this.props.layout.visibleTiles);
return Math.min(nVisible, this.numTiles);
}
public componentDidUpdate() {
@ -105,7 +119,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
private onResize = (e: React.MouseEvent, data: ResizeCallbackData) => {
const direction = e.movementY < 0 ? -1 : +1;
const tileDiff = this.props.layout.pixelsToTiles(Math.abs(e.movementY)) * direction;
this.props.layout.visibleTiles += tileDiff;
this.props.layout.setVisibleTilesWithin(tileDiff, this.numTiles);
this.forceUpdate(); // because the layout doesn't trigger a re-render
};
@ -165,6 +179,30 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
this.forceUpdate(); // because the layout doesn't trigger a re-render
};
private onBadgeClick = (ev: React.MouseEvent) => {
ev.preventDefault();
ev.stopPropagation();
let room;
if (this.props.tagId === DefaultTagID.Invite) {
// switch to first room as that'll be the top of the list for the user
room = this.props.rooms && this.props.rooms[0];
} else {
// find the first room with a count of the same colour as the badge count
room = this.props.rooms.find((r: Room) => {
const notifState = this.state.notificationState.getForRoom(r);
return notifState.count > 0 && notifState.color === this.state.notificationState.color;
});
}
if (room) {
dis.dispatch({
action: 'view_room',
room_id: room.roomId,
});
}
};
private onHeaderClick = (ev: React.MouseEvent<HTMLDivElement>) => {
let target = ev.target as HTMLDivElement;
if (!target.classList.contains('mx_RoomSublist2_headerText')) {
@ -184,13 +222,21 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
}
};
private renderTiles(): React.ReactElement[] {
if (this.props.layout && this.props.layout.isCollapsed) return []; // don't waste time on rendering
private renderVisibleTiles(): React.ReactElement[] {
if (this.props.layout && this.props.layout.isCollapsed) {
// don't waste time on rendering
return [];
}
const tiles: React.ReactElement[] = [];
if (this.props.extraBadTilesThatShouldntExist) {
tiles.push(...this.props.extraBadTilesThatShouldntExist);
}
if (this.props.rooms) {
for (const room of this.props.rooms) {
const visibleRooms = this.props.rooms.slice(0, this.numVisibleTiles);
for (const room of visibleRooms) {
tiles.push(
<RoomTile2
room={room}
@ -203,6 +249,14 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
}
}
// We only have to do this because of the extra tiles. We do it conditionally
// to avoid spending cycles on slicing. It's generally fine to do this though
// as users are unlikely to have more than a handful of tiles when the extra
// tiles are used.
if (tiles.length > this.numVisibleTiles) {
return tiles.slice(0, this.numVisibleTiles);
}
return tiles;
}
@ -286,16 +340,25 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
// TODO: Use onFocus: https://github.com/vector-im/riot-web/issues/14180
const tabIndex = isActive ? 0 : -1;
const badge = <NotificationBadge forceCount={true} notification={this.state.notificationState}/>;
const badge = (
<NotificationBadge
forceCount={true}
notification={this.state.notificationState}
onClick={this.onBadgeClick}
tabIndex={tabIndex}
/>
);
let addRoomButton = null;
if (!!this.props.onAddRoom) {
addRoomButton = (
<AccessibleButton
<AccessibleTooltipButton
tabIndex={tabIndex}
onClick={this.onAddRoom}
className="mx_RoomSublist2_auxButton"
aria-label={this.props.addRoomLabel || _t("Add room")}
title={this.props.addRoomLabel}
tooltipClassName={"mx_RoomSublist2_addRoomTooltip"}
/>
);
}
@ -354,7 +417,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
public render(): React.ReactElement {
// TODO: Error boundary: https://github.com/vector-im/riot-web/issues/14185
const tiles = this.renderTiles();
const visibleTiles = this.renderVisibleTiles();
const classes = classNames({
'mx_RoomSublist2': true,
@ -363,13 +426,10 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
});
let content = null;
if (tiles.length > 0) {
if (visibleTiles.length > 0) {
const layout = this.props.layout; // to shorten calls
const nVisible = Math.floor(layout.visibleTiles);
const visibleTiles = tiles.slice(0, nVisible);
const maxTilesFactored = layout.tilesWithResizerBoxFactor(tiles.length);
const maxTilesFactored = layout.tilesWithResizerBoxFactor(this.numTiles);
const showMoreBtnClasses = classNames({
'mx_RoomSublist2_showNButton': true,
'mx_RoomSublist2_isCutting': this.state.isResizing && layout.visibleTiles < maxTilesFactored,
@ -379,9 +439,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
// floats above the resize handle, if we have one present. If the user has all
// tiles visible, it becomes 'show less'.
let showNButton = null;
if (tiles.length > nVisible) {
if (this.numTiles > visibleTiles.length) {
// we have a cutoff condition - add the button to show all
const numMissing = tiles.length - visibleTiles.length;
const numMissing = this.numTiles - visibleTiles.length;
let showMoreText = (
<span className='mx_RoomSublist2_showNButtonText'>
{_t("Show %(count)s more", {count: numMissing})}
@ -396,7 +456,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
{showMoreText}
</div>
);
} else if (tiles.length <= nVisible && tiles.length > this.props.layout.defaultVisibleTiles) {
} else if (this.numTiles <= visibleTiles.length && this.numTiles > this.props.layout.defaultVisibleTiles) {
// we have all tiles visible - add a button to show less
let showLessText = (
<span className='mx_RoomSublist2_showNButtonText'>
@ -416,7 +476,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
// Figure out if we need a handle
let handles = ['s'];
if (layout.visibleTiles >= tiles.length && tiles.length <= layout.minVisibleTiles) {
if (layout.visibleTiles >= this.numTiles && this.numTiles <= layout.minVisibleTiles) {
handles = []; // no handles, we're at a minimum
}
@ -435,9 +495,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
if (showNButton) padding += SHOW_N_BUTTON_HEIGHT;
padding += RESIZE_HANDLE_HEIGHT; // always append the handle height
const relativeTiles = layout.tilesWithPadding(tiles.length, padding);
const relativeTiles = layout.tilesWithPadding(this.numTiles, padding);
const minTilesPx = layout.calculateTilesToPixelsMin(relativeTiles, layout.minVisibleTiles, padding);
const maxTilesPx = layout.tilesToPixelsWithPadding(tiles.length, padding);
const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, padding);
const tilesWithoutPadding = Math.min(relativeTiles, layout.visibleTiles);
const tilesPx = layout.calculateTilesToPixelsMin(relativeTiles, tilesWithoutPadding, padding);