Merge pull request #5085 from matrix-org/t3chguy/notifications11

Factor out Iconized Context menu for reusability
This commit is contained in:
Michael Telatynski 2020-08-08 11:41:28 +01:00 committed by GitHub
commit 4799f0ee43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 433 additions and 332 deletions

View file

@ -0,0 +1,124 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
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.
*/
import React from "react";
import classNames from "classnames";
import {
ChevronFace,
ContextMenu,
IProps as IContextMenuProps,
MenuItem,
MenuItemCheckbox, MenuItemRadio,
} from "../../structures/ContextMenu";
interface IProps extends IContextMenuProps {
className?: string;
compact?: boolean;
}
interface IOptionListProps {
first?: boolean;
red?: boolean;
className?: string;
}
interface IOptionProps extends React.ComponentProps<typeof MenuItem> {
iconClassName: string;
}
interface ICheckboxProps extends React.ComponentProps<typeof MenuItemCheckbox> {
iconClassName: string;
}
interface IRadioProps extends React.ComponentProps<typeof MenuItemRadio> {
iconClassName: string;
}
export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({
label,
iconClassName,
active,
className,
...props
}) => {
return <MenuItemRadio
{...props}
className={classNames(className, {
mx_IconizedContextMenu_active: active,
})}
active={active}
label={label}
>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
</MenuItemRadio>;
};
export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
label,
iconClassName,
active,
className,
...props
}) => {
return <MenuItemCheckbox
{...props}
className={classNames(className, {
mx_IconizedContextMenu_active: active,
})}
active={active}
label={label}
>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
</MenuItemCheckbox>;
};
export const IconizedContextMenuOption: React.FC<IOptionProps> = ({label, iconClassName, ...props}) => {
return <MenuItem {...props} label={label}>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
</MenuItem>;
};
export const IconizedContextMenuOptionList: React.FC<IOptionListProps> = ({first, red, className, children}) => {
const classes = classNames("mx_IconizedContextMenu_optionList", className, {
mx_IconizedContextMenu_optionList_notFirst: !first,
mx_IconizedContextMenu_optionList_red: red,
});
return <div className={classes}>
{children}
</div>;
};
const IconizedContextMenu: React.FC<IProps> = ({className, children, compact, ...props}) => {
const classes = classNames("mx_IconizedContextMenu", className, {
mx_IconizedContextMenu_compact: compact,
});
return <ContextMenu chevronFace={ChevronFace.None} {...props}>
<div className={classes}>
{ children }
</div>
</ContextMenu>;
};
export default IconizedContextMenu;

View file

@ -27,14 +27,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher';
import { Key } from "../../../Keyboard";
import ActiveRoomObserver from "../../../ActiveRoomObserver";
import { _t } from "../../../languageHandler";
import {
ChevronFace,
ContextMenu,
ContextMenuTooltipButton,
MenuItem,
MenuItemCheckbox,
MenuItemRadio,
} from "../../structures/ContextMenu";
import { ChevronFace, ContextMenuTooltipButton, MenuItemRadio } from "../../structures/ContextMenu";
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
@ -51,6 +44,11 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { EchoChamber } from "../../../stores/local-echo/EchoChamber";
import { CachedRoomKey, RoomEchoChamber } from "../../../stores/local-echo/RoomEchoChamber";
import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber";
import IconizedContextMenu, {
IconizedContextMenuCheckbox,
IconizedContextMenuOption,
IconizedContextMenuOptionList, IconizedContextMenuRadio
} from "../context_menus/IconizedContextMenu";
interface IProps {
room: Room;
@ -78,32 +76,6 @@ const contextMenuBelow = (elementRect: PartialDOMRect) => {
return {left, top, chevronFace};
};
interface INotifOptionProps {
active: boolean;
iconClassName: string;
label: string;
onClick(ev: ButtonEvent);
}
const NotifOption: React.FC<INotifOptionProps> = ({active, onClick, iconClassName, label}) => {
const classes = classNames({
mx_RoomTile_contextMenu_activeRow: active,
});
let activeIcon;
if (active) {
activeIcon = <span className="mx_IconizedContextMenu_icon mx_RoomTile_iconCheck" />;
}
return (
<MenuItemRadio className={classes} onClick={onClick} active={active} label={label}>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{ label }</span>
{ activeIcon }
</MenuItemRadio>
);
};
export default class RoomTile extends React.PureComponent<IProps, IState> {
private dispatcherRef: string;
private roomTileRef = createRef<HTMLDivElement>();
@ -335,38 +307,39 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
let contextMenu = null;
if (this.state.notificationsMenuPosition) {
contextMenu = (
<ContextMenu {...contextMenuBelow(this.state.notificationsMenuPosition)} onFinished={this.onCloseNotificationsMenu}>
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
<div className="mx_IconizedContextMenu_optionList">
<NotifOption
label={_t("Use default")}
active={state === ALL_MESSAGES}
iconClassName="mx_RoomTile_iconBell"
onClick={this.onClickAllNotifs}
/>
<NotifOption
label={_t("All messages")}
active={state === ALL_MESSAGES_LOUD}
iconClassName="mx_RoomTile_iconBellDot"
onClick={this.onClickAlertMe}
/>
<NotifOption
label={_t("Mentions & Keywords")}
active={state === MENTIONS_ONLY}
iconClassName="mx_RoomTile_iconBellMentions"
onClick={this.onClickMentions}
/>
<NotifOption
label={_t("None")}
active={state === MUTE}
iconClassName="mx_RoomTile_iconBellCrossed"
onClick={this.onClickMute}
/>
</div>
</div>
</ContextMenu>
);
contextMenu = <IconizedContextMenu
{...contextMenuBelow(this.state.notificationsMenuPosition)}
onFinished={this.onCloseNotificationsMenu}
className="mx_RoomTile_contextMenu"
compact
>
<IconizedContextMenuOptionList first>
<IconizedContextMenuRadio
label={_t("Use default")}
active={state === ALL_MESSAGES}
iconClassName="mx_RoomTile_iconBell"
onClick={this.onClickAllNotifs}
/>
<IconizedContextMenuRadio
label={_t("All messages")}
active={state === ALL_MESSAGES_LOUD}
iconClassName="mx_RoomTile_iconBellDot"
onClick={this.onClickAlertMe}
/>
<IconizedContextMenuRadio
label={_t("Mentions & Keywords")}
active={state === MENTIONS_ONLY}
iconClassName="mx_RoomTile_iconBellMentions"
onClick={this.onClickMentions}
/>
<IconizedContextMenuRadio
label={_t("None")}
active={state === MUTE}
iconClassName="mx_RoomTile_iconBellCrossed"
onClick={this.onClickMute}
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
}
const classes = classNames("mx_RoomTile_notificationsButton", {
@ -400,18 +373,20 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
let contextMenu = null;
if (this.state.generalMenuPosition && this.props.tag === DefaultTagID.Archived) {
contextMenu = (
<ContextMenu {...contextMenuBelow(this.state.generalMenuPosition)} onFinished={this.onCloseGeneralMenu}>
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
<div className="mx_IconizedContextMenu_optionList mx_RoomTile_contextMenu_redRow">
<MenuItem onClick={this.onForgetRoomClick} label={_t("Leave Room")}>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSignOut" />
<span className="mx_IconizedContextMenu_label">{_t("Forget Room")}</span>
</MenuItem>
</div>
</div>
</ContextMenu>
);
contextMenu = <IconizedContextMenu
{...contextMenuBelow(this.state.generalMenuPosition)}
onFinished={this.onCloseGeneralMenu}
className="mx_RoomTile_contextMenu"
compact
>
<IconizedContextMenuOptionList red>
<IconizedContextMenuOption
iconClassName="mx_RoomTile_iconSignOut"
label={_t("Forget Room")}
onClick={this.onForgetRoomClick}
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
} else if (this.state.generalMenuPosition) {
const roomTags = RoomListStore.instance.getTagsForRoom(this.props.room);
@ -421,42 +396,40 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
const lowPriorityLabel = _t("Low Priority");
contextMenu = (
<ContextMenu {...contextMenuBelow(this.state.generalMenuPosition)} onFinished={this.onCloseGeneralMenu}>
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
<div className="mx_IconizedContextMenu_optionList">
<MenuItemCheckbox
className={isFavorite ? "mx_RoomTile_contextMenu_activeRow" : ""}
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}
active={isFavorite}
label={favouriteLabel}
>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconStar" />
<span className="mx_IconizedContextMenu_label">{favouriteLabel}</span>
</MenuItemCheckbox>
<MenuItemCheckbox
className={isLowPriority ? "mx_RoomTile_contextMenu_activeRow" : ""}
onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}
active={isLowPriority}
label={lowPriorityLabel}
>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconArrowDown" />
<span className="mx_IconizedContextMenu_label">{lowPriorityLabel}</span>
</MenuItemCheckbox>
<MenuItem onClick={this.onOpenRoomSettings} label={_t("Settings")}>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSettings" />
<span className="mx_IconizedContextMenu_label">{_t("Settings")}</span>
</MenuItem>
</div>
<div className="mx_IconizedContextMenu_optionList mx_RoomTile_contextMenu_redRow">
<MenuItem onClick={this.onLeaveRoomClick} label={_t("Leave Room")}>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSignOut" />
<span className="mx_IconizedContextMenu_label">{_t("Leave Room")}</span>
</MenuItem>
</div>
</div>
</ContextMenu>
);
contextMenu = <IconizedContextMenu
{...contextMenuBelow(this.state.generalMenuPosition)}
onFinished={this.onCloseGeneralMenu}
className="mx_RoomTile_contextMenu"
compact
>
<IconizedContextMenuOptionList>
<IconizedContextMenuCheckbox
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}
active={isFavorite}
label={favouriteLabel}
iconClassName="mx_RoomTile_iconStar"
/>
<IconizedContextMenuCheckbox
onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}
active={isLowPriority}
label={lowPriorityLabel}
iconClassName="mx_RoomTile_iconArrowDown"
/>
<IconizedContextMenuOption
onClick={this.onOpenRoomSettings}
label={_t("Settings")}
iconClassName="mx_RoomTile_iconSettings"
/>
</IconizedContextMenuOptionList>
<IconizedContextMenuOptionList red>
<IconizedContextMenuOption
onClick={this.onLeaveRoomClick}
label={_t("Leave Room")}
iconClassName="mx_RoomTile_iconSignOut"
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
}
return (