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

 Conflicts:
	src/components/views/rooms/RoomSublist2.tsx
	src/components/views/rooms/RoomTile2.tsx
This commit is contained in:
Michael Telatynski 2020-07-03 00:02:37 +01:00
commit 9cf162e267
20 changed files with 118 additions and 92 deletions

View file

@ -140,6 +140,13 @@ export class ContextMenu extends React.Component {
e.stopPropagation();
};
// Prevent clicks on the background from going through to the component which opened the menu.
_onFinished = (ev: InputEvent) => {
ev.stopPropagation();
ev.preventDefault();
if (this.props.onFinished) this.props.onFinished();
};
_onMoveFocus = (element, up) => {
let descending = false; // are we currently descending or ascending through the DOM tree?
@ -326,7 +333,7 @@ export class ContextMenu extends React.Component {
let background;
if (hasBackground) {
background = (
<div className="mx_ContextualMenu_background" style={wrapperStyle} onClick={props.onFinished} onContextMenu={this.onContextMenu} />
<div className="mx_ContextualMenu_background" style={wrapperStyle} onClick={this._onFinished} onContextMenu={this.onContextMenu} />
);
}

View file

@ -37,6 +37,7 @@ import { OwnProfileStore } from "../../stores/OwnProfileStore";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
import BaseAvatar from '../views/avatars/BaseAvatar';
import classNames from "classnames";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
interface IProps {
isMinimized: boolean;
@ -230,7 +231,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
{MatrixClientPeg.get().getUserId()}
</span>
</div>
<div
<AccessibleTooltipButton
className="mx_UserMenu_contextMenu_themeButton"
onClick={this.onSwitchThemeClick}
title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")}
@ -240,7 +241,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
alt={_t("Switch theme")}
width={16}
/>
</div>
</AccessibleTooltipButton>
</div>
{hostingLink}
<div className="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst">

View file

@ -27,7 +27,7 @@ export type ButtonEvent = React.MouseEvent<Element> | React.KeyboardEvent<Elemen
* onClick: (required) Event handler for button activation. Should be
* implemented exactly like a normal onClick handler.
*/
interface IProps extends React.InputHTMLAttributes<Element> {
export interface IProps extends React.InputHTMLAttributes<Element> {
inputRef?: React.Ref<Element>;
element?: string;
// The kind of button, similar to how Bootstrap works.

View file

@ -16,21 +16,28 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import AccessibleButton from "./AccessibleButton";
import * as sdk from "../../../index";
import {IProps} from "./AccessibleButton";
import Tooltip from './Tooltip';
export default class AccessibleTooltipButton extends React.PureComponent {
static propTypes = {
...AccessibleButton.propTypes,
// The tooltip to render on hover
title: PropTypes.string.isRequired,
};
interface ITooltipProps extends IProps {
title: string;
tooltipClassName?: string;
}
state = {
hover: false,
};
interface IState {
hover: boolean;
}
export default class AccessibleTooltipButton extends React.PureComponent<ITooltipProps, IState> {
constructor(props: ITooltipProps) {
super(props);
this.state = {
hover: false,
};
}
onMouseOver = () => {
this.setState({
@ -45,14 +52,15 @@ export default class AccessibleTooltipButton extends React.PureComponent {
};
render() {
const Tooltip = sdk.getComponent("elements.Tooltip");
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const {title, children, ...props} = this.props;
const tooltipClassName = classnames(
"mx_AccessibleTooltipButton_tooltip",
this.props.tooltipClassName,
);
const tip = this.state.hover ? <Tooltip
className="mx_AccessibleTooltipButton_container"
tooltipClassName="mx_AccessibleTooltipButton_tooltip"
tooltipClassName={tooltipClassName}
label={title}
/> : <div />;
return (

View file

@ -26,6 +26,7 @@ import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { CSSTransition } from "react-transition-group";
import RoomListStore from "../../../stores/room-list/RoomListStore2";
import { DefaultTagID } from "../../../stores/room-list/models";
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
@ -98,11 +99,13 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
const roomTags = RoomListStore.instance.getTagsForRoom(r);
const roomTag = roomTags.includes(DefaultTagID.DM) ? DefaultTagID.DM : roomTags[0];
return (
<AccessibleButton
<AccessibleTooltipButton
className="mx_RoomBreadcrumbs2_crumb"
key={r.roomId}
onClick={() => this.viewRoom(r, i)}
aria-label={_t("Room %(name)s", {name: r.name})}
title={r.name}
tooltipClassName={"mx_RoomBreadcrumbs2_Tooltip"}
>
<DecoratedRoomAvatar
room={r}
@ -111,7 +114,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
displayBadge={true}
forceCount={true}
/>
</AccessibleButton>
</AccessibleTooltipButton>
);
});

View file

@ -166,19 +166,26 @@ export default class RoomList2 extends React.Component<IProps, IState> {
}
public componentDidMount(): void {
RoomListStore.instance.on(LISTS_UPDATE_EVENT, (store: RoomListStore2) => {
const newLists = store.orderedLists;
console.log("new lists", newLists);
const layoutMap = new Map<TagID, ListLayout>();
for (const tagId of Object.keys(newLists)) {
layoutMap.set(tagId, new ListLayout(tagId));
}
this.setState({sublists: newLists, layouts: layoutMap});
});
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.updateLists);
this.updateLists(); // trigger the first update
}
public componentWillUnmount() {
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
}
private updateLists = () => {
const newLists = RoomListStore.instance.orderedLists;
console.log("new lists", newLists);
const layoutMap = new Map<TagID, ListLayout>();
for (const tagId of Object.keys(newLists)) {
layoutMap.set(tagId, new ListLayout(tagId));
}
this.setState({sublists: newLists, layouts: layoutMap});
};
private renderCommunityInvites(): React.ReactElement[] {
// TODO: Put community invites in a more sensible place (not in the room list)
return MatrixClientPeg.get().getGroups().filter(g => {

View file

@ -35,6 +35,8 @@ 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";
import { Key } from "../../../Keyboard";
// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231
@ -397,11 +399,13 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
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"}
/>
);
}

View file

@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from "react";
import React from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import classNames from "classnames";
import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
@ -30,7 +30,6 @@ import { ContextMenu, ContextMenuButton, MenuItemRadio } from "../../structures/
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import RoomTileIcon from "./RoomTileIcon";
import { getRoomNotifsState, ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { setRoomNotifsState } from "../../../RoomNotifs";
@ -120,6 +119,10 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
ActiveRoomObserver.addListener(this.props.room.roomId, this.onActiveRoomUpdate);
}
private get showContextMenu(): boolean {
return !this.props.isMinimized && this.props.tag !== DefaultTagID.Invite;
}
public componentWillUnmount() {
if (this.props.room) {
ActiveRoomObserver.removeListener(this.props.room.roomId, this.onActiveRoomUpdate);
@ -169,6 +172,9 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
};
private onContextMenu = (ev: React.MouseEvent) => {
// If we don't have a context menu to show, ignore the action.
if (!this.showContextMenu) return;
ev.preventDefault();
ev.stopPropagation();
this.setState({
@ -236,7 +242,7 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
private onClickMute = ev => this.saveNotifState(ev, MUTE);
private renderNotificationsMenu(isActive: boolean): React.ReactElement {
if (this.props.isMinimized || MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Invite) {
if (MatrixClientPeg.get().isGuest() || !this.showContextMenu) {
// the menu makes no sense in these cases so do not show one
return null;
}
@ -281,12 +287,14 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
const classes = classNames("mx_RoomTile2_notificationsButton", {
// Show bell icon for the default case too.
mx_RoomTile2_iconBell: state === ALL_MESSAGES_LOUD || state === ALL_MESSAGES,
mx_RoomTile2_iconBellDot: state === MENTIONS_ONLY,
mx_RoomTile2_iconBell: state === state === ALL_MESSAGES,
mx_RoomTile2_iconBellDot: state === ALL_MESSAGES_LOUD,
mx_RoomTile2_iconBellMentions: state === MENTIONS_ONLY,
mx_RoomTile2_iconBellCrossed: state === MUTE,
// XXX: RoomNotifs assumes ALL_MESSAGES is default, this is wrong,
// but cannot be fixed until FTUE Notifications lands.
mx_RoomTile2_notificationsButton_show: state !== ALL_MESSAGES,
// Only show the icon by default if the room is overridden to muted.
// TODO: [FTUE Notifications] Probably need to detect global mute state
mx_RoomTile2_notificationsButton_show: state === MUTE,
});
return (
@ -304,12 +312,9 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
}
private renderGeneralMenu(): React.ReactElement {
if (this.props.isMinimized) return null; // no menu when minimized
if (!this.showContextMenu) return null; // no menu to show
// TODO: Get a proper invite context menu, or take invites out of the room list.
if (this.props.tag === DefaultTagID.Invite) {
return null;
}
// TODO: We could do with a proper invite context menu, unlike what showContextMenu suggests
let contextMenu = null;
if (this.state.generalMenuPosition) {