Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0482ec1
Extract room general context menu from roomtile
justjanne Jun 27, 2022
cd8a5f2
Create hook to access and change a room’s notification state
justjanne Jun 27, 2022
9be9869
Extract room notification context menu from roomtile
justjanne Jun 27, 2022
5cf0b28
Add room context menus to rooms in spotlight
justjanne Jun 27, 2022
66f5b88
Update i18n
justjanne Jun 27, 2022
3a4245e
Merge branch 'develop' into justjanne/feat/22361-spotlight-accessibility
justjanne Jun 27, 2022
7e98180
Implement changes requested by designer
justjanne Jun 28, 2022
ea51b43
Merge branch 'develop' into justjanne/feat/22361-spotlight-accessibility
justjanne Jul 7, 2022
076056a
Merge branch 'develop' into justjanne/feat/22361-spotlight-accessibility
justjanne Jul 7, 2022
e32e1a4
Merge branch 'develop' into justjanne/feat/22361-spotlight-accessibility
justjanne Jul 7, 2022
73093a3
ci: empty commit to force CI to run again
justjanne Jul 8, 2022
1cd3a8f
Merge branch 'develop' into justjanne/feat/22361-spotlight-accessibility
justjanne Jul 8, 2022
91a5790
Merge remote-tracking branch 'origin/develop' into justjanne/feat/223…
justjanne Jul 11, 2022
78bde65
Make arrow movement apply to the whole dialog, not just the input box
justjanne Jul 11, 2022
2d536b6
Use aria-describedby instead of aria-details where possible
justjanne Jul 11, 2022
89e0cd4
Merge remote-tracking branch 'origin/develop' into justjanne/feat/223…
justjanne Jul 11, 2022
5a05020
Improve labels of option groups
justjanne Jul 12, 2022
40e41c0
Merge remote-tracking branch 'origin/develop' into justjanne/feat/223…
justjanne Jul 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
@import "./views/context_menus/_DeviceContextMenu.scss";
@import "./views/context_menus/_IconizedContextMenu.scss";
@import "./views/context_menus/_MessageContextMenu.scss";
@import "./views/context_menus/_RoomGeneralContextMenu.scss";
@import "./views/context_menus/_RoomNotificationContextMenu.scss";
@import "./views/dialogs/_AddExistingToSpaceDialog.scss";
@import "./views/dialogs/_AnalyticsLearnMoreDialog.scss";
@import "./views/dialogs/_BugReportDialog.scss";
Expand Down
63 changes: 63 additions & 0 deletions res/css/views/context_menus/_RoomGeneralContextMenu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.mx_RoomGeneralContextMenu_iconStar::before {
mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg');
}

.mx_RoomGeneralContextMenu_iconArrowDown::before {
mask-image: url('$(res)/img/element-icons/roomlist/low-priority.svg');
}

.mx_RoomGeneralContextMenu_iconNotificationsDefault::before {
mask-image: url('$(res)/img/element-icons/notifications.svg');
}

.mx_RoomGeneralContextMenu_iconNotificationsAllMessages::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-default.svg');
}

.mx_RoomGeneralContextMenu_iconNotificationsMentionsKeywords::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-dm.svg');
}

.mx_RoomGeneralContextMenu_iconNotificationsNone::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-off.svg');
}

.mx_RoomGeneralContextMenu_iconPeople::before {
mask-image: url('$(res)/img/element-icons/room/members.svg');
}

.mx_RoomGeneralContextMenu_iconFiles::before {
mask-image: url('$(res)/img/element-icons/room/files.svg');
}

.mx_RoomGeneralContextMenu_iconPins::before {
mask-image: url('$(res)/img/element-icons/room/pin-upright.svg');
}

.mx_RoomGeneralContextMenu_iconWidgets::before {
mask-image: url('$(res)/img/element-icons/room/apps.svg');
}

.mx_RoomGeneralContextMenu_iconSettings::before {
mask-image: url('$(res)/img/element-icons/settings.svg');
}

.mx_RoomGeneralContextMenu_iconExport::before {
mask-image: url('$(res)/img/element-icons/export.svg');
}

.mx_RoomGeneralContextMenu_iconDeveloperTools::before {
mask-image: url('$(res)/img/element-icons/settings/flask.svg');
}

.mx_RoomGeneralContextMenu_iconCopyLink::before {
mask-image: url('$(res)/img/element-icons/link.svg');
}

.mx_RoomGeneralContextMenu_iconInvite::before {
mask-image: url('$(res)/img/element-icons/room/invite.svg');
}

.mx_RoomGeneralContextMenu_iconSignOut::before {
mask-image: url('$(res)/img/element-icons/leave.svg');
}
12 changes: 12 additions & 0 deletions res/css/views/context_menus/_RoomNotificationContextMenu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.mx_RoomNotificationContextMenu_iconBell::before {
mask-image: url('$(res)/img/element-icons/notifications.svg');
}
.mx_RoomNotificationContextMenu_iconBellDot::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-default.svg');
}
.mx_RoomNotificationContextMenu_iconBellMentions::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-dm.svg');
}
.mx_RoomNotificationContextMenu_iconBellCrossed::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-off.svg');
}
46 changes: 45 additions & 1 deletion res/css/views/dialogs/_SpotlightDialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ limitations under the License.
text-overflow: ellipsis;
overflow: hidden;

.mx_SpotlightDialog_option--endAdornment {
display: inline-flex;
flex-direction: row;
margin-left: auto;
align-items: start;
}

&.mx_SpotlightDialog_result_multiline {
align-items: start;

Expand Down Expand Up @@ -309,8 +316,45 @@ limitations under the License.
margin-left: $spacing-8;
}

.mx_SpotlightDialog_option--menu,
.mx_SpotlightDialog_option--notifications {
width: 20px;
min-width: 20px;
height: 20px;
margin-top: auto;
margin-bottom: auto;
position: relative;
display: none;

&::before {
top: 2px;
left: 2px;
content: '';
width: 16px;
height: 16px;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background: $tertiary-content;
}

&:hover::before, &[aria-selected=true]::before {
background-color: $secondary-content;
}
}

.mx_SpotlightDialog_option--menu::before {
mask-image: url('$(res)/img/element-icons/context-menu.svg');
}

&:hover, &[aria-selected=true] {
background-color: $system;

.mx_SpotlightDialog_option--menu,
.mx_SpotlightDialog_option--notifications {
display: block;
}
}

&[aria-selected=true] .mx_SpotlightDialog_enterPrompt {
Expand Down Expand Up @@ -436,7 +480,7 @@ limitations under the License.
color: $tertiary-content;
border-radius: 6px;
background-color: $quinary-content;
margin: 0 $spacing-4 0 auto;
margin-right: $spacing-4;
display: none;
}

Expand Down
186 changes: 186 additions & 0 deletions src/components/views/context_menus/RoomGeneralContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
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 { logger } from "matrix-js-sdk/src/logger";
import { Room } from "matrix-js-sdk/src/models/room";
import React, { useContext } from "react";

import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import RoomListActions from "../../../actions/RoomListActions";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import dis from "../../../dispatcher/dispatcher";
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { _t } from "../../../languageHandler";
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
import DMRoomMap from "../../../utils/DMRoomMap";
import { IProps as IContextMenuProps } from "../../structures/ContextMenu";
import IconizedContextMenu, {
IconizedContextMenuCheckbox,
IconizedContextMenuOption,
IconizedContextMenuOptionList,
} from "../context_menus/IconizedContextMenu";
import { ButtonEvent } from "../elements/AccessibleButton";

interface IProps extends IContextMenuProps {
room: Room;
onPostFavoriteClick?: (event: ButtonEvent) => void;
onPostLowPriorityClick?: (event: ButtonEvent) => void;
onPostInviteClick?: (event: ButtonEvent) => void;
onPostCopyLinkClick?: (event: ButtonEvent) => void;
onPostSettingsClick?: (event: ButtonEvent) => void;
onPostForgetClick?: (event: ButtonEvent) => void;
onPostLeaveClick?: (event: ButtonEvent) => void;
}

export const RoomGeneralContextMenu = ({
room, onFinished,
onPostFavoriteClick, onPostLowPriorityClick, onPostInviteClick, onPostCopyLinkClick, onPostSettingsClick,
onPostLeaveClick, onPostForgetClick, ...props
}: IProps) => {
const cli = useContext(MatrixClientContext);
const roomTags = useEventEmitterState(
RoomListStore.instance,
LISTS_UPDATE_EVENT,
() => RoomListStore.instance.getTagsForRoom(room),
);
const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
const wrapHandler = (
handler: (ev: ButtonEvent) => void,
postHandler?: (ev: ButtonEvent) => void,
persistent = false,
): (ev: ButtonEvent) => void => {
return (ev: ButtonEvent) => {
ev.preventDefault();
ev.stopPropagation();

handler(ev);

const action = getKeyBindingsManager().getAccessibilityAction(ev as React.KeyboardEvent);
if (!persistent || action === KeyBindingAction.Enter) {
onFinished();
}
postHandler?.(ev);
};
};

const onTagRoom = (ev: ButtonEvent, tagId: TagID) => {
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}`);
}
};

const isFavorite = roomTags.includes(DefaultTagID.Favourite);
const favoriteOption: JSX.Element = <IconizedContextMenuCheckbox
onClick={wrapHandler((ev) =>
onTagRoom(ev, DefaultTagID.Favourite), onPostFavoriteClick, true)}
active={isFavorite}
label={isFavorite ? _t("Favourited") : _t("Favourite")}
iconClassName="mx_RoomGeneralContextMenu_iconStar"
/>;

const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
const lowPriorityOption: JSX.Element = <IconizedContextMenuCheckbox
onClick={wrapHandler((ev) =>
onTagRoom(ev, DefaultTagID.LowPriority), onPostLowPriorityClick, true)}
active={isLowPriority}
label={_t("Low Priority")}
iconClassName="mx_RoomGeneralContextMenu_iconArrowDown"
/>;

let inviteOption: JSX.Element;
if (room.canInvite(cli.getUserId()) && !isDm) {
inviteOption = <IconizedContextMenuOption
onClick={wrapHandler(() => dis.dispatch({
action: "view_invite",
roomId: room.roomId,
}), onPostInviteClick)}
label={_t("Invite")}
iconClassName="mx_RoomGeneralContextMenu_iconInvite"
/>;
}

let copyLinkOption: JSX.Element;
if (!isDm) {
copyLinkOption = <IconizedContextMenuOption
onClick={wrapHandler(() => dis.dispatch({
action: "copy_room",
room_id: room.roomId,
}), onPostCopyLinkClick)}
label={_t("Copy room link")}
iconClassName="mx_RoomGeneralContextMenu_iconCopyLink"
/>;
}

const settingsOption: JSX.Element = <IconizedContextMenuOption
onClick={wrapHandler(() => dis.dispatch({
action: "open_room_settings",
room_id: room.roomId,
}), onPostSettingsClick)}
label={_t("Settings")}
iconClassName="mx_RoomGeneralContextMenu_iconSettings"
/>;

let leaveOption: JSX.Element;
if (roomTags.includes(DefaultTagID.Archived)) {
leaveOption = <IconizedContextMenuOption
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
label={_t("Forget Room")}
className="mx_IconizedContextMenu_option_red"
onClick={wrapHandler(() => dis.dispatch({
action: "forget_room",
room_id: room.roomId,
}), onPostForgetClick)}
/>;
} else {
leaveOption = <IconizedContextMenuOption
onClick={wrapHandler(() => dis.dispatch({
action: "leave_room",
room_id: room.roomId,
}), onPostLeaveClick)}
label={_t("Leave")}
className="mx_IconizedContextMenu_option_red"
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
/>;
}

return <IconizedContextMenu
{...props}
onFinished={onFinished}
className="mx_RoomGeneralContextMenu"
compact
>
{ !roomTags.includes(DefaultTagID.Archived) && (
<IconizedContextMenuOptionList>
{ favoriteOption }
{ lowPriorityOption }
{ inviteOption }
{ copyLinkOption }
{ settingsOption }
</IconizedContextMenuOptionList>
) }
<IconizedContextMenuOptionList red>
{ leaveOption }
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
};
Loading