Merge pull request #4875 from matrix-org/t3chguy/room-list/4

Add click-to-jump on badge in the room sublist header
pull/21833/head
Michael Telatynski 2020-07-02 20:04:43 +01:00 committed by GitHub
commit 4a03b464d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 9 deletions

View File

@ -29,6 +29,8 @@ import { IDestroyable } from "../../../utils/IDestroyable";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { readReceiptChangeIsFor } from "../../../utils/read-receipts"; import { readReceiptChangeIsFor } from "../../../utils/read-receipts";
import AccessibleButton from "../elements/AccessibleButton";
import { XOR } from "../../../@types/common";
export const NOTIFICATION_STATE_UPDATE = "update"; export const NOTIFICATION_STATE_UPDATE = "update";
@ -62,11 +64,18 @@ interface IProps {
roomId?: string; roomId?: string;
} }
interface IClickableProps extends IProps, React.InputHTMLAttributes<Element> {
/**
* If specified will return an AccessibleButton instead of a div.
*/
onClick?(ev: React.MouseEvent);
}
interface IState { interface IState {
showCounts: boolean; // whether or not to show counts. Independent of props.forceCount showCounts: boolean; // whether or not to show counts. Independent of props.forceCount
} }
export default class NotificationBadge extends React.PureComponent<IProps, IState> { export default class NotificationBadge extends React.PureComponent<XOR<IProps, IClickableProps>, IState> {
private countWatcherRef: string; private countWatcherRef: string;
constructor(props: IProps) { constructor(props: IProps) {
@ -109,20 +118,22 @@ export default class NotificationBadge extends React.PureComponent<IProps, IStat
}; };
public render(): React.ReactElement { public render(): React.ReactElement {
// Don't show a badge if we don't need to const {notification, forceCount, roomId, onClick, ...props} = this.props;
if (this.props.notification.color <= NotificationColor.None) return null;
const hasNotif = this.props.notification.color >= NotificationColor.Red; // Don't show a badge if we don't need to
const hasCount = this.props.notification.color >= NotificationColor.Grey; if (notification.color <= NotificationColor.None) return null;
const hasUnread = this.props.notification.color >= NotificationColor.Bold;
const hasNotif = notification.color >= NotificationColor.Red;
const hasCount = notification.color >= NotificationColor.Grey;
const hasUnread = notification.color >= NotificationColor.Bold;
const couldBeEmpty = (!this.state.showCounts || hasUnread) && !hasNotif; const couldBeEmpty = (!this.state.showCounts || hasUnread) && !hasNotif;
let isEmptyBadge = couldBeEmpty && (!this.state.showCounts || !hasCount); let isEmptyBadge = couldBeEmpty && (!this.state.showCounts || !hasCount);
if (this.props.forceCount) { if (forceCount) {
isEmptyBadge = false; isEmptyBadge = false;
if (!hasCount) return null; // Can't render a badge if (!hasCount) return null; // Can't render a badge
} }
let symbol = this.props.notification.symbol || formatMinimalBadgeCount(this.props.notification.count); let symbol = notification.symbol || formatMinimalBadgeCount(notification.count);
if (isEmptyBadge) symbol = ""; if (isEmptyBadge) symbol = "";
const classes = classNames({ const classes = classNames({
@ -134,6 +145,14 @@ export default class NotificationBadge extends React.PureComponent<IProps, IStat
'mx_NotificationBadge_3char': symbol.length > 2, 'mx_NotificationBadge_3char': symbol.length > 2,
}); });
if (onClick) {
return (
<AccessibleButton {...props} className={classes} onClick={onClick}>
<span className="mx_NotificationBadge_count">{symbol}</span>
</AccessibleButton>
);
}
return ( return (
<div className={classes}> <div className={classes}>
<span className="mx_NotificationBadge_count">{symbol}</span> <span className="mx_NotificationBadge_count">{symbol}</span>

View File

@ -33,6 +33,7 @@ import StyledRadioButton from "../elements/StyledRadioButton";
import RoomListStore from "../../../stores/room-list/RoomListStore2"; import RoomListStore from "../../../stores/room-list/RoomListStore2";
import { ListAlgorithm, SortAlgorithm } from "../../../stores/room-list/algorithms/models"; import { ListAlgorithm, SortAlgorithm } from "../../../stores/room-list/algorithms/models";
import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import dis from "../../../dispatcher/dispatcher";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 // 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 // TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231
@ -175,6 +176,30 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
this.forceUpdate(); // because the layout doesn't trigger a re-render 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>) => { private onHeaderClick = (ev: React.MouseEvent<HTMLDivElement>) => {
let target = ev.target as HTMLDivElement; let target = ev.target as HTMLDivElement;
if (!target.classList.contains('mx_RoomSublist2_headerText')) { if (!target.classList.contains('mx_RoomSublist2_headerText')) {
@ -312,7 +337,14 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
// TODO: Use onFocus: https://github.com/vector-im/riot-web/issues/14180 // TODO: Use onFocus: https://github.com/vector-im/riot-web/issues/14180
const tabIndex = isActive ? 0 : -1; 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; let addRoomButton = null;
if (!!this.props.onAddRoom) { if (!!this.props.onAddRoom) {