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.
This commit is contained in:
Travis Ralston 2020-06-04 21:21:04 -06:00
parent 4c1bc50649
commit 0c15b2bdb6
8 changed files with 205 additions and 116 deletions

View file

@ -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}
/>
);
}

View file

@ -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>
));
}

View file

@ -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>