Unified room context menus (#7072)
This commit is contained in:
parent
720b092844
commit
27c3153947
23 changed files with 660 additions and 133 deletions
|
@ -40,6 +40,7 @@ export const ContextMenuTooltipButton: React.FC<IProps> = ({
|
|||
onContextMenu={onContextMenu || onClick}
|
||||
aria-haspopup={true}
|
||||
aria-expanded={isExpanded}
|
||||
forceHide={isExpanded}
|
||||
>
|
||||
{ children }
|
||||
</AccessibleTooltipButton>
|
||||
|
|
|
@ -1486,10 +1486,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
});
|
||||
};
|
||||
|
||||
private onSettingsClick = () => {
|
||||
dis.dispatch({ action: "open_room_settings" });
|
||||
};
|
||||
|
||||
private onAppsClick = () => {
|
||||
dis.dispatch({
|
||||
action: "appsDrawer",
|
||||
|
@ -2111,7 +2107,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
oobData={this.props.oobData}
|
||||
inRoom={myMembership === 'join'}
|
||||
onSearchClick={this.onSearchClick}
|
||||
onSettingsClick={this.onSettingsClick}
|
||||
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
onAppsClick={this.state.hasPinnedWidgets ? this.onAppsClick : null}
|
||||
|
|
308
src/components/views/context_menus/RoomContextMenu.tsx
Normal file
308
src/components/views/context_menus/RoomContextMenu.tsx
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
Copyright 2021 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, { useContext } from "react";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { IProps as IContextMenuProps } from "../../structures/ContextMenu";
|
||||
import IconizedContextMenu, {
|
||||
IconizedContextMenuCheckbox,
|
||||
IconizedContextMenuOption,
|
||||
IconizedContextMenuOptionList,
|
||||
} from "./IconizedContextMenu";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||
import RoomListStore from "../../../stores/room-list/RoomListStore";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import RoomListActions from "../../../actions/RoomListActions";
|
||||
import { Key } from "../../../Keyboard";
|
||||
import { EchoChamber } from "../../../stores/local-echo/EchoChamber";
|
||||
import { RoomNotifState } from "../../../RoomNotifs";
|
||||
import Modal from "../../../Modal";
|
||||
import ExportDialog from "../dialogs/ExportDialog";
|
||||
import { onRoomFilesClick, onRoomMembersClick } from "../right_panel/RoomSummaryCard";
|
||||
import RoomViewStore from "../../../stores/RoomViewStore";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { SetRightPanelPhasePayload } from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
|
||||
import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog";
|
||||
|
||||
interface IProps extends IContextMenuProps {
|
||||
room: Room;
|
||||
}
|
||||
|
||||
const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const roomTags = RoomListStore.instance.getTagsForRoom(room);
|
||||
|
||||
let leaveOption: JSX.Element;
|
||||
if (roomTags.includes(DefaultTagID.Archived)) {
|
||||
const onForgetRoomClick = (ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "forget_room",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
};
|
||||
|
||||
leaveOption = <IconizedContextMenuOption
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
label={_t("Forget")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
onClick={onForgetRoomClick}
|
||||
/>;
|
||||
} else {
|
||||
const onLeaveRoomClick = (ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "leave_room",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
};
|
||||
|
||||
leaveOption = <IconizedContextMenuOption
|
||||
onClick={onLeaveRoomClick}
|
||||
label={_t("Leave")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
/>;
|
||||
}
|
||||
|
||||
let inviteOption: JSX.Element;
|
||||
if (room.canInvite(cli.getUserId())) {
|
||||
const onInviteClick = (ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "view_invite",
|
||||
roomId: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
};
|
||||
|
||||
inviteOption = <IconizedContextMenuOption
|
||||
onClick={onInviteClick}
|
||||
label={_t("Invite")}
|
||||
iconClassName="mx_RoomTile_iconInvite"
|
||||
/>;
|
||||
}
|
||||
|
||||
let favouriteOption: JSX.Element;
|
||||
let lowPriorityOption: JSX.Element;
|
||||
let notificationOption: JSX.Element;
|
||||
if (room.getMyMembership() === "join") {
|
||||
const isFavorite = roomTags.includes(DefaultTagID.Favourite);
|
||||
favouriteOption = <IconizedContextMenuCheckbox
|
||||
onClick={(e) => onTagRoom(e, DefaultTagID.Favourite)}
|
||||
active={isFavorite}
|
||||
label={isFavorite ? _t("Favourited") : _t("Favourite")}
|
||||
iconClassName="mx_RoomTile_iconStar"
|
||||
/>;
|
||||
|
||||
const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
|
||||
lowPriorityOption = <IconizedContextMenuCheckbox
|
||||
onClick={(e) => onTagRoom(e, DefaultTagID.LowPriority)}
|
||||
active={isLowPriority}
|
||||
label={_t("Low priority")}
|
||||
iconClassName="mx_RoomTile_iconArrowDown"
|
||||
/>;
|
||||
|
||||
const echoChamber = EchoChamber.forRoom(room);
|
||||
let notificationLabel: string;
|
||||
let iconClassName: string;
|
||||
switch (echoChamber.notificationVolume) {
|
||||
case RoomNotifState.AllMessages:
|
||||
notificationLabel = _t("Default");
|
||||
iconClassName = "mx_RoomTile_iconNotificationsDefault";
|
||||
break;
|
||||
case RoomNotifState.AllMessagesLoud:
|
||||
notificationLabel = _t("All messages");
|
||||
iconClassName = "mx_RoomTile_iconNotificationsAllMessages";
|
||||
break;
|
||||
case RoomNotifState.MentionsOnly:
|
||||
notificationLabel = _t("Mentions only");
|
||||
iconClassName = "mx_RoomTile_iconNotificationsMentionsKeywords";
|
||||
break;
|
||||
case RoomNotifState.Mute:
|
||||
notificationLabel = _t("Mute");
|
||||
iconClassName = "mx_RoomTile_iconNotificationsNone";
|
||||
break;
|
||||
}
|
||||
|
||||
notificationOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
initial_tab_id: ROOM_NOTIFICATIONS_TAB,
|
||||
});
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Notifications")}
|
||||
iconClassName={iconClassName}
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_sublabel">
|
||||
{ notificationLabel }
|
||||
</span>
|
||||
</IconizedContextMenuOption>;
|
||||
}
|
||||
|
||||
const onTagRoom = (ev: ButtonEvent, tagId: TagID) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (tagId === DefaultTagID.Favourite || tagId === DefaultTagID.LowPriority) {
|
||||
const inverseTag = tagId === DefaultTagID.Favourite ? DefaultTagID.LowPriority : DefaultTagID.Favourite;
|
||||
const isApplied = RoomListStore.instance.getTagsForRoom(room).includes(tagId);
|
||||
const removeTag = isApplied ? tagId : inverseTag;
|
||||
const addTag = isApplied ? null : tagId;
|
||||
dis.dispatch(RoomListActions.tagRoom(cli, room, removeTag, addTag, undefined, 0));
|
||||
} else {
|
||||
logger.warn(`Unexpected tag ${tagId} applied to ${room.roomId}`);
|
||||
}
|
||||
|
||||
if ((ev as React.KeyboardEvent).key === Key.ENTER) {
|
||||
// Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12
|
||||
onFinished();
|
||||
}
|
||||
};
|
||||
|
||||
const ensureViewingRoom = () => {
|
||||
if (RoomViewStore.getRoomId() === room.roomId) return;
|
||||
dis.dispatch({
|
||||
action: "view_room",
|
||||
room_id: room.roomId,
|
||||
}, true);
|
||||
};
|
||||
|
||||
return <IconizedContextMenu {...props} onFinished={onFinished} className="mx_RoomTile_contextMenu" compact>
|
||||
<IconizedContextMenuOptionList>
|
||||
{ inviteOption }
|
||||
{ notificationOption }
|
||||
{ favouriteOption }
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom();
|
||||
onRoomMembersClick(false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("People")}
|
||||
iconClassName="mx_RoomTile_iconPeople"
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_sublabel">
|
||||
{ room.getJoinedMemberCount() }
|
||||
</span>
|
||||
</IconizedContextMenuOption>
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom();
|
||||
onRoomFilesClick(false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Files")}
|
||||
iconClassName="mx_RoomTile_iconFiles"
|
||||
/>
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom();
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.RoomSummary,
|
||||
allowClose: false,
|
||||
});
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Widgets")}
|
||||
iconClassName="mx_RoomTile_iconWidgets"
|
||||
/>
|
||||
|
||||
{ lowPriorityOption }
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "copy_room",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Copy link")}
|
||||
iconClassName="mx_RoomTile_iconCopyLink"
|
||||
/>
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Settings")}
|
||||
iconClassName="mx_RoomTile_iconSettings"
|
||||
/>
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
Modal.createTrackedDialog('Export room dialog', '', ExportDialog, { room });
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Export chat")}
|
||||
iconClassName="mx_RoomTile_iconExport"
|
||||
/>
|
||||
|
||||
{ leaveOption }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
};
|
||||
|
||||
export default RoomContextMenu;
|
||||
|
|
@ -113,7 +113,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
|
|||
ROOM_NOTIFICATIONS_TAB,
|
||||
_td("Notifications"),
|
||||
"mx_RoomSettingsDialog_notificationsIcon",
|
||||
<NotificationSettingsTab roomId={this.props.roomId} />,
|
||||
<NotificationSettingsTab roomId={this.props.roomId} closeSettingsFn={() => this.props.onFinished(true)} />,
|
||||
));
|
||||
|
||||
if (SettingsStore.getValue("feature_bridge_state")) {
|
||||
|
|
|
@ -207,17 +207,19 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
|
|||
</Group>;
|
||||
};
|
||||
|
||||
const onRoomMembersClick = () => {
|
||||
export const onRoomMembersClick = (allowClose = true) => {
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.RoomMemberList,
|
||||
allowClose,
|
||||
});
|
||||
};
|
||||
|
||||
const onRoomFilesClick = () => {
|
||||
export const onRoomFilesClick = (allowClose = true) => {
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.FilePanel,
|
||||
allowClose,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -275,10 +277,13 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
|||
return <BaseCard header={header} className="mx_RoomSummaryCard" onClose={onClose}>
|
||||
<Group title={_t("About")} className="mx_RoomSummaryCard_aboutGroup">
|
||||
<Button className="mx_RoomSummaryCard_icon_people" onClick={onRoomMembersClick}>
|
||||
{ _t("%(count)s people", { count: memberCount }) }
|
||||
{ _t("People") }
|
||||
<span className="mx_BaseCard_Button_sublabel">
|
||||
{ memberCount }
|
||||
</span>
|
||||
</Button>
|
||||
<Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomFilesClick}>
|
||||
{ _t("Show files") }
|
||||
{ _t("Files") }
|
||||
</Button>
|
||||
<Button className="mx_RoomSummaryCard_icon_export" onClick={onRoomExportClick}>
|
||||
{ _t("Export chat") }
|
||||
|
|
|
@ -36,6 +36,9 @@ import { MatrixEvent, Room, RoomState } from 'matrix-js-sdk/src';
|
|||
import { E2EStatus } from '../../../utils/ShieldUtils';
|
||||
import { IOOBData } from '../../../stores/ThreepidInviteStore';
|
||||
import { SearchScope } from './SearchBar';
|
||||
import { ContextMenuTooltipButton } from '../../structures/ContextMenu';
|
||||
import RoomContextMenu from "../context_menus/RoomContextMenu";
|
||||
import { contextMenuBelow } from './RoomTile';
|
||||
|
||||
export interface ISearchInfo {
|
||||
searchTerm: string;
|
||||
|
@ -47,7 +50,6 @@ interface IProps {
|
|||
room: Room;
|
||||
oobData?: IOOBData;
|
||||
inRoom: boolean;
|
||||
onSettingsClick: () => void;
|
||||
onSearchClick: () => void;
|
||||
onForgetClick: () => void;
|
||||
onCallPlaced: (type: PlaceCallType) => void;
|
||||
|
@ -57,13 +59,23 @@ interface IProps {
|
|||
searchInfo: ISearchInfo;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
contextMenuPosition?: DOMRect;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.rooms.RoomHeader")
|
||||
export default class RoomHeader extends React.Component<IProps> {
|
||||
export default class RoomHeader extends React.Component<IProps, IState> {
|
||||
static defaultProps = {
|
||||
editing: false,
|
||||
inRoom: false,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||
|
@ -97,6 +109,17 @@ export default class RoomHeader extends React.Component<IProps> {
|
|||
});
|
||||
}
|
||||
|
||||
private onContextMenuOpenClick = (ev: React.MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const target = ev.target as HTMLButtonElement;
|
||||
this.setState({ contextMenuPosition: target.getBoundingClientRect() });
|
||||
};
|
||||
|
||||
private onContextMenuCloseClick = () => {
|
||||
this.setState({ contextMenuPosition: null });
|
||||
};
|
||||
|
||||
public render() {
|
||||
let searchStatus = null;
|
||||
|
||||
|
@ -127,17 +150,35 @@ export default class RoomHeader extends React.Component<IProps> {
|
|||
oobName = this.props.oobData.name;
|
||||
}
|
||||
|
||||
let contextMenu: JSX.Element;
|
||||
if (this.state.contextMenuPosition && this.props.room) {
|
||||
contextMenu = (
|
||||
<RoomContextMenu
|
||||
{...contextMenuBelow(this.state.contextMenuPosition)}
|
||||
room={this.props.room}
|
||||
onFinished={this.onContextMenuCloseClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const textClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint });
|
||||
const name =
|
||||
<div className="mx_RoomHeader_name" onClick={this.props.onSettingsClick}>
|
||||
const name = (
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_RoomHeader_name"
|
||||
onClick={this.onContextMenuOpenClick}
|
||||
isExpanded={!!this.state.contextMenuPosition}
|
||||
title={_t("Room options")}
|
||||
>
|
||||
<RoomName room={this.props.room}>
|
||||
{ (name) => {
|
||||
const roomName = name || oobName;
|
||||
return <div dir="auto" className={textClasses} title={roomName}>{ roomName }</div>;
|
||||
} }
|
||||
</RoomName>
|
||||
{ searchStatus }
|
||||
</div>;
|
||||
{ this.props.room && <div className="mx_RoomHeader_chevron" /> }
|
||||
{ contextMenu }
|
||||
</ContextMenuTooltipButton>
|
||||
);
|
||||
|
||||
const topicElement = <RoomTopic room={this.props.room}>
|
||||
{ (topic, ref) => <div className="mx_RoomHeader_topic" ref={ref} title={topic} dir="auto">
|
||||
|
@ -149,7 +190,7 @@ export default class RoomHeader extends React.Component<IProps> {
|
|||
if (this.props.room) {
|
||||
roomAvatar = <DecoratedRoomAvatar
|
||||
room={this.props.room}
|
||||
avatarSize={32}
|
||||
avatarSize={24}
|
||||
oobData={this.props.oobData}
|
||||
viewAvatarOnClick={true}
|
||||
/>;
|
||||
|
@ -219,6 +260,7 @@ export default class RoomHeader extends React.Component<IProps> {
|
|||
<div className="mx_RoomHeader_avatar">{ roomAvatar }</div>
|
||||
<div className="mx_RoomHeader_e2eIcon">{ e2eIcon }</div>
|
||||
{ name }
|
||||
{ searchStatus }
|
||||
{ topicElement }
|
||||
{ rightRow }
|
||||
<RoomHeaderButtons room={this.props.room} />
|
||||
|
|
|
@ -70,7 +70,7 @@ interface IState {
|
|||
|
||||
const messagePreviewId = (roomId: string) => `mx_RoomTile_messagePreview_${roomId}`;
|
||||
|
||||
const contextMenuBelow = (elementRect: PartialDOMRect) => {
|
||||
export const contextMenuBelow = (elementRect: PartialDOMRect) => {
|
||||
// align the context menu's icons with the icon which opened the context menu
|
||||
const left = elementRect.left + window.pageXOffset - 9;
|
||||
const top = elementRect.bottom + window.pageYOffset + 17;
|
||||
|
@ -189,7 +189,6 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
this.notificationState.off(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
|
||||
this.roomProps.off(PROPERTY_UPDATED, this.onRoomPropertyUpdate);
|
||||
this.roomProps.off("Room.name", this.onRoomNameUpdate);
|
||||
CommunityPrototypeStore.instance.off(
|
||||
CommunityPrototypeStore.getUpdateEventName(this.props.room.roomId),
|
||||
this.onCommunityUpdate,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019 - 2021 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.
|
||||
|
@ -24,9 +24,18 @@ import { SettingLevel } from "../../../../../settings/SettingLevel";
|
|||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { RoomEchoChamber } from "../../../../../stores/local-echo/RoomEchoChamber";
|
||||
import { EchoChamber } from '../../../../../stores/local-echo/EchoChamber';
|
||||
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
|
||||
import StyledRadioGroup from "../../../elements/StyledRadioGroup";
|
||||
import { RoomNotifState } from '../../../../../RoomNotifs';
|
||||
import defaultDispatcher from "../../../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../dispatcher/actions";
|
||||
import { UserTab } from "../../../dialogs/UserSettingsDialog";
|
||||
|
||||
interface IProps {
|
||||
roomId: string;
|
||||
closeSettingsFn(): void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -36,10 +45,16 @@ interface IState {
|
|||
|
||||
@replaceableComponent("views.settings.tabs.room.NotificationsSettingsTab")
|
||||
export default class NotificationsSettingsTab extends React.Component<IProps, IState> {
|
||||
private readonly roomProps: RoomEchoChamber;
|
||||
private soundUpload = createRef<HTMLInputElement>();
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
||||
constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||
super(props, context);
|
||||
|
||||
this.roomProps = EchoChamber.forRoom(context.getRoom(this.props.roomId));
|
||||
|
||||
this.state = {
|
||||
currentSound: "default",
|
||||
|
@ -144,6 +159,19 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
|
|||
});
|
||||
};
|
||||
|
||||
private onRoomNotificationChange = (value: RoomNotifState) => {
|
||||
this.roomProps.notificationVolume = value;
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
private onOpenSettingsClick = () => {
|
||||
this.props.closeSettingsFn();
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Notifications,
|
||||
});
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
let currentUploadedFile = null;
|
||||
if (this.state.uploadedFile) {
|
||||
|
@ -157,6 +185,63 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
|
|||
return (
|
||||
<div className="mx_SettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{ _t("Notifications") }</div>
|
||||
|
||||
<div className="mx_SettingsTab_section mx_NotificationSettingsTab_notificationsSection">
|
||||
<StyledRadioGroup
|
||||
name="roomNotificationSetting"
|
||||
definitions={[
|
||||
{
|
||||
value: RoomNotifState.AllMessages,
|
||||
className: "mx_NotificationSettingsTab_defaultEntry",
|
||||
label: <>
|
||||
{ _t("Default") }
|
||||
<div className="mx_NotificationSettingsTab_microCopy">
|
||||
{ _t("Get notifications as set up in your <a>settings</a>", {}, {
|
||||
a: sub => <AccessibleButton kind="link" onClick={this.onOpenSettingsClick}>
|
||||
{ sub }
|
||||
</AccessibleButton>,
|
||||
}) }
|
||||
</div>
|
||||
</>,
|
||||
}, {
|
||||
value: RoomNotifState.AllMessagesLoud,
|
||||
className: "mx_NotificationSettingsTab_allMessagesEntry",
|
||||
label: <>
|
||||
{ _t("All messages") }
|
||||
<div className="mx_NotificationSettingsTab_microCopy">
|
||||
{ _t("Get notified for every message") }
|
||||
</div>
|
||||
</>,
|
||||
}, {
|
||||
value: RoomNotifState.MentionsOnly,
|
||||
className: "mx_NotificationSettingsTab_mentionsKeywordsEntry",
|
||||
label: <>
|
||||
{ _t("@mentions & keywords") }
|
||||
<div className="mx_NotificationSettingsTab_microCopy">
|
||||
{ _t("Get notified only with mentions and keywords " +
|
||||
"as set up in your <a>settings</a>", {}, {
|
||||
a: sub => <AccessibleButton kind="link" onClick={this.onOpenSettingsClick}>
|
||||
{ sub }
|
||||
</AccessibleButton>,
|
||||
}) }
|
||||
</div>
|
||||
</>,
|
||||
}, {
|
||||
value: RoomNotifState.Mute,
|
||||
className: "mx_NotificationSettingsTab_noneEntry",
|
||||
label: <>
|
||||
{ _t("Off") }
|
||||
<div className="mx_NotificationSettingsTab_microCopy">
|
||||
{ _t("You won't get any notifications") }
|
||||
</div>
|
||||
</>,
|
||||
},
|
||||
]}
|
||||
onChange={this.onRoomNotificationChange}
|
||||
value={this.roomProps.notificationVolume}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mx_SettingsTab_section mx_SettingsTab_subsectionText'>
|
||||
<span className='mx_SettingsTab_subheading'>{ _t("Sounds") }</span>
|
||||
<div>
|
||||
|
|
|
@ -1470,6 +1470,12 @@
|
|||
"URL Previews": "URL Previews",
|
||||
"Room Addresses": "Room Addresses",
|
||||
"Uploaded sound": "Uploaded sound",
|
||||
"Get notifications as set up in your <a>settings</a>": "Get notifications as set up in your <a>settings</a>",
|
||||
"All messages": "All messages",
|
||||
"Get notified for every message": "Get notified for every message",
|
||||
"@mentions & keywords": "@mentions & keywords",
|
||||
"Get notified only with mentions and keywords as set up in your <a>settings</a>": "Get notified only with mentions and keywords as set up in your <a>settings</a>",
|
||||
"You won't get any notifications": "You won't get any notifications",
|
||||
"Sounds": "Sounds",
|
||||
"Notification sound": "Notification sound",
|
||||
"Set a new custom sound": "Set a new custom sound",
|
||||
|
@ -1680,6 +1686,7 @@
|
|||
"(~%(count)s results)|other": "(~%(count)s results)",
|
||||
"(~%(count)s results)|one": "(~%(count)s result)",
|
||||
"Join Room": "Join Room",
|
||||
"Room options": "Room options",
|
||||
"Forget room": "Forget room",
|
||||
"Hide Widgets": "Hide Widgets",
|
||||
"Show Widgets": "Show Widgets",
|
||||
|
@ -1761,7 +1768,6 @@
|
|||
"Show %(count)s more|one": "Show %(count)s more",
|
||||
"Show less": "Show less",
|
||||
"Use default": "Use default",
|
||||
"All messages": "All messages",
|
||||
"Mentions & Keywords": "Mentions & Keywords",
|
||||
"Notification options": "Notification options",
|
||||
"Forget Room": "Forget Room",
|
||||
|
@ -1772,7 +1778,6 @@
|
|||
"Copy Room Link": "Copy Room Link",
|
||||
"Settings": "Settings",
|
||||
"Leave Room": "Leave Room",
|
||||
"Room options": "Room options",
|
||||
"%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.",
|
||||
"%(count)s unread messages including mentions.|one": "1 unread mention.",
|
||||
"%(count)s unread messages.|other": "%(count)s unread messages.",
|
||||
|
@ -1873,9 +1878,7 @@
|
|||
"Add widgets, bridges & bots": "Add widgets, bridges & bots",
|
||||
"Not encrypted": "Not encrypted",
|
||||
"About": "About",
|
||||
"%(count)s people|other": "%(count)s people",
|
||||
"%(count)s people|one": "%(count)s person",
|
||||
"Show files": "Show files",
|
||||
"Files": "Files",
|
||||
"Export chat": "Export chat",
|
||||
"Share room": "Share room",
|
||||
"Room settings": "Room settings",
|
||||
|
@ -2738,6 +2741,10 @@
|
|||
"Collapse reply thread": "Collapse reply thread",
|
||||
"Report": "Report",
|
||||
"View in room": "View in room",
|
||||
"Forget": "Forget",
|
||||
"Leave": "Leave",
|
||||
"Mentions only": "Mentions only",
|
||||
"Copy link": "Copy link",
|
||||
"See room timeline (devtools)": "See room timeline (devtools)",
|
||||
"Add space": "Add space",
|
||||
"Manage & explore rooms": "Manage & explore rooms",
|
||||
|
@ -2845,7 +2852,6 @@
|
|||
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.",
|
||||
"Leave Community": "Leave Community",
|
||||
"Leave %(groupName)s?": "Leave %(groupName)s?",
|
||||
"Leave": "Leave",
|
||||
"Unable to leave community": "Unable to leave community",
|
||||
"Community Settings": "Community Settings",
|
||||
"Want more than a community? <a>Get your own server</a>": "Want more than a community? <a>Get your own server</a>",
|
||||
|
|
|
@ -19,6 +19,7 @@ import { getRoomNotifsState, RoomNotifState, setRoomNotifsState } from "../../Ro
|
|||
import { RoomEchoContext } from "./RoomEchoContext";
|
||||
import { _t } from "../../languageHandler";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
|
||||
export enum CachedRoomKey {
|
||||
NotificationVolume,
|
||||
|
@ -46,7 +47,7 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
|
|||
}
|
||||
|
||||
private onAccountData = (event: MatrixEvent) => {
|
||||
if (event.getType() === "m.push_rules") {
|
||||
if (event.getType() === EventType.PushRules) {
|
||||
const currentVolume = this.properties.get(CachedRoomKey.NotificationVolume) as RoomNotifState;
|
||||
const newVolume = getRoomNotifsState(this.context.room.roomId) as RoomNotifState;
|
||||
if (currentVolume !== newVolume) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue