Improve icon rendering in iconized context menu (#31458)

* Fix composer button visibility in contrast colour mode

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Simplify

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve icon rendering in iconized context menu

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2025-12-09 15:10:42 +00:00 committed by GitHub
parent 4fda167c11
commit efe59ff35f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 440 additions and 564 deletions

View File

@ -120,9 +120,6 @@
@import "./views/context_menus/_DeviceContextMenu.pcss";
@import "./views/context_menus/_IconizedContextMenu.pcss";
@import "./views/context_menus/_LegacyCallContextMenu.pcss";
@import "./views/context_menus/_MessageContextMenu.pcss";
@import "./views/context_menus/_RoomGeneralContextMenu.pcss";
@import "./views/context_menus/_RoomNotificationContextMenu.pcss";
@import "./views/dialogs/_AddExistingToSpaceDialog.pcss";
@import "./views/dialogs/_AnalyticsLearnMoreDialog.pcss";
@import "./views/dialogs/_BugReportDialog.pcss";

View File

@ -428,46 +428,6 @@ Please see LICENSE files in the repository root for full details.
white-space: nowrap;
}
.mx_SpacePanel_iconHome::before {
mask-image: url("@vector-im/compound-design-tokens/icons/home-solid.svg");
}
.mx_SpacePanel_iconInvite::before {
mask-image: url("$(res)/img/element-icons/room/invite.svg");
}
.mx_SpacePanel_iconSettings::before {
mask-image: url("@vector-im/compound-design-tokens/icons/settings-solid.svg");
}
.mx_SpacePanel_iconLeave::before {
mask-image: url("@vector-im/compound-design-tokens/icons/leave.svg");
}
.mx_SpacePanel_iconMembers::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-profile-solid.svg");
}
.mx_SpacePanel_iconPlus::before {
mask-image: url("@vector-im/compound-design-tokens/icons/plus.svg");
}
.mx_SpacePanel_iconExplore::before {
mask-image: url("@vector-im/compound-design-tokens/icons/search.svg");
}
.mx_SpacePanel_iconPreferences::before {
mask-image: url("@vector-im/compound-design-tokens/icons/preferences.svg");
}
.mx_SpacePanel_noIcon {
display: none;
& + .mx_IconizedContextMenu_label {
padding-left: 5px !important; /* override default iconized label style to align with header */
}
}
.mx_SpacePanel_contextMenu_separatorLabel {
color: $tertiary-content;
font-size: $font-10px;

View File

@ -116,48 +116,11 @@ Please see LICENSE files in the repository root for full details.
}
}
.mx_IconizedContextMenu_icon {
width: 16px;
height: 16px;
display: block;
&::before {
content: "";
width: 16px;
height: 16px;
display: block;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background: $icon-button-color;
}
}
.mx_UserMenu_iconHome::before {
mask-image: url("@vector-im/compound-design-tokens/icons/home-solid.svg");
}
.mx_UserMenu_iconBell::before {
mask-image: url("$(res)/img/element-icons/notifications.svg");
}
.mx_UserMenu_iconLock::before {
mask-image: url("@vector-im/compound-design-tokens/icons/lock-solid.svg");
}
.mx_UserMenu_iconSettings::before {
mask-image: url("@vector-im/compound-design-tokens/icons/settings-solid.svg");
.mx_IconizedContextMenu_icon svg {
color: $icon-button-color;
}
.mx_UserMenu_iconMessage::before {
mask-image: url("$(res)/img/element-icons/feedback.svg");
}
.mx_UserMenu_iconSignOut::before {
mask-image: url("@vector-im/compound-design-tokens/icons/leave.svg");
}
.mx_UserMenu_iconQr::before {
mask-image: url("@vector-im/compound-design-tokens/icons/qr-code.svg");
}
}

View File

@ -9,10 +9,6 @@ Please see LICENSE files in the repository root for full details.
.mx_DeviceContextMenu {
max-width: 252px;
.mx_DeviceContextMenu_device_icon {
display: none;
}
.mx_IconizedContextMenu_label {
padding-left: 0 !important;
}

View File

@ -68,19 +68,6 @@ Please see LICENSE files in the repository root for full details.
cursor: not-allowed;
}
img,
svg,
.mx_IconizedContextMenu_icon {
/* icons */
width: 16px;
min-width: 16px;
max-width: 16px;
& + .mx_IconizedContextMenu_label {
padding-left: 14px;
}
}
span.mx_IconizedContextMenu_label {
/* labels */
width: 100%;
@ -92,27 +79,23 @@ Please see LICENSE files in the repository root for full details.
white-space: nowrap;
}
svg {
width: 16px;
height: 16px;
display: block;
flex-shrink: 0;
& + .mx_IconizedContextMenu_label {
padding-left: 14px;
}
}
.mx_BetaCard_betaPill {
margin-left: 16px;
}
}
}
.mx_IconizedContextMenu_icon {
position: relative;
&::before {
content: "";
width: inherit;
height: inherit;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background-color: var(--cpd-color-icon-primary);
}
}
.mx_IconizedContextMenu_optionList_red {
.mx_IconizedContextMenu_item {
color: $alert !important;
@ -121,10 +104,6 @@ Please see LICENSE files in the repository root for full details.
svg {
color: var(--cpd-color-icon-critical-primary);
}
.mx_IconizedContextMenu_icon::before {
background-color: var(--cpd-color-icon-critical-primary);
}
}
.mx_IconizedContextMenu_option_red {
@ -133,24 +112,16 @@ Please see LICENSE files in the repository root for full details.
svg {
color: $alert;
}
.mx_IconizedContextMenu_icon::before {
background-color: $alert;
}
}
.mx_IconizedContextMenu_active {
&.mx_IconizedContextMenu_item,
.mx_IconizedContextMenu_item {
color: $accent !important;
}
svg {
color: $accent;
}
.mx_IconizedContextMenu_icon::before {
background-color: $accent;
svg {
color: $accent;
}
}
}
@ -160,24 +131,11 @@ Please see LICENSE files in the repository root for full details.
}
}
.mx_IconizedContextMenu_checked,
.mx_IconizedContextMenu_unchecked {
svg.mx_IconizedContextMenu_checked {
margin-left: 16px;
margin-right: -5px;
}
.mx_IconizedContextMenu_developerTools::before {
mask-image: url("@vector-im/compound-design-tokens/icons/labs.svg");
}
.mx_IconizedContextMenu_checked::before {
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
}
.mx_IconizedContextMenu_unchecked::before {
content: unset;
}
.mx_IconizedContextMenu_sublabel {
margin-left: 20px;
color: $tertiary-content;

View File

@ -1,113 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 Michael Weimann <mail@michael-weimann.eu>
Copyright 2015, 2016 OpenMarket Ltd
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
.mx_MessageContextMenu {
.mx_IconizedContextMenu_icon {
width: 16px;
height: 16px;
display: block;
&::before {
content: "";
width: 16px;
height: 16px;
display: block;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
}
}
.mx_MessageContextMenu_iconCollapse::before {
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-up.svg");
}
.mx_MessageContextMenu_iconReport::before {
mask-image: url("@vector-im/compound-design-tokens/icons/error-solid.svg");
}
.mx_MessageContextMenu_iconLink::before {
mask-image: url("@vector-im/compound-design-tokens/icons/link.svg");
}
.mx_MessageContextMenu_iconPermalink::before {
mask-image: url("@vector-im/compound-design-tokens/icons/share.svg");
}
.mx_MessageContextMenu_iconUnhidePreview::before {
mask-image: url("@vector-im/compound-design-tokens/icons/visibility-on.svg");
}
.mx_MessageContextMenu_iconOpenInMapSite::before {
mask-image: url("@vector-im/compound-design-tokens/icons/pop-out.svg");
}
.mx_MessageContextMenu_iconEndPoll::before {
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
}
.mx_MessageContextMenu_iconForward::before {
mask-image: url("@vector-im/compound-design-tokens/icons/forward.svg");
}
.mx_MessageContextMenu_iconRedact::before {
mask-image: url("@vector-im/compound-design-tokens/icons/delete.svg");
}
.mx_MessageContextMenu_iconResend::before {
mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg");
}
.mx_MessageContextMenu_iconSource::before {
mask-image: url("@vector-im/compound-design-tokens/icons/inline-code.svg");
}
.mx_MessageContextMenu_iconQuote::before {
mask-image: url("@vector-im/compound-design-tokens/icons/quote.svg");
}
.mx_MessageContextMenu_iconPin::before {
mask-image: url("@vector-im/compound-design-tokens/icons/pin.svg");
}
.mx_MessageContextMenu_iconUnpin::before {
mask-image: url("@vector-im/compound-design-tokens/icons/unpin.svg");
}
.mx_MessageContextMenu_iconCopy::before {
height: 16px;
mask-image: url($copy-button-url);
position: relative;
width: 16px;
}
.mx_MessageContextMenu_iconEdit::before {
mask-image: url("@vector-im/compound-design-tokens/icons/edit.svg");
}
.mx_MessageContextMenu_iconReply::before {
mask-image: url("@vector-im/compound-design-tokens/icons/reply.svg");
}
.mx_MessageContextMenu_iconReplyInThread::before {
mask-image: url("@vector-im/compound-design-tokens/icons/threads.svg");
}
.mx_MessageContextMenu_iconReact::before {
mask-image: url("@vector-im/compound-design-tokens/icons/reaction-add.svg");
}
.mx_MessageContextMenu_iconViewInRoom::before {
mask-image: url("$(res)/img/element-icons/view-in-room.svg");
}
.mx_MessageContextMenu_jumpToEvent::before {
mask-image: url("$(res)/img/element-icons/child-relationship.svg");
}
}

View File

@ -1,31 +0,0 @@
.mx_RoomGeneralContextMenu_iconStar::before {
mask-image: url("@vector-im/compound-design-tokens/icons/favourite-solid.svg");
}
.mx_RoomGeneralContextMenu_iconArrowDown::before {
mask-image: url("@vector-im/compound-design-tokens/icons/arrow-down.svg");
}
.mx_RoomGeneralContextMenu_iconMarkAsRead::before {
mask-image: url("@vector-im/compound-design-tokens/icons/mark-as-read.svg");
}
.mx_RoomGeneralContextMenu_iconMarkAsUnread::before {
mask-image: url("@vector-im/compound-design-tokens/icons/mark-as-unread.svg");
}
.mx_RoomGeneralContextMenu_iconSettings::before {
mask-image: url("@vector-im/compound-design-tokens/icons/settings-solid.svg");
}
.mx_RoomGeneralContextMenu_iconCopyLink::before {
mask-image: url("@vector-im/compound-design-tokens/icons/link.svg");
}
.mx_RoomGeneralContextMenu_iconInvite::before {
mask-image: url("$(res)/img/element-icons/room/invite.svg");
}
.mx_RoomGeneralContextMenu_iconSignOut::before {
mask-image: url("@vector-im/compound-design-tokens/icons/leave.svg");
}

View File

@ -1,12 +0,0 @@
.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");
}

View File

@ -340,37 +340,28 @@ Please see LICENSE files in the repository root for full details.
.mx_SpotlightDialog_option--menu,
.mx_SpotlightDialog_option--notifications {
width: 20px;
min-width: 20px;
height: 20px;
width: 16px;
height: 16px;
padding: var(--cpd-space-0-5x);
flex-shrink: 0;
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;
svg {
width: inherit;
height: inherit;
display: block;
color: $tertiary-content;
}
&:hover::before,
&:focus-visible::before {
background-color: $secondary-content;
&:hover svg,
&:focus-visible svg {
color: $secondary-content;
}
}
.mx_SpotlightDialog_option--menu::before {
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
}
&:hover,
&[aria-selected="true"] {
.mx_SpotlightDialog_option--menu,

View File

@ -167,14 +167,6 @@ Please see LICENSE files in the repository root for full details.
}
}
.mx_ThreadPanel_viewInRoom::before {
mask-image: url("$(res)/img/element-icons/view-in-room.svg");
}
.mx_ThreadPanel_copyLinkToThread::before {
mask-image: url("@vector-im/compound-design-tokens/icons/link.svg");
}
.mx_ContextualMenu_wrapper {
.mx_ThreadPanel_Header_FilterOptionItem {
display: flex;

View File

@ -9,25 +9,3 @@ Please see LICENSE files in the repository root for full details.
.mx_LegacyRoomList {
padding-right: 7px; /* width of the scrollbar, to line things up */
}
.mx_LegacyRoomList_iconPlus::before {
mask-image: url("@vector-im/compound-design-tokens/icons/plus.svg");
}
.mx_LegacyRoomList_iconNewRoom::before {
mask-image: url("@vector-im/compound-design-tokens/icons/plus.svg");
}
.mx_LegacyRoomList_iconNewVideoRoom::before {
mask-image: url("$(res)/img/element-icons/roomlist/hash-video.svg");
}
.mx_LegacyRoomList_iconAddExistingRoom::before {
mask-image: url("@vector-im/compound-design-tokens/icons/room.svg");
}
.mx_LegacyRoomList_iconExplore::before {
mask-image: url("@vector-im/compound-design-tokens/icons/search.svg");
}
.mx_LegacyRoomList_iconStartChat::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-add-solid.svg");
}
.mx_LegacyRoomList_iconInvite::before {
mask-image: url("@vector-im/compound-design-tokens/icons/share.svg");
}

View File

@ -87,22 +87,3 @@ Please see LICENSE files in the repository root for full details.
}
}
}
.mx_LegacyRoomListHeader_iconInvite::before {
mask-image: url("$(res)/img/element-icons/room/invite.svg");
}
.mx_LegacyRoomListHeader_iconStartChat::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-add-solid.svg");
}
.mx_LegacyRoomListHeader_iconNewRoom::before {
mask-image: url("@vector-im/compound-design-tokens/icons/plus.svg");
}
.mx_LegacyRoomListHeader_iconNewVideoRoom::before {
mask-image: url("$(res)/img/element-icons/roomlist/hash-video.svg");
}
.mx_LegacyRoomListHeader_iconExplore::before {
mask-image: url("@vector-im/compound-design-tokens/icons/search.svg");
}
.mx_LegacyRoomListHeader_iconPlus::before {
mask-image: url("@vector-im/compound-design-tokens/icons/plus.svg");
}

View File

@ -105,35 +105,23 @@ Please see LICENSE files in the repository root for full details.
/* The context menu buttons are hidden by default */
.mx_RoomTile_menuButton,
.mx_RoomTile_notificationsButton {
width: 20px;
min-width: 20px; /* yay flex */
height: 20px;
width: 16px;
height: 16px;
padding: var(--cpd-space-0-5x);
flex-shrink: 0;
margin-top: auto;
margin-bottom: auto;
position: relative;
display: none;
svg {
width: 16px;
height: 16px;
padding: var(--cpd-space-0-5x);
width: inherit;
height: inherit;
display: block;
color: var(--cpd-color-icon-primary);
}
}
.mx_RoomTile_notificationsButton::before {
top: 2px;
left: 2px;
content: "";
width: 16px;
height: 16px;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background: var(--cpd-color-icon-primary);
}
/* If the room has an overriden notification setting then we always show the notifications menu button */
.mx_RoomTile_notificationsButton.mx_RoomTile_notificationsButton_show {
display: block;

View File

@ -1,5 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2375 19.5C10.1625 19.7 10.125 19.9 10.125 20.125C10.125 21.1625 10.9625 22 12 22C13.0375 22 13.875 21.1625 13.875 20.125C13.875 19.9 13.825 19.7 13.7625 19.5H10.2375Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6125 16.0625C18.8625 15.625 18.25 14.8875 18.25 13.75V9.5C18.25 6.05 15.3 3.25 12 3.25C8.7 3.25 5.75 6.05 5.75 9.5V13.75C5.75 14.875 5.125 15.625 4.3875 16.0625C3.8875 16.3625 3.25 16.775 3.25 17.275C3.25 17.85 3.6125 18.2375 4.3625 18.2375H12H19.6375C20.3875 18.2375 20.75 17.8375 20.75 17.275C20.75 16.775 20.125 16.3625 19.6125 16.0625Z" fill="black"/>
<path d="M12 4.5C12.6904 4.5 13.25 3.94036 13.25 3.25C13.25 2.55964 12.6904 2 12 2C11.3096 2 10.75 2.55964 10.75 3.25C10.75 3.94036 11.3096 4.5 12 4.5Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2375 19.5C10.1625 19.7 10.125 19.9 10.125 20.125C10.125 21.1625 10.9625 22 12 22C13.0375 22 13.875 21.1625 13.875 20.125C13.875 19.9 13.825 19.7 13.7625 19.5H10.2375Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6125 16.0625C18.8625 15.625 18.25 14.8875 18.25 13.75V9.5C18.25 6.05 15.3 3.25 12 3.25C8.7 3.25 5.75 6.05 5.75 9.5V13.75C5.75 14.875 5.125 15.625 4.3875 16.0625C3.8875 16.3625 3.25 16.775 3.25 17.275C3.25 17.85 3.6125 18.2375 4.3625 18.2375H12H19.6375C20.3875 18.2375 20.75 17.8375 20.75 17.275C20.75 16.775 20.125 16.3625 19.6125 16.0625Z" fill="currentColor"/>
<path d="M12 4.5C12.6904 4.5 13.25 3.94036 13.25 3.25C13.25 2.55964 12.6904 2 12 2C11.3096 2 10.75 2.55964 10.75 3.25C10.75 3.94036 11.3096 4.5 12 4.5Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 937 B

View File

@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="-0.4 1 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.1001 9C18.7779 9 18.5168 8.73883 18.5168 8.41667V6.08333H16.1834C15.8613 6.08333 15.6001 5.82217 15.6001 5.5C15.6001 5.17783 15.8613 4.91667 16.1834 4.91667H18.5168V2.58333C18.5168 2.26117 18.7779 2 19.1001 2C19.4223 2 19.6834 2.26117 19.6834 2.58333V4.91667H22.0168C22.3389 4.91667 22.6001 5.17783 22.6001 5.5C22.6001 5.82217 22.3389 6.08333 22.0168 6.08333H19.6834V8.41667C19.6834 8.73883 19.4223 9 19.1001 9ZM19.6001 11C20.0669 11 20.5212 10.9467 20.9574 10.8458C21.1161 11.5383 21.2 12.2594 21.2 13C21.2 16.1409 19.6917 18.9294 17.3598 20.6808V20.6807C16.0014 21.7011 14.3635 22.3695 12.5815 22.5505C12.2588 22.5832 11.9314 22.6 11.6 22.6C6.29807 22.6 2 18.302 2 13C2 7.69809 6.29807 3.40002 11.6 3.40002C12.3407 3.40002 13.0618 3.48391 13.7543 3.64268C13.6534 4.07884 13.6001 4.53319 13.6001 5C13.6001 8.31371 16.2864 11 19.6001 11ZM11.5999 20.68C13.6754 20.68 15.5585 19.8567 16.9407 18.5189C16.0859 16.4086 14.0167 14.92 11.5998 14.92C9.18298 14.92 7.11378 16.4086 6.25901 18.5189C7.64115 19.8567 9.52436 20.68 11.5999 20.68ZM11.7426 7.41172C10.3168 7.54168 9.2 8.74043 9.2 10.2C9.2 11.7464 10.4536 13 12 13C13.0308 13 13.9315 12.443 14.4176 11.6135C13.0673 10.6058 12.0929 9.12248 11.7426 7.41172Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.1001 9C18.7779 9 18.5168 8.73883 18.5168 8.41667V6.08333H16.1834C15.8613 6.08333 15.6001 5.82217 15.6001 5.5C15.6001 5.17783 15.8613 4.91667 16.1834 4.91667H18.5168V2.58333C18.5168 2.26117 18.7779 2 19.1001 2C19.4223 2 19.6834 2.26117 19.6834 2.58333V4.91667H22.0168C22.3389 4.91667 22.6001 5.17783 22.6001 5.5C22.6001 5.82217 22.3389 6.08333 22.0168 6.08333H19.6834V8.41667C19.6834 8.73883 19.4223 9 19.1001 9ZM19.6001 11C20.0669 11 20.5212 10.9467 20.9574 10.8458C21.1161 11.5383 21.2 12.2594 21.2 13C21.2 16.1409 19.6917 18.9294 17.3598 20.6808V20.6807C16.0014 21.7011 14.3635 22.3695 12.5815 22.5505C12.2588 22.5832 11.9314 22.6 11.6 22.6C6.29807 22.6 2 18.302 2 13C2 7.69809 6.29807 3.40002 11.6 3.40002C12.3407 3.40002 13.0618 3.48391 13.7543 3.64268C13.6534 4.07884 13.6001 4.53319 13.6001 5C13.6001 8.31371 16.2864 11 19.6001 11ZM11.5999 20.68C13.6754 20.68 15.5585 19.8567 16.9407 18.5189C16.0859 16.4086 14.0167 14.92 11.5998 14.92C9.18298 14.92 7.11378 16.4086 6.25901 18.5189C7.64115 19.8567 9.52436 20.68 11.5999 20.68ZM11.7426 7.41172C10.3168 7.54168 9.2 8.74043 9.2 10.2C9.2 11.7464 10.4536 13 12 13C13.0308 13 13.9315 12.443 14.4176 11.6135C13.0673 10.6058 12.0929 9.12248 11.7426 7.41172Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,5 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.59 15C7.53 15.16 7.5 15.32 7.5 15.5C7.5 16.33 8.17 17 9 17C9.83 17 10.5 16.33 10.5 15.5C10.5 15.32 10.46 15.16 10.41 15H7.59Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.4C14 11.31 14.49 11.9 15.09 12.25C15.5 12.49 16 12.82 16 13.22C16 13.67 15.71 13.99 15.11 13.99H9H2.89C2.29 13.99 2 13.68 2 13.22C2 12.82 2.51 12.49 2.91 12.25C3.5 11.9 4 11.3 4 10.4V7C4 4.24 6.36 2 9 2C11.64 2 14 4.24 14 7V10.4ZM11.2402 6.1226C11.4223 5.9515 11.7099 5.96101 11.8824 6.14162C12.0358 6.31272 12.0358 6.56938 11.9016 6.74048L8.92075 10.685L8.892 10.723C8.62358 11.0462 8.13469 11.0938 7.80875 10.8276C7.7798 10.8132 7.75632 10.7881 7.73417 10.7643C7.72698 10.7566 7.71994 10.749 7.71289 10.7421L6.13071 8.99336C5.93899 8.76522 5.95816 8.42302 6.18823 8.2329C6.37995 8.0618 6.65795 8.0618 6.85926 8.19488L8.21138 9.012L11.2402 6.1226Z" fill="black"/>
<path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.59 15C7.53 15.16 7.5 15.32 7.5 15.5C7.5 16.33 8.17 17 9 17C9.83 17 10.5 16.33 10.5 15.5C10.5 15.32 10.46 15.16 10.41 15H7.59Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.4C14 11.31 14.49 11.9 15.09 12.25C15.5 12.49 16 12.82 16 13.22C16 13.67 15.71 13.99 15.11 13.99H9H2.89C2.29 13.99 2 13.68 2 13.22C2 12.82 2.51 12.49 2.91 12.25C3.5 11.9 4 11.3 4 10.4V7C4 4.24 6.36 2 9 2C11.64 2 14 4.24 14 7V10.4ZM11.2402 6.1226C11.4223 5.9515 11.7099 5.96101 11.8824 6.14162C12.0358 6.31272 12.0358 6.56938 11.9016 6.74048L8.92075 10.685L8.892 10.723C8.62358 11.0462 8.13469 11.0938 7.80875 10.8276C7.7798 10.8132 7.75632 10.7881 7.73417 10.7643C7.72698 10.7566 7.71994 10.749 7.71289 10.7421L6.13071 8.99336C5.93899 8.76522 5.95816 8.42302 6.18823 8.2329C6.37995 8.0618 6.65795 8.0618 6.85926 8.19488L8.21138 9.012L11.2402 6.1226Z" fill="currentColor"/>
<path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,3 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 7C14.6569 7 16 5.65685 16 4C16 2.34315 14.6569 1 13 1C11.3431 1 10 2.34315 10 4C10 5.65685 11.3431 7 13 7ZM8.9677 2C6.34078 2.01817 4 4.25115 4 6.99989V10.3999C4 11.2999 3.5 11.8999 2.91 12.2499C2.51 12.4899 2 12.8199 2 13.2199C2 13.6799 2.29 13.9899 2.89 13.9899H9H15.11C15.71 13.9899 16 13.6699 16 13.2199C16 12.8199 15.5 12.4899 15.09 12.2499C14.49 11.8999 14 11.3099 14 10.3999V8.38838C13.6784 8.46136 13.3437 8.49989 13 8.49989C10.5147 8.49989 8.5 6.48517 8.5 3.99989C8.5 3.28151 8.66833 2.60245 8.9677 2ZM7.59 15C7.53 15.16 7.5 15.32 7.5 15.5C7.5 16.33 8.17 17 9 17C9.83 17 10.5 16.33 10.5 15.5C10.5 15.32 10.46 15.16 10.41 15H7.59Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 7C14.6569 7 16 5.65685 16 4C16 2.34315 14.6569 1 13 1C11.3431 1 10 2.34315 10 4C10 5.65685 11.3431 7 13 7ZM8.9677 2C6.34078 2.01817 4 4.25115 4 6.99989V10.3999C4 11.2999 3.5 11.8999 2.91 12.2499C2.51 12.4899 2 12.8199 2 13.2199C2 13.6799 2.29 13.9899 2.89 13.9899H9H15.11C15.71 13.9899 16 13.6699 16 13.2199C16 12.8199 15.5 12.4899 15.09 12.2499C14.49 11.8999 14 11.3099 14 10.3999V8.38838C13.6784 8.46136 13.3437 8.49989 13 8.49989C10.5147 8.49989 8.5 6.48517 8.5 3.99989C8.5 3.28151 8.66833 2.60245 8.9677 2ZM7.59 15C7.53 15.16 7.5 15.32 7.5 15.5C7.5 16.33 8.17 17 9 17C9.83 17 10.5 16.33 10.5 15.5C10.5 15.32 10.46 15.16 10.41 15H7.59Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 811 B

After

Width:  |  Height:  |  Size: 818 B

View File

@ -1,7 +1,7 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.59 15C7.53 15.16 7.5 15.32 7.5 15.5C7.5 16.33 8.17 17 9 17C9.83 17 10.5 16.33 10.5 15.5C10.5 15.32 10.46 15.16 10.41 15H7.59Z" fill="black"/>
<path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.08 10.9698C14.03 10.7998 14 10.6098 14 10.4098V7.00977C14 4.24977 11.64 2.00977 8.99998 2.00977C7.93998 2.00977 6.92998 2.37977 6.09998 2.98977L14.08 10.9698Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.38 5.14978C4.14 5.72978 4 6.34978 4 6.99978V10.3998C4 11.2998 3.5 11.8998 2.91 12.2498C2.51 12.4898 2 12.8198 2 13.2198C2 13.6798 2.29 13.9898 2.89 13.9898H9H13.22L4.38 5.14978Z" fill="black"/>
<path d="M1 1L17 17" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.59 15C7.53 15.16 7.5 15.32 7.5 15.5C7.5 16.33 8.17 17 9 17C9.83 17 10.5 16.33 10.5 15.5C10.5 15.32 10.46 15.16 10.41 15H7.59Z" fill="currentColor"/>
<path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.08 10.9698C14.03 10.7998 14 10.6098 14 10.4098V7.00977C14 4.24977 11.64 2.00977 8.99998 2.00977C7.93998 2.00977 6.92998 2.37977 6.09998 2.98977L14.08 10.9698Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.38 5.14978C4.14 5.72978 4 6.34978 4 6.99978V10.3998C4 11.2998 3.5 11.8998 2.91 12.2498C2.51 12.4898 2 12.8198 2 13.2198C2 13.6798 2.29 13.9898 2.89 13.9898H9H13.22L4.38 5.14978Z" fill="currentColor"/>
<path d="M1 1L17 17" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -10,6 +10,7 @@ import { EventType, RoomType, JoinRule, Preset, type Room, RoomEvent } from "mat
import { KnownMembership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger";
import React, { type JSX, useCallback, useContext, useRef, useState } from "react";
import { PlusIcon, RoomIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import createRoom, { type IOpts } from "../../createRoom";
@ -66,6 +67,7 @@ import MainSplit from "./MainSplit";
import RightPanel from "./RightPanel";
import SpaceHierarchy, { showRoom } from "./SpaceHierarchy";
import { type RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import { Icon as HashVideoIcon } from "../../../res/img/element-icons/roomlist/hash-video.svg";
interface IProps {
space: Room;
@ -117,7 +119,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
<>
<IconizedContextMenuOption
label={_t("action|new_room")}
iconClassName="mx_LegacyRoomList_iconNewRoom"
icon={<PlusIcon />}
onClick={async (e): Promise<void> => {
e.preventDefault();
e.stopPropagation();
@ -132,7 +134,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
{videoRoomsEnabled && (
<IconizedContextMenuOption
label={_t("action|new_video_room")}
iconClassName="mx_LegacyRoomList_iconNewVideoRoom"
icon={<HashVideoIcon />}
onClick={async (e): Promise<void> => {
e.preventDefault();
e.stopPropagation();
@ -157,7 +159,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
)}
<IconizedContextMenuOption
label={_t("action|add_existing_room")}
iconClassName="mx_LegacyRoomList_iconAddExistingRoom"
icon={<RoomIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -168,7 +170,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
{canCreateSpace && (
<IconizedContextMenuOption
label={_t("room_list|add_space_label")}
iconClassName="mx_LegacyRoomList_iconPlus"
icon={<PlusIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();

View File

@ -8,6 +8,13 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, createRef, type ReactNode } from "react";
import { type Room } from "matrix-js-sdk/src/matrix";
import {
HomeSolidIcon,
LockSolidIcon,
QrCodeIcon,
SettingsSolidIcon,
LeaveIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import defaultDispatcher from "../../dispatcher/dispatcher";
@ -42,7 +49,9 @@ import PosthogTrackers from "../../PosthogTrackers";
import { type ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload";
import { SDKContext } from "../../contexts/SDKContext";
import { shouldShowFeedback } from "../../utils/Feedback";
import DarkLightModeSvg from "../../../res/img/element-icons/roomlist/dark-light-mode.svg";
import { Icon as DarkLightModeSvg } from "../../../res/img/element-icons/roomlist/dark-light-mode.svg";
import { Icon as NotificationsIcon } from "../../../res/img/element-icons/notifications.svg";
import { Icon as FeedbackIcon } from "../../../res/img/element-icons/feedback.svg";
interface IProps {
isPanelCollapsed: boolean;
@ -297,7 +306,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
if (this.hasHomePage) {
homeButton = (
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconHome"
icon={<HomeSolidIcon />}
label={_t("common|home")}
onClick={this.onHomeClick}
/>
@ -308,7 +317,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
if (shouldShowFeedback()) {
feedbackButton = (
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconMessage"
icon={<FeedbackIcon />}
label={_t("common|feedback")}
onClick={this.onProvideFeedback}
/>
@ -317,7 +326,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
const linkNewDeviceButton = (
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconQr"
icon={<QrCodeIcon />}
label={_t("user_menu|link_new_device")}
onClick={(e) => this.onSettingsOpen(e, UserTab.SessionManager, { showMsc4108QrCode: true })}
/>
@ -328,24 +337,24 @@ export default class UserMenu extends React.Component<IProps, IState> {
{homeButton}
{linkNewDeviceButton}
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconBell"
icon={<NotificationsIcon />}
label={_t("notifications|enable_prompt_toast_title")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Notifications)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
icon={<LockSolidIcon />}
label={_t("room_settings|security|title")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Security)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings"
icon={<SettingsSolidIcon />}
label={_t("user_menu|settings")}
onClick={(e) => this.onSettingsOpen(e)}
/>
{feedbackButton}
<IconizedContextMenuOption
className="mx_IconizedContextMenu_option_red"
iconClassName="mx_UserMenu_iconSignOut"
icon={<LeaveIcon />}
label={_t("action|sign_out")}
onClick={this.onSignOutClick}
/>
@ -357,7 +366,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
<IconizedContextMenuOptionList>
{homeButton}
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings"
icon={<SettingsSolidIcon />}
label={_t("common|settings")}
onClick={(e) => this.onSettingsOpen(e)}
/>
@ -398,7 +407,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
: _t("user_menu|switch_theme_dark")
}
>
<img src={DarkLightModeSvg} role="presentation" alt="" width={16} />
<DarkLightModeSvg width="16px" height="16px" />
</RovingAccessibleButton>
</div>
{topSection}

View File

@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { LabsIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import Modal from "../../../Modal";
import DevtoolsDialog from "../dialogs/DevtoolsDialog";
@ -32,7 +33,7 @@ export const DeveloperToolsOption: React.FC<Props> = ({ onFinished, roomId }) =>
onFinished();
}}
label={_t("devtools|title")}
iconClassName="mx_IconizedContextMenu_developerTools"
icon={<LabsIcon />}
/>
);
};

View File

@ -26,14 +26,7 @@ interface IDeviceContextMenuDeviceProps {
}
const DeviceContextMenuDevice: React.FC<IDeviceContextMenuDeviceProps> = ({ label, selected, onClick }) => {
return (
<IconizedContextMenuRadio
iconClassName="mx_DeviceContextMenu_device_icon"
label={label}
active={selected}
onClick={onClick}
/>
);
return <IconizedContextMenuRadio label={label} active={selected} onClick={onClick} />;
};
interface IDeviceContextMenuSectionProps {

View File

@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, type ReactNode } from "react";
import classNames from "classnames";
import { CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import ContextMenu, {
ChevronFace,
@ -33,26 +34,19 @@ interface IOptionListProps {
interface IOptionProps extends React.ComponentProps<typeof MenuItem> {
icon?: ReactNode;
iconClassName?: string;
isDestructive?: boolean;
}
interface ICheckboxProps extends React.ComponentProps<typeof MenuItemCheckbox> {
iconClassName: string;
icon?: ReactNode;
words?: boolean;
}
interface IRadioProps extends React.ComponentProps<typeof MenuItemRadio> {
iconClassName?: string;
icon?: ReactNode;
}
export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({
label,
iconClassName,
active,
className,
...props
}) => {
export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({ label, icon, active, className, ...props }) => {
return (
<MenuItemRadio
{...props}
@ -63,35 +57,28 @@ export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({
active={active}
label={label}
>
{iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />}
{icon}
<span className="mx_IconizedContextMenu_label">{label}</span>
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
{active && <CheckIcon className="mx_IconizedContextMenu_checked" />}
</MenuItemRadio>
);
};
export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
label,
iconClassName,
icon,
active,
className,
words,
...props
}) => {
let marker: JSX.Element;
let marker: JSX.Element | undefined;
if (words) {
marker = (
<span className="mx_IconizedContextMenu_activeText">{active ? _t("common|on") : _t("common|off")}</span>
);
} else {
marker = (
<span
className={classNames("mx_IconizedContextMenu_icon", {
mx_IconizedContextMenu_checked: active,
mx_IconizedContextMenu_unchecked: !active,
})}
/>
);
} else if (active) {
marker = <CheckIcon className="mx_IconizedContextMenu_checked" />;
}
return (
@ -104,7 +91,7 @@ export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
active={active}
label={label}
>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
{icon}
<span className="mx_IconizedContextMenu_label">{label}</span>
{marker}
</MenuItemCheckbox>
@ -114,7 +101,6 @@ export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
export const IconizedContextMenuOption: React.FC<IOptionProps> = ({
label,
className,
iconClassName,
icon,
children,
isDestructive,
@ -130,7 +116,6 @@ export const IconizedContextMenuOption: React.FC<IOptionProps> = ({
})}
label={label}
>
{iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />}
{icon}
<span className="mx_IconizedContextMenu_label">{label}</span>
{children}

View File

@ -20,6 +20,27 @@ import {
Thread,
M_POLL_START,
} from "matrix-js-sdk/src/matrix";
import {
CheckIcon,
ChevronUpIcon,
EditIcon,
ErrorSolidIcon,
InlineCodeIcon,
LinkIcon,
PinIcon,
QuoteIcon,
ReactionAddIcon,
ReplyIcon,
RestartIcon,
ThreadsIcon,
UnpinIcon,
DeleteIcon,
ForwardIcon,
PopOutIcon,
VisibilityOnIcon,
ShareIcon,
CopyIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher";
@ -53,6 +74,8 @@ import { type ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadP
import { CardContext } from "../right_panel/context";
import PinningUtils from "../../../utils/PinningUtils";
import PosthogTrackers from "../../../PosthogTrackers.ts";
import { Icon as ViewInRoomIcon } from "../../../../res/img/element-icons/view-in-room.svg";
import { Icon as ChildRelationshipIcon } from "../../../../res/img/element-icons/child-relationship.svg";
interface IReplyInThreadButton {
mxEvent: MatrixEvent;
@ -86,13 +109,7 @@ const ReplyInThreadButton: React.FC<IReplyInThreadButton> = ({ mxEvent, closeMen
closeMenu();
};
return (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconReplyInThread"
label={_t("action|reply_in_thread")}
onClick={onClick}
/>
);
return <IconizedContextMenuOption icon={<ThreadsIcon />} label={_t("action|reply_in_thread")} onClick={onClick} />;
};
interface IProps extends MenuProps {
@ -413,7 +430,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (!mxEvent.isRedacted() && unsentReactionsCount !== 0) {
resendReactionsButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconResend"
icon={<RestartIcon />}
label={_t("timeline|context_menu|resent_unsent_reactions", { unsentCount: unsentReactionsCount })}
onClick={this.onResendReactionsClick}
/>
@ -424,7 +441,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (isSent && this.state.canRedact) {
redactButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconRedact"
icon={<DeleteIcon />}
label={_t("action|remove")}
onClick={this.onRedactClick}
/>
@ -437,7 +454,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
const mapSiteLink = createMapSiteLinkFromEvent(shareableLocationEvent);
openInMapSiteButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconOpenInMapSite"
icon={<PopOutIcon />}
onClick={null}
label={_t("timeline|context_menu|open_in_osm")}
element="a"
@ -455,7 +472,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (contentActionable && forwardableEvent) {
forwardButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconForward"
icon={<ForwardIcon />}
label={_t("action|forward")}
onClick={this.onForwardClick(forwardableEvent)}
/>
@ -465,7 +482,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
// This is specifically not behind the developerMode flag to give people insight into the Matrix
const viewSourceButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconSource"
icon={<InlineCodeIcon />}
label={_t("timeline|context_menu|view_source")}
onClick={this.onViewSourceClick}
/>
@ -475,7 +492,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (eventTileOps?.isWidgetHidden()) {
unhidePreviewButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconUnhidePreview"
icon={<VisibilityOnIcon />}
label={_t("timeline|context_menu|show_url_preview")}
onClick={this.onUnhidePreviewClick}
/>
@ -486,7 +503,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (permalink) {
permalinkButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconPermalink"
icon={<ShareIcon />}
onClick={this.onShareClick}
label={_t("action|share")}
element="a"
@ -506,7 +523,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (this.canEndPoll(mxEvent)) {
endPollButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconEndPoll"
icon={<CheckIcon />}
label={_t("poll|end_title")}
onClick={this.onEndPollClick}
/>
@ -521,7 +538,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
) {
externalURLButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconLink"
icon={<LinkIcon />}
onClick={this.closeMenu}
label={_t("timeline|context_menu|external_url")}
element="a"
@ -541,7 +558,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (collapseReplyChain) {
collapseReplyChainButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconCollapse"
icon={<ChevronUpIcon />}
label={_t("timeline|context_menu|collapse_reply_thread")}
onClick={this.onCollapseReplyChainClick}
/>
@ -553,7 +570,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (relatedEventId && SettingsStore.getValue("developerMode")) {
jumpToRelatedEventButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_jumpToEvent"
icon={<ChildRelationshipIcon />}
label={_t("timeline|context_menu|view_related_event")}
onClick={() => this.onJumpToRelatedEventClick(relatedEventId)}
/>
@ -564,7 +581,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (mxEvent.getSender() !== me) {
reportEventButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconReport"
icon={<ErrorSolidIcon />}
label={_t("timeline|context_menu|report")}
onClick={this.onReportEventClick}
/>
@ -575,7 +592,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (link) {
copyLinkButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconCopy"
icon={<CopyIcon />}
onClick={this.onCopyLinkClick}
label={_t("action|copy_link")}
element="a"
@ -597,7 +614,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (rightClick && selectedText) {
copyButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconCopy"
icon={<CopyIcon />}
label={_t("action|copy")}
triggerOnMouseDown={true} // We use onMouseDown so that the selection isn't cleared when we click
onClick={this.onCopyClick}
@ -609,7 +626,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (rightClick && selectedText && selectedText.trim().length > 0 && this.isSelectionWithinSingleTextBody()) {
quoteButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconQuote"
icon={<QuoteIcon />}
label={_t("action|quote")}
triggerOnMouseDown={true}
onClick={this.onQuoteClick}
@ -620,11 +637,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
let editButton: JSX.Element | undefined;
if (rightClick && canEditContent(cli, mxEvent)) {
editButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconEdit"
label={_t("action|edit")}
onClick={this.onEditClick}
/>
<IconizedContextMenuOption icon={<EditIcon />} label={_t("action|edit")} onClick={this.onEditClick} />
);
}
@ -632,7 +645,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (rightClick && contentActionable && canSendMessages) {
replyButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconReply"
icon={<ReplyIcon />}
label={_t("action|reply")}
onClick={this.onReplyClick}
/>
@ -654,7 +667,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (rightClick && contentActionable && canReact) {
reactButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconReact"
icon={<ReactionAddIcon />}
label={_t("action|react")}
onClick={this.onReactClick}
inputRef={this.reactButtonRef}
@ -667,7 +680,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
const isPinned = PinningUtils.isPinned(MatrixClientPeg.safeGet(), this.props.mxEvent);
pinButton = (
<IconizedContextMenuOption
iconClassName={isPinned ? "mx_MessageContextMenu_iconUnpin" : "mx_MessageContextMenu_iconPin"}
icon={isPinned ? <UnpinIcon /> : <PinIcon />}
label={isPinned ? _t("action|unpin") : _t("action|pin")}
onClick={() => this.onPinClick(isPinned)}
/>
@ -678,7 +691,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
if (isThreadRootEvent) {
viewInRoomButton = (
<IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconViewInRoom"
icon={<ViewInRoomIcon />}
label={_t("timeline|mab|view_in_room")}
onClick={this.viewInRoom}
/>

View File

@ -9,6 +9,15 @@ Please see LICENSE files in the repository root for full details.
import { logger } from "matrix-js-sdk/src/logger";
import { type Room } from "matrix-js-sdk/src/matrix";
import React, { type JSX, useContext } from "react";
import {
FavouriteSolidIcon,
LinkIcon,
SettingsSolidIcon,
ArrowDownIcon,
MarkAsReadIcon,
MarkAsUnreadIcon,
LeaveIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import RoomListActions from "../../../actions/RoomListActions";
@ -34,6 +43,7 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent
import { UIComponent } from "../../../settings/UIFeature";
import { DeveloperToolsOption } from "./DeveloperToolsOption";
import { useSettingValue } from "../../../hooks/useSettings";
import { Icon as InviteIcon } from "../../../../res/img/element-icons/room/invite.svg";
export interface RoomGeneralContextMenuProps extends IContextMenuProps {
room: Room;
@ -153,7 +163,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.Favourite), onPostFavoriteClick, true)}
active={isFavorite}
label={isFavorite ? _t("room|context_menu|unfavourite") : _t("room|context_menu|favourite")}
iconClassName="mx_RoomGeneralContextMenu_iconStar"
icon={<FavouriteSolidIcon />}
/>
);
@ -163,7 +173,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.LowPriority), onPostLowPriorityClick, true)}
active={isLowPriority}
label={_t("room|context_menu|low_priority")}
iconClassName="mx_RoomGeneralContextMenu_iconArrowDown"
icon={<ArrowDownIcon />}
/>
);
@ -180,7 +190,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onPostInviteClick,
)}
label={_t("action|invite")}
iconClassName="mx_RoomGeneralContextMenu_iconInvite"
icon={<InviteIcon />}
/>
);
}
@ -198,7 +208,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onPostCopyLinkClick,
)}
label={_t("room|context_menu|copy_link")}
iconClassName="mx_RoomGeneralContextMenu_iconCopyLink"
icon={<LinkIcon />}
/>
);
}
@ -214,7 +224,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onPostSettingsClick,
)}
label={_t("common|settings")}
iconClassName="mx_RoomGeneralContextMenu_iconSettings"
icon={<SettingsSolidIcon />}
/>
);
@ -222,7 +232,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
if (roomTags.includes(DefaultTagID.Archived)) {
leaveOption = (
<IconizedContextMenuOption
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
icon={<LeaveIcon />}
label={_t("room|context_menu|forget")}
className="mx_IconizedContextMenu_option_red"
onClick={wrapHandler(
@ -248,7 +258,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
)}
label={_t("action|leave")}
className="mx_IconizedContextMenu_option_red"
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
icon={<LeaveIcon />}
/>
);
}
@ -263,7 +273,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onFinished?.();
}, onPostMarkAsReadClick)}
label={_t("room|context_menu|mark_read")}
iconClassName="mx_RoomGeneralContextMenu_iconMarkAsRead"
icon={<MarkAsReadIcon />}
/>
);
} else if (!roomTags.includes(DefaultTagID.Archived)) {
@ -274,7 +284,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onFinished?.();
}, onPostMarkAsUnreadClick)}
label={_t("room|context_menu|mark_unread")}
iconClassName="mx_RoomGeneralContextMenu_iconMarkAsUnread"
icon={<MarkAsUnreadIcon />}
/>
);
} else {

View File

@ -20,6 +20,10 @@ import IconizedContextMenu, {
IconizedContextMenuRadio,
} from "../context_menus/IconizedContextMenu";
import { type ButtonEvent } from "../elements/AccessibleButton";
import { Icon as NotificationsIcon } from "../../../../res/img/element-icons/notifications.svg";
import { Icon as NotificationsDefaultIcon } from "../../../../res/img/element-icons/roomlist/notifications-default.svg";
import { Icon as NotificationsDmIcon } from "../../../../res/img/element-icons/roomlist/notifications-dm.svg";
import { Icon as NotificationsOffIcon } from "../../../../res/img/element-icons/roomlist/notifications-off.svg";
interface IProps extends IContextMenuProps {
room: Room;
@ -46,7 +50,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
<IconizedContextMenuRadio
label={_t("room|context_menu|notifications_default")}
active={notificationState === RoomNotifState.AllMessages}
iconClassName="mx_RoomNotificationContextMenu_iconBell"
icon={<NotificationsIcon />}
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessages))}
/>
);
@ -55,7 +59,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
<IconizedContextMenuRadio
label={_t("notifications|all_messages")}
active={notificationState === RoomNotifState.AllMessagesLoud}
iconClassName="mx_RoomNotificationContextMenu_iconBellDot"
icon={<NotificationsDefaultIcon />}
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessagesLoud))}
/>
);
@ -64,7 +68,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
<IconizedContextMenuRadio
label={_t("notifications|mentions_keywords")}
active={notificationState === RoomNotifState.MentionsOnly}
iconClassName="mx_RoomNotificationContextMenu_iconBellMentions"
icon={<NotificationsDmIcon />}
onClick={wrapHandler(() => setNotificationState(RoomNotifState.MentionsOnly))}
/>
);
@ -73,7 +77,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
<IconizedContextMenuRadio
label={_t("room|context_menu|notifications_mute")}
active={notificationState === RoomNotifState.Mute}
iconClassName="mx_RoomNotificationContextMenu_iconBellCrossed"
icon={<NotificationsOffIcon />}
onClick={wrapHandler(() => setNotificationState(RoomNotifState.Mute))}
/>
);

View File

@ -8,6 +8,14 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, useContext } from "react";
import { type Room, EventType, RoomType } from "matrix-js-sdk/src/matrix";
import {
HomeSolidIcon,
PlusIcon,
SettingsSolidIcon,
LeaveIcon,
SearchIcon,
PreferencesIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { type IProps as IContextMenuProps } from "../../structures/ContextMenu";
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "./IconizedContextMenu";
@ -32,6 +40,7 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent
import { UIComponent } from "../../../settings/UIFeature";
import PosthogTrackers from "../../../PosthogTrackers";
import { type ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { Icon as InviteIcon } from "../../../../res/img/element-icons/room/invite.svg";
interface IProps extends IContextMenuProps {
space?: Room;
@ -60,7 +69,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
<IconizedContextMenuOption
data-testid="invite-option"
className="mx_SpacePanel_contextMenu_inviteButton"
iconClassName="mx_SpacePanel_iconInvite"
icon={<InviteIcon />}
label={_t("action|invite")}
onClick={onInviteClick}
/>
@ -81,7 +90,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
settingsOption = (
<IconizedContextMenuOption
data-testid="settings-option"
iconClassName="mx_SpacePanel_iconSettings"
icon={<SettingsSolidIcon />}
label={_t("common|settings")}
onClick={onSettingsClick}
/>
@ -98,7 +107,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
leaveOption = (
<IconizedContextMenuOption
data-testid="leave-option"
iconClassName="mx_SpacePanel_iconLeave"
icon={<LeaveIcon />}
className="mx_IconizedContextMenu_option_red"
label={_t("space|leave_dialog_action")}
onClick={onLeaveClick}
@ -123,7 +132,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
devtoolsOption = (
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconSettings"
icon={<SettingsSolidIcon />}
label={_t("space|context_menu|devtools_open_timeline")}
onClick={onViewTimelineClick}
/>
@ -170,7 +179,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
{canAddRooms && (
<IconizedContextMenuOption
data-testid="new-room-option"
iconClassName="mx_SpacePanel_iconPlus"
icon={<PlusIcon />}
label={_t("common|room")}
onClick={onNewRoomClick}
/>
@ -178,7 +187,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
{canAddVideoRooms && (
<IconizedContextMenuOption
data-testid="new-video-room-option"
iconClassName="mx_SpacePanel_iconPlus"
icon={<PlusIcon />}
label={_t("common|video_room")}
onClick={onNewVideoRoomClick}
>
@ -188,7 +197,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
{canAddSubSpaces && (
<IconizedContextMenuOption
data-testid="new-subspace-option"
iconClassName="mx_SpacePanel_iconPlus"
icon={<PlusIcon />}
label={_t("common|space")}
onClick={onNewSubspaceClick}
>
@ -234,18 +243,18 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
{!hideHeader && <div className="mx_SpacePanel_contextMenu_header">{space.name}</div>}
<IconizedContextMenuOptionList first>
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconHome"
icon={<HomeSolidIcon />}
label={_t("space|context_menu|home")}
onClick={onHomeClick}
/>
{inviteOption}
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconExplore"
icon={<SearchIcon />}
label={canAddRooms ? _t("space|context_menu|manage_and_explore") : _t("space|context_menu|explore")}
onClick={onExploreRoomsClick}
/>
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconPreferences"
icon={<PreferencesIcon />}
label={_t("common|preferences")}
onClick={onPreferencesClick}
/>

View File

@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React, { useCallback, useEffect } from "react";
import { type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { LinkIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { type ButtonEvent } from "../elements/AccessibleButton";
import dis from "../../../dispatcher/dispatcher";
@ -20,6 +21,7 @@ import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOpti
import { WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { type ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { Icon as ViewInRoomIcon } from "../../../../res/img/element-icons/view-in-room.svg";
export interface ThreadListContextMenuProps {
mxEvent: MatrixEvent;
@ -102,7 +104,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
<IconizedContextMenuOption
onClick={(e) => viewInRoom(e)}
label={_t("timeline|mab|view_in_room")}
iconClassName="mx_ThreadPanel_viewInRoom"
icon={<ViewInRoomIcon />}
/>
)}
{permalinkCreator && (
@ -110,7 +112,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
data-testid="copy-thread-link"
onClick={(e) => copyLinkToThread(e)}
label={_t("timeline|mab|copy_link_thread")}
iconClassName="mx_ThreadPanel_copyLinkToThread"
icon={<LinkIcon />}
/>
)}
</IconizedContextMenuOptionList>

View File

@ -6,9 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import classNames from "classnames";
import { type Room } from "matrix-js-sdk/src/matrix";
import React, { type JSX, Fragment, useState } from "react";
import React, { type JSX, Fragment, useState, type ReactNode } from "react";
import { OverflowHorizontalIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { ContextMenuTooltipButton } from "../../../../accessibility/context_menu/ContextMenuTooltipButton";
import { useNotificationState } from "../../../../hooks/useRoomNotificationState";
@ -21,11 +21,25 @@ import { type ButtonEvent } from "../../elements/AccessibleButton";
import { contextMenuBelow } from "../../rooms/RoomTile";
import { shouldShowComponent } from "../../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../../settings/UIFeature";
import { Icon as NotificationsIcon } from "../../../../../res/img/element-icons/notifications.svg";
import { Icon as NotificationsDefaultIcon } from "../../../../../res/img/element-icons/roomlist/notifications-default.svg";
import { Icon as NotificationsDmIcon } from "../../../../../res/img/element-icons/roomlist/notifications-dm.svg";
import { Icon as NotificationsOffIcon } from "../../../../../res/img/element-icons/roomlist/notifications-off.svg";
interface Props {
room: Room;
}
export function getNotificationIcon(state: RoomNotifState): ReactNode {
const icons: Record<RoomNotifState, JSX.Element> = {
[RoomNotifState.AllMessages]: <NotificationsIcon />,
[RoomNotifState.AllMessagesLoud]: <NotificationsDefaultIcon />,
[RoomNotifState.MentionsOnly]: <NotificationsDmIcon />,
[RoomNotifState.Mute]: <NotificationsOffIcon />,
};
return icons[state];
}
export function RoomResultContextMenus({ room }: Props): JSX.Element {
const [notificationState] = useNotificationState(room);
@ -64,14 +78,6 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
);
}
const notificationMenuClasses = classNames("mx_SpotlightDialog_option--notifications", {
// Show bell icon for the default case too.
mx_RoomNotificationContextMenu_iconBell: notificationState === RoomNotifState.AllMessages,
mx_RoomNotificationContextMenu_iconBellDot: notificationState === RoomNotifState.AllMessagesLoud,
mx_RoomNotificationContextMenu_iconBellMentions: notificationState === RoomNotifState.MentionsOnly,
mx_RoomNotificationContextMenu_iconBellCrossed: notificationState === RoomNotifState.Mute,
});
return (
<Fragment>
{shouldShowComponent(UIComponent.RoomOptionsMenu) && (
@ -86,11 +92,13 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
}}
title={room.isSpaceRoom() ? _t("space|context_menu|options") : _t("room|context_menu|title")}
isExpanded={generalMenuPosition !== null}
/>
>
<OverflowHorizontalIcon />
</ContextMenuTooltipButton>
)}
{!room.isSpaceRoom() && (
<ContextMenuTooltipButton
className={notificationMenuClasses}
className="mx_SpotlightDialog_option--notifications"
onClick={(ev: ButtonEvent) => {
ev.preventDefault();
ev.stopPropagation();
@ -100,7 +108,9 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
}}
title={_t("room_list|notification_options")}
isExpanded={notificationMenuPosition !== null}
/>
>
{getNotificationIcon(notificationState!)}
</ContextMenuTooltipButton>
)}
{generalMenu}
{notificationMenu}

View File

@ -8,6 +8,13 @@ Please see LICENSE files in the repository root for full details.
import { EventType, type Room, RoomType } from "matrix-js-sdk/src/matrix";
import React, { type JSX, type ComponentType, createRef, type ReactComponentElement, type SyntheticEvent } from "react";
import {
PlusIcon,
UserAddSolidIcon,
RoomIcon,
SearchIcon,
ShareIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { type IState as IRovingTabIndexState, RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex.tsx";
import MatrixClientContext from "../../../contexts/MatrixClientContext.tsx";
@ -67,6 +74,7 @@ import { getKeyBindingsManager } from "../../../KeyBindingsManager.ts";
import AccessibleButton from "../elements/AccessibleButton.tsx";
import { Landmark, LandmarkNavigation } from "../../../accessibility/LandmarkNavigation.ts";
import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../../LegacyCallHandler.tsx";
import { Icon as HashVideoIcon } from "../../../../res/img/element-icons/roomlist/hash-video.svg";
interface IProps {
onKeyDown: (ev: React.KeyboardEvent, state: IRovingTabIndexState) => void;
@ -142,7 +150,7 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
{showCreateRooms && (
<IconizedContextMenuOption
label={_t("action|start_new_chat")}
iconClassName="mx_LegacyRoomList_iconStartChat"
icon={<UserAddSolidIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -158,7 +166,7 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
{showInviteUsers && (
<IconizedContextMenuOption
label={_t("action|invite_to_space")}
iconClassName="mx_LegacyRoomList_iconInvite"
icon={<ShareIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -230,7 +238,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
<IconizedContextMenuOptionList first>
<IconizedContextMenuOption
label={_t("action|explore_rooms")}
iconClassName="mx_LegacyRoomList_iconExplore"
icon={<SearchIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -247,7 +255,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
<>
<IconizedContextMenuOption
label={_t("action|new_room")}
iconClassName="mx_LegacyRoomList_iconNewRoom"
icon={<PlusIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -261,7 +269,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
{videoRoomsEnabled && (
<IconizedContextMenuOption
label={_t("action|new_video_room")}
iconClassName="mx_LegacyRoomList_iconNewVideoRoom"
icon={<HashVideoIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -279,7 +287,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
)}
<IconizedContextMenuOption
label={_t("action|add_existing_room")}
iconClassName="mx_LegacyRoomList_iconAddExistingRoom"
icon={<RoomIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -300,7 +308,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
<>
<IconizedContextMenuOption
label={_t("action|new_room")}
iconClassName="mx_LegacyRoomList_iconNewRoom"
icon={<PlusIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -312,7 +320,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
{videoRoomsEnabled && (
<IconizedContextMenuOption
label={_t("action|new_video_room")}
iconClassName="mx_LegacyRoomList_iconNewVideoRoom"
icon={<HashVideoIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -333,7 +341,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
{showExploreRooms ? (
<IconizedContextMenuOption
label={_t("action|explore_public_rooms")}
iconClassName="mx_LegacyRoomList_iconExplore"
icon={<SearchIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();

View File

@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
import { ClientEvent, EventType, type Room, RoomEvent, RoomType } from "matrix-js-sdk/src/matrix";
import React, { type JSX, useContext, useEffect, useState } from "react";
import { Tooltip } from "@vector-im/compound-web";
import { PlusIcon, UserAddSolidIcon, SearchIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
@ -51,6 +52,8 @@ import IconizedContextMenu, {
import SpaceContextMenu from "../context_menus/SpaceContextMenu";
import InlineSpinner from "../elements/InlineSpinner";
import { HomeButtonContextMenu } from "../spaces/SpacePanel";
import { Icon as InviteIcon } from "../../../../res/img/element-icons/room/invite.svg";
import { Icon as HashVideoIcon } from "../../../../res/img/element-icons/roomlist/hash-video.svg";
const contextMenuBelow = (elementRect: DOMRect): MenuProps => {
// align the context menu's icons with the icon which opened the context menu
@ -178,7 +181,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
inviteOption = (
<IconizedContextMenuOption
label={_t("action|invite")}
iconClassName="mx_LegacyRoomListHeader_iconInvite"
icon={<InviteIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -194,7 +197,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
newRoomOptions = (
<>
<IconizedContextMenuOption
iconClassName="mx_LegacyRoomListHeader_iconNewRoom"
icon={<PlusIcon />}
label={_t("action|new_room")}
onClick={(e) => {
e.preventDefault();
@ -206,7 +209,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
/>
{videoRoomsEnabled && (
<IconizedContextMenuOption
iconClassName="mx_LegacyRoomListHeader_iconNewVideoRoom"
icon={<HashVideoIcon />}
label={_t("action|new_video_room")}
onClick={(e) => {
e.preventDefault();
@ -236,7 +239,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
{newRoomOptions}
<IconizedContextMenuOption
label={_t("action|explore_rooms")}
iconClassName="mx_LegacyRoomListHeader_iconExplore"
icon={<SearchIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -251,7 +254,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
/>
<IconizedContextMenuOption
label={_t("action|add_existing_room")}
iconClassName="mx_LegacyRoomListHeader_iconPlus"
icon={<PlusIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -264,7 +267,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
{canCreateSpaces && (
<IconizedContextMenuOption
label={_t("room_list|add_space_label")}
iconClassName="mx_LegacyRoomListHeader_iconPlus"
icon={<PlusIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -289,7 +292,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
<>
<IconizedContextMenuOption
label={_t("action|start_new_chat")}
iconClassName="mx_LegacyRoomListHeader_iconStartChat"
icon={<UserAddSolidIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -300,7 +303,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
/>
<IconizedContextMenuOption
label={_t("action|new_room")}
iconClassName="mx_LegacyRoomListHeader_iconNewRoom"
icon={<PlusIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -312,7 +315,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
{videoRoomsEnabled && (
<IconizedContextMenuOption
label={_t("action|new_video_room")}
iconClassName="mx_LegacyRoomListHeader_iconNewVideoRoom"
icon={<HashVideoIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -333,7 +336,7 @@ const LegacyRoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
joinRoomOpt = (
<IconizedContextMenuOption
label={_t("room_list|join_public_room_label")}
iconClassName="mx_LegacyRoomListHeader_iconExplore"
icon={<SearchIcon />}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();

View File

@ -45,6 +45,7 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent
import { UIComponent } from "../../../settings/UIFeature";
import { isKnockDenied } from "../../../utils/membership";
import SettingsStore from "../../../settings/SettingsStore";
import { getNotificationIcon } from "../dialogs/spotlight/RoomResultContextMenus.tsx";
interface Props {
room: Room;
@ -293,12 +294,6 @@ class RoomTile extends React.PureComponent<Props, State> {
const state = this.roomProps.notificationVolume;
const classes = classNames("mx_RoomTile_notificationsButton", {
// Show bell icon for the default case too.
mx_RoomNotificationContextMenu_iconBell: state === RoomNotifState.AllMessages,
mx_RoomNotificationContextMenu_iconBellDot: state === RoomNotifState.AllMessagesLoud,
mx_RoomNotificationContextMenu_iconBellMentions: state === RoomNotifState.MentionsOnly,
mx_RoomNotificationContextMenu_iconBellCrossed: state === RoomNotifState.Mute,
// Only show the icon by default if the room is overridden to muted.
// TODO: [FTUE Notifications] Probably need to detect global mute state
mx_RoomTile_notificationsButton_show: state === RoomNotifState.Mute,
@ -312,7 +307,9 @@ class RoomTile extends React.PureComponent<Props, State> {
title={_t("room_list|notification_options")}
isExpanded={!!this.state.notificationsMenuPosition}
tabIndex={isActive ? 0 : -1}
/>
>
{getNotificationIcon(state!)}
</ContextMenuTooltipButton>
{this.state.notificationsMenuPosition && (
<RoomNotificationContextMenu
{...contextMenuBelow(this.state.notificationsMenuPosition)}

View File

@ -99,7 +99,6 @@ export const HomeButtonContextMenu: React.FC<ComponentProps<typeof SpaceContextM
{!hideHeader && <div className="mx_SpacePanel_contextMenu_header">{_t("common|home")}</div>}
<IconizedContextMenuOptionList first>
<IconizedContextMenuCheckbox
iconClassName="mx_SpacePanel_noIcon"
label={_t("settings|sidebar|metaspaces_home_all_rooms")}
active={allRoomsInHome}
onClick={() => {

View File

@ -0,0 +1,39 @@
/*
Copyright 2025 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { render } from "jest-matrix-react";
import React from "react";
import { clearAllModals } from "../../../../test-utils";
import DeviceContextMenu from "../../../../../src/components/views/context_menus/DeviceContextMenu.tsx";
import MediaDeviceHandler from "../../../../../src/MediaDeviceHandler.ts";
describe("DeviceContextMenu", () => {
afterEach(async () => {
await clearAllModals();
});
it("renders a menu with the selected device checked", async () => {
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
videoinput: [
{ deviceId: "A", label: "Camera 1" } as MediaDeviceInfo,
{ deviceId: "B", label: "Camera 2" } as MediaDeviceInfo,
{ deviceId: "C", label: "Camera 3" } as MediaDeviceInfo,
],
audioinput: [],
audiooutput: [],
});
jest.spyOn(MediaDeviceHandler, "getDevice").mockReturnValue("B");
const { container, findByLabelText } = render(
<DeviceContextMenu deviceKinds={["videoinput"]} onFinished={jest.fn()} mountAsChild />,
);
await expect(findByLabelText("Camera 2")).resolves.toBeChecked();
expect(container).toMatchSnapshot();
});
});

View File

@ -0,0 +1,85 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`DeviceContextMenu renders a menu with the selected device checked 1`] = `
<div>
<div
class="mx_ContextualMenu_wrapper"
>
<div
class="mx_ContextualMenu_background"
/>
<div
class="mx_ContextualMenu"
role="menu"
>
<ul
class="mx_IconizedContextMenu mx_DeviceContextMenu mx_IconizedContextMenu_compact"
role="none"
>
<div
class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst"
>
<div>
<span
class="mx_IconizedContextMenu_optionList_label"
>
Cameras
</span>
</div>
<div
aria-checked="false"
aria-label="Camera 1"
class="mx_AccessibleButton mx_IconizedContextMenu_item"
role="menuitemradio"
tabindex="0"
>
<span
class="mx_IconizedContextMenu_label"
>
Camera 1
</span>
</div>
<div
aria-checked="true"
aria-label="Camera 2"
class="mx_AccessibleButton mx_IconizedContextMenu_item mx_IconizedContextMenu_active"
role="menuitemradio"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_label"
>
Camera 2
</span>
<svg
class="mx_IconizedContextMenu_checked"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
</div>
<div
aria-checked="false"
aria-label="Camera 3"
class="mx_AccessibleButton mx_IconizedContextMenu_item"
role="menuitemradio"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_label"
>
Camera 3
</span>
</div>
</div>
</ul>
</div>
</div>
</div>
`;

View File

@ -32,9 +32,20 @@ exports[`RoomGeneralContextMenu renders an empty context menu for archived rooms
role="menuitem"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_icon mx_RoomGeneralContextMenu_iconSignOut"
/>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
/>
<path
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
/>
</svg>
<span
class="mx_IconizedContextMenu_label"
>
@ -80,9 +91,20 @@ exports[`RoomGeneralContextMenu renders the default context menu 1`] = `
role="menuitem"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_icon mx_RoomGeneralContextMenu_iconSignOut"
/>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
/>
<path
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
/>
</svg>
<span
class="mx_IconizedContextMenu_label"
>

View File

@ -34,9 +34,17 @@ exports[`<SpaceContextMenu /> renders menu correctly 1`] = `
role="menuitem"
tabindex="0"
>
<span
class="mx_IconizedContextMenu_icon mx_SpacePanel_iconHome"
/>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12.971 3.54 7 3.889A2 2 0 0 1 21 9.177V19a2 2 0 0 1-2 2h-4v-9H9v9H5a2 2 0 0 1-2-2V9.177a2 2 0 0 1 1.029-1.748l7-3.89a2 2 0 0 1 1.942 0"
/>
</svg>
<span
class="mx_IconizedContextMenu_label"
>
@ -49,9 +57,17 @@ exports[`<SpaceContextMenu /> renders menu correctly 1`] = `
role="menuitem"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_icon mx_SpacePanel_iconExplore"
/>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414zM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0"
/>
</svg>
<span
class="mx_IconizedContextMenu_label"
>
@ -64,9 +80,19 @@ exports[`<SpaceContextMenu /> renders menu correctly 1`] = `
role="menuitem"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_icon mx_SpacePanel_iconPreferences"
/>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M6.5 2h11a4.5 4.5 0 1 1 0 9h-11a4.5 4.5 0 0 1 0-9m0 2h7.258A4.5 4.5 0 0 0 13 6.5c0 .925.28 1.785.758 2.5H6.5a2.5 2.5 0 0 1 0-5M15 6.5a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0m-13 11A4.5 4.5 0 0 1 6.5 13h11a4.5 4.5 0 1 1 0 9h-11q-.233 0-.46-.023A4.5 4.5 0 0 1 2 17.5m8.242-2.5H17.5a2.5 2.5 0 0 1 0 5h-7.258A4.5 4.5 0 0 0 11 17.5c0-.925-.28-1.785-.758-2.5M6.5 15a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5"
fill-rule="evenodd"
/>
</svg>
<span
class="mx_IconizedContextMenu_label"
>
@ -80,9 +106,20 @@ exports[`<SpaceContextMenu /> renders menu correctly 1`] = `
role="menuitem"
tabindex="-1"
>
<span
class="mx_IconizedContextMenu_icon mx_SpacePanel_iconLeave"
/>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
/>
<path
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
/>
</svg>
<span
class="mx_IconizedContextMenu_label"
>