diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index f9cd5ba3161..e74d66274f4 100644 --- a/cypress/e2e/crypto/crypto.spec.ts +++ b/cypress/e2e/crypto/crypto.spec.ts @@ -116,7 +116,7 @@ const verify = function (this: CryptoTestContext) { const bobsVerificationRequestPromise = waitForVerificationRequest(this.bob); openRoomInfo().within(() => { - cy.findByRole("button", { name: /People \d/ }).click(); // \d is the number of the room members + cy.findByRole("menuitem", { name: "People" }).click(); cy.findByText("Bob").click(); cy.findByRole("button", { name: "Verify" }).click(); cy.findByRole("button", { name: "Start Verification" }).click(); diff --git a/cypress/e2e/invite/invite-dialog.spec.ts b/cypress/e2e/invite/invite-dialog.spec.ts index b013429b920..683cdecd85d 100644 --- a/cypress/e2e/invite/invite-dialog.spec.ts +++ b/cypress/e2e/invite/invite-dialog.spec.ts @@ -47,16 +47,8 @@ describe("Invite dialog", function () { // Open the room info panel cy.findByRole("button", { name: "Room info" }).click(); - cy.get(".mx_RightPanel").within(() => { - // Click "People" button on the panel - // Regex pattern due to the string of "mx_BaseCard_Button_sublabel" - cy.findByRole("button", { name: /People/ }).click(); - }); - cy.get(".mx_BaseCard").within(() => { - // Click "Invite to this room" button - // Regex pattern due to "mx_MemberList_invite span::before" - cy.findByRole("button", { name: /Invite to this room/ }).click(); + cy.findByRole("menuitem", { name: "Invite" }).click(); }); cy.get(".mx_InviteDialog_other").within(() => { diff --git a/cypress/e2e/lazy-loading/lazy-loading.spec.ts b/cypress/e2e/lazy-loading/lazy-loading.spec.ts index db7fedfb0ef..9b5f0eb6703 100644 --- a/cypress/e2e/lazy-loading/lazy-loading.spec.ts +++ b/cypress/e2e/lazy-loading/lazy-loading.spec.ts @@ -121,7 +121,7 @@ describe("Lazy Loading", () => { }); cy.get(".mx_RoomSummaryCard").within(() => { - cy.findByRole("button", { name: /People \d/ }).click(); // \d represents the number of the room members + cy.findByRole("menuitem", { name: "People" }).click(); // \d represents the number of the room members }); } diff --git a/cypress/e2e/polls/pollHistory.spec.ts b/cypress/e2e/polls/pollHistory.spec.ts index 00938ab768b..dec4fed5afa 100644 --- a/cypress/e2e/polls/pollHistory.spec.ts +++ b/cypress/e2e/polls/pollHistory.spec.ts @@ -77,7 +77,7 @@ describe("Poll history", () => { function openPollHistory(): void { cy.findByRole("button", { name: "Room info" }).click(); cy.get(".mx_RoomSummaryCard").within(() => { - cy.findByRole("button", { name: "Poll history" }).click(); + cy.findByRole("menuitem", { name: "Poll history" }).click(); }); } diff --git a/cypress/e2e/right-panel/file-panel.spec.ts b/cypress/e2e/right-panel/file-panel.spec.ts index a1bf2e9c96f..d2ada561081 100644 --- a/cypress/e2e/right-panel/file-panel.spec.ts +++ b/cypress/e2e/right-panel/file-panel.spec.ts @@ -61,7 +61,7 @@ describe("FilePanel", () => { // Open the file panel viewRoomSummaryByName(ROOM_NAME); - cy.get(".mx_RoomSummaryCard_icon_files").click(); + cy.findByRole("menuitem", { name: "Files" }).click(); cy.get(".mx_FilePanel").should("have.length", 1); }); diff --git a/cypress/e2e/right-panel/right-panel.spec.ts b/cypress/e2e/right-panel/right-panel.spec.ts index d566ceb3c44..b68093b0f5a 100644 --- a/cypress/e2e/right-panel/right-panel.spec.ts +++ b/cypress/e2e/right-panel/right-panel.spec.ts @@ -103,21 +103,21 @@ describe("RightPanel", () => { it("should handle viewing export chat", () => { viewRoomSummaryByName(ROOM_NAME); - cy.findByRole("button", { name: "Export chat" }).click(); + cy.findByRole("menuitem", { name: "Export Chat" }).click(); cy.get(".mx_ExportDialog").should("have.length", 1); }); it("should handle viewing share room", () => { viewRoomSummaryByName(ROOM_NAME); - cy.findByRole("button", { name: "Share room" }).click(); + cy.findByRole("menuitem", { name: "Copy link" }).click(); cy.get(".mx_ShareDialog").should("have.length", 1); }); it("should handle viewing room settings", () => { viewRoomSummaryByName(ROOM_NAME); - cy.findByRole("button", { name: "Room settings" }).click(); + cy.findByRole("menuitem", { name: "Settings" }).click(); cy.get(".mx_RoomSettingsDialog").should("have.length", 1); cy.get(".mx_Dialog_title").within(() => { cy.findByText("Room Settings - " + ROOM_NAME).should("exist"); @@ -127,7 +127,7 @@ describe("RightPanel", () => { it("should handle viewing files", () => { viewRoomSummaryByName(ROOM_NAME); - cy.findByRole("button", { name: "Files" }).click(); + cy.findByRole("menuitem", { name: "Files" }).click(); cy.get(".mx_FilePanel").should("have.length", 1); cy.get(".mx_FilePanel_empty").should("have.length", 1); @@ -138,8 +138,7 @@ describe("RightPanel", () => { it("should handle viewing room member", () => { viewRoomSummaryByName(ROOM_NAME); - // \d represents the number of the room members inside mx_BaseCard_Button_sublabel - cy.findByRole("button", { name: /People \d/ }).click(); + cy.findByRole("menuitem", { name: "People" }).click(); cy.get(".mx_MemberList").should("have.length", 1); getMemberTileByName(NAME).click(); diff --git a/package.json b/package.json index 49978683fcd..b708963482d 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@sentry/tracing": "^7.0.0", "@testing-library/react-hooks": "^8.0.1", "@vector-im/compound-design-tokens": "^0.0.7", - "@vector-im/compound-web": "0.6.3", + "@vector-im/compound-web": "0.8.1", "@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4", "@zxcvbn-ts/language-en": "^3.0.2", diff --git a/res/css/views/right_panel/_BaseCard.pcss b/res/css/views/right_panel/_BaseCard.pcss index 494c61c1806..79d18a7dbba 100644 --- a/res/css/views/right_panel/_BaseCard.pcss +++ b/res/css/views/right_panel/_BaseCard.pcss @@ -158,11 +158,18 @@ limitations under the License. .mx_BaseCard_close { flex-shrink: 0; position: relative; + // @TODO(kerrya) background colours here are not semantic + // these buttons to be replaced with IconButton after secondary variant is added + // https://github.com/vector-im/compound/issues/279 background-color: var(--cpd-color-bg-subtle-secondary); width: var(--BaseCard_header-button-size); height: var(--BaseCard_header-button-size); border-radius: 50%; + &:hover { + background-color: var(--cpd-color-bg-subtle-primary); + } + &::before { content: ""; position: absolute; diff --git a/res/css/views/right_panel/_RoomSummaryCard.pcss b/res/css/views/right_panel/_RoomSummaryCard.pcss index 4e68f258d1b..72b23d860e3 100644 --- a/res/css/views/right_panel/_RoomSummaryCard.pcss +++ b/res/css/views/right_panel/_RoomSummaryCard.pcss @@ -17,7 +17,7 @@ limitations under the License. .mx_RoomSummaryCard { .mx_RoomSummaryCard_container { text-align: center; - margin-top: $spacing-20; + margin: $spacing-20 var(--cpd-space-4x) 0; } .mx_RoomSummaryCard_roomName, @@ -29,10 +29,6 @@ limitations under the License. overflow: hidden; } - .mx_RoomSummaryCard_roomName { - margin: $spacing-12 0 $spacing-4; - } - .mx_RoomSummaryCard_alias { text-overflow: ellipsis; } @@ -203,42 +199,6 @@ limitations under the License. cursor: pointer; } -.mx_RoomSummaryCard_icon_people::before { - mask-image: url("$(res)/img/element-icons/room/members.svg"); -} - -.mx_RoomSummaryCard_icon_files::before { - mask-image: url("$(res)/img/element-icons/room/files.svg"); -} - -.mx_RoomSummaryCard_icon_pins::before { - mask-image: url("$(res)/img/element-icons/room/pin-upright.svg"); -} - -.mx_RoomSummaryCard_icon_threads::before { - mask-image: url("$(res)/img/element-icons/message/thread.svg"); -} - -.mx_RoomSummaryCard_icon_share::before { - mask-image: url("$(res)/img/element-icons/room/share.svg"); -} - -.mx_RoomSummaryCard_icon_settings::before { - mask-image: url("$(res)/img/element-icons/settings.svg"); -} - -.mx_RoomSummaryCard_icon_export::before { - mask-image: url("$(res)/img/element-icons/export.svg"); -} - -.mx_RoomSummaryCard_icon_poll::before { - mask-image: url("$(res)/img/element-icons/room/composer/poll.svg"); -} - -.mx_RoomSummaryCard_icon_search::before { - mask-image: url("$(res)/img/element-icons/room/search-inset.svg"); -} - .mx_RoomSummaryCard_searchBtn { background: var(--cpd-color-bg-canvas-default); color: var(--cpd-color-icon-primary); @@ -248,9 +208,12 @@ limitations under the License. height: 36px; padding: var(--cpd-space-2x); cursor: pointer; - transition: all 0.3s ease; &:hover { background: var(--cpd-color-bg-subtle-primary); } } + +.mx_RoomSummaryCard_roomName { + margin: $spacing-12 0 $spacing-4; +} diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx index ee3c6ded8a9..7527ef4feef 100644 --- a/src/components/views/context_menus/RoomContextMenu.tsx +++ b/src/components/views/context_menus/RoomContextMenu.tsx @@ -16,7 +16,6 @@ limitations under the License. import React, { useContext } from "react"; import { Room } from "matrix-js-sdk/src/matrix"; -import { logger } from "matrix-js-sdk/src/logger"; import { IProps as IContextMenuProps } from "../../structures/ContextMenu"; import IconizedContextMenu, { @@ -30,7 +29,6 @@ import { ButtonEvent } from "../elements/AccessibleButton"; import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore"; import dis from "../../../dispatcher/dispatcher"; -import RoomListActions from "../../../actions/RoomListActions"; import { EchoChamber } from "../../../stores/local-echo/EchoChamber"; import { RoomNotifState } from "../../../RoomNotifs"; import Modal from "../../../Modal"; @@ -52,6 +50,7 @@ import { SdkContextClass } from "../../../contexts/SDKContext"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { UIComponent } from "../../../settings/UIFeature"; import { DeveloperToolsOption } from "./DeveloperToolsOption"; +import { tagRoom } from "../../../utils/room/tagRoom"; interface IProps extends IContextMenuProps { room: Room; @@ -333,15 +332,7 @@ const RoomContextMenu: React.FC = ({ room, onFinished, ...props }) => { 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, 0)); - } else { - logger.warn(`Unexpected tag ${tagId} applied to ${room.roomId}`); - } + tagRoom(room, tagId); const action = getKeyBindingsManager().getAccessibilityAction(ev as React.KeyboardEvent); switch (action) { diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index ef6e3ea3ab2..d6dd212e7cc 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -16,25 +16,35 @@ limitations under the License. import React, { useCallback, useContext, useEffect, useMemo, useState } from "react"; import classNames from "classnames"; -import { EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix"; -import { Badge, Heading, Text, Tooltip } from "@vector-im/compound-web"; +import { MenuItem, Tooltip, Separator, ToggleMenuItem, Text, Badge, Heading } from "@vector-im/compound-web"; import { Icon as SearchIcon } from "@vector-im/compound-design-tokens/icons/search.svg"; +import { Icon as FavouriteIcon } from "@vector-im/compound-design-tokens/icons/favourite-off.svg"; +import { Icon as UserAddIcon } from "@vector-im/compound-design-tokens/icons/user-add.svg"; +import { Icon as UserProfileSolidIcon } from "@vector-im/compound-design-tokens/icons/user-profile-solid.svg"; +import { Icon as LinkIcon } from "@vector-im/compound-design-tokens/icons/link.svg"; +import { Icon as SettingsIcon } from "@vector-im/compound-design-tokens/icons/settings.svg"; +import { Icon as ExportArchiveIcon } from "@vector-im/compound-design-tokens/icons/export-archive.svg"; +import { Icon as LeaveIcon } from "@vector-im/compound-design-tokens/icons/leave.svg"; +import { Icon as FilesIcon } from "@vector-im/compound-design-tokens/icons/files.svg"; +import { Icon as PollsIcon } from "@vector-im/compound-design-tokens/icons/polls.svg"; +import { Icon as PinIcon } from "@vector-im/compound-design-tokens/icons/pin-off.svg"; import { Icon as LockIcon } from "@vector-im/compound-design-tokens/icons/lock.svg"; import { Icon as LockOffIcon } from "@vector-im/compound-design-tokens/icons/lock-off.svg"; import { Icon as PublicIcon } from "@vector-im/compound-design-tokens/icons/public.svg"; import { Icon as ErrorIcon } from "@vector-im/compound-design-tokens/icons/error.svg"; +import { EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { useIsEncrypted } from "../../../hooks/useIsEncrypted"; import BaseCard, { Group } from "./BaseCard"; import { _t } from "../../../languageHandler"; import RoomAvatar from "../avatars/RoomAvatar"; -import AccessibleButton, { ButtonEvent, IAccessibleButtonProps } from "../elements/AccessibleButton"; +import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; import Modal from "../../../Modal"; import ShareDialog from "../dialogs/ShareDialog"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useEventEmitter, useEventEmitterState } from "../../../hooks/useEventEmitter"; import WidgetUtils from "../../../utils/WidgetUtils"; import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; import SettingsStore from "../../../settings/SettingsStore"; @@ -47,7 +57,6 @@ import RoomContext from "../../../contexts/RoomContext"; import { UIComponent, UIFeature } from "../../../settings/UIFeature"; import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu"; import { WidgetContextMenu } from "../context_menus/WidgetContextMenu"; -import { useRoomMemberCount } from "../../../hooks/useRoomMembers"; import { useFeatureEnabled } from "../../../hooks/useSettings"; import { usePinnedEvents } from "./PinnedMessagesCard"; import { Container, MAX_PINNED, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; @@ -59,6 +68,11 @@ import PosthogTrackers from "../../../PosthogTrackers"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { PollHistoryDialog } from "../dialogs/PollHistoryDialog"; import { Flex } from "../../utils/Flex"; +import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore"; +import { DefaultTagID } from "../../../stores/room-list/models"; +import { tagRoom } from "../../../utils/room/tagRoom"; +import { canInviteTo } from "../../../utils/room/canInviteTo"; +import { inviteToRoom } from "../../../utils/room/inviteToRoom"; import { useAccountData } from "../../../hooks/useAccountData"; import { useRoomState } from "../../../hooks/useRoomState"; @@ -73,23 +87,6 @@ interface IAppsSectionProps { room: Room; } -interface IButtonProps extends IAccessibleButtonProps { - className: string; - onClick(ev: ButtonEvent): void; -} - -const Button: React.FC = ({ children, className, onClick, ...props }) => { - return ( - - {children} - - ); -}; - export const useWidgets = (room: Room): IApp[] => { const [apps, setApps] = useState(() => WidgetStore.instance.getApps(room.roomId)); @@ -263,11 +260,6 @@ const AppsSection: React.FC = ({ room }) => { ); }; -const onRoomMembersClick = (ev: ButtonEvent): void => { - RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, true); - PosthogTrackers.trackInteraction("WebRightPanelRoomInfoPeopleButton", ev); -}; - const onRoomFilesClick = (): void => { RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, true); }; @@ -304,6 +296,18 @@ const RoomSummaryCard: React.FC = ({ room, permalinkCreator, onClose, on }); }; + const onLeaveRoomClick = (): void => { + defaultDispatcher.dispatch({ + action: "leave_room", + room_id: room.roomId, + }); + }; + + const onRoomMembersClick = (ev: ButtonEvent): void => { + RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, true); + PosthogTrackers.trackInteraction("WebRightPanelRoomInfoPeopleButton", ev); + }; + const isRoomEncrypted = useIsEncrypted(cli, room); const roomContext = useContext(RoomContext); const e2eStatus = roomContext.e2eStatus; @@ -383,10 +387,14 @@ const RoomSummaryCard: React.FC = ({ room, permalinkCreator, onClose, on ); - const memberCount = useRoomMemberCount(room); const pinningEnabled = useFeatureEnabled("feature_pinning"); const pinCount = usePinnedEvents(pinningEnabled ? room : undefined)?.length; + const roomTags = useEventEmitterState(RoomListStore.instance, LISTS_UPDATE_EVENT, () => + RoomListStore.instance.getTagsForRoom(room), + ); + const isFavorite = roomTags.includes(DefaultTagID.Favourite); + return ( = ({ room, permalinkCreator, onClose, on {header} - - - {!isVideoRoom && ( - - )} - {!isVideoRoom && ( - - )} - {pinningEnabled && !isVideoRoom && ( - - )} - {!isVideoRoom && ( - - )} - - - + + + + tagRoom(room, DefaultTagID.Favourite)} + /> + inviteToRoom(room)} + /> + + + + + + {!isVideoRoom && ( + <> + + + {pinningEnabled && ( + + + {pinCount} + + + )} + + + )} + + + + {SettingsStore.getValue(UIFeature.Widgets) && !isVideoRoom && diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d5b76383b47..d6f4f43d118 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -418,7 +418,6 @@ "spaceinvaders_message": "sends space invaders" }, "common": { - "about": "About", "access_token": "Access Token", "accessibility": "Accessibility", "advanced": "Advanced", @@ -1854,8 +1853,6 @@ "room_summary_card": { "title": "Room info" }, - "settings_button": "Room settings", - "share_button": "Share room", "thread_list": { "context_menu_label": "Thread options" }, diff --git a/src/utils/room/canInviteTo.ts b/src/utils/room/canInviteTo.ts new file mode 100644 index 00000000000..55265e6cc8a --- /dev/null +++ b/src/utils/room/canInviteTo.ts @@ -0,0 +1,33 @@ +/* +Copyright 2023 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 { JoinRule, Room } from "matrix-js-sdk/src/matrix"; + +import { shouldShowComponent } from "../../customisations/helpers/UIComponents"; +import { UIComponent } from "../../settings/UIFeature"; + +/** + * Can a user invite new members to the room + * @param room + * @returns whether the user can invite new members to the room + */ +export function canInviteTo(room: Room): boolean { + const client = room.client; + const canInvite = + !!room.canInvite(client.getSafeUserId()) || !!(room.isSpaceRoom() && room.getJoinRule() === JoinRule.Public); + + return canInvite && room.getMyMembership() === "join" && shouldShowComponent(UIComponent.InviteUsers); +} diff --git a/src/utils/room/inviteToRoom.ts b/src/utils/room/inviteToRoom.ts new file mode 100644 index 00000000000..b8fb1214fef --- /dev/null +++ b/src/utils/room/inviteToRoom.ts @@ -0,0 +1,36 @@ +/* +Copyright 2023 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 { Room } from "matrix-js-sdk/src/matrix"; + +import dis from "../../dispatcher/dispatcher"; + +/** + * Invite to a room and prompts guests to registers + * @param room + */ +export function inviteToRoom(room: Room): void { + if (room.client.isGuest()) { + dis.dispatch({ action: "require_registration" }); + return; + } + + // open the room inviter + dis.dispatch({ + action: "view_invite", + roomId: room.roomId, + }); +} diff --git a/src/utils/room/tagRoom.ts b/src/utils/room/tagRoom.ts new file mode 100644 index 00000000000..f358b288ae4 --- /dev/null +++ b/src/utils/room/tagRoom.ts @@ -0,0 +1,40 @@ +/* +Copyright 2023 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 { Room } from "matrix-js-sdk/src/matrix"; +import { logger } from "matrix-js-sdk/src/logger"; + +import RoomListStore from "../../stores/room-list/RoomListStore"; +import { DefaultTagID, TagID } from "../../stores/room-list/models"; +import RoomListActions from "../../actions/RoomListActions"; +import dis from "../../dispatcher/dispatcher"; + +/** + * Toggle tag for a given room + * @param room The room to tag + * @param tagId The tag to invert + */ +export function tagRoom(room: Room, tagId: TagID): void { + 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(room.client, room, removeTag, addTag, 0)); + } else { + logger.warn(`Unexpected tag ${tagId} applied to ${room.roomId}`); + } +} diff --git a/test/components/structures/RightPanel-test.tsx b/test/components/structures/RightPanel-test.tsx index 7ae471a23bf..d7f5fbef114 100644 --- a/test/components/structures/RightPanel-test.tsx +++ b/test/components/structures/RightPanel-test.tsx @@ -16,7 +16,6 @@ limitations under the License. import React from "react"; import { render, screen, waitFor } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; import { jest } from "@jest/globals"; import { mocked, MockedObject } from "jest-mock"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; @@ -85,47 +84,6 @@ describe("RightPanel", () => { const waitForRpsUpdate = () => new Promise((resolve) => RightPanelStore.instance.once(UPDATE_EVENT, resolve)); - it("navigates from room summary to member list", async () => { - const r1 = mkRoom(cli, "r1"); - cli.getRoom.mockImplementation((roomId) => (roomId === "r1" ? r1 : null)); - - // Set up right panel state - const realGetValue = SettingsStore.getValue; - jest.spyOn(SettingsStore, "getValue").mockImplementation((name, roomId) => { - if (name !== "RightPanel.phases") return realGetValue(name, roomId); - if (roomId === "r1") { - return { - history: [{ phase: RightPanelPhases.RoomSummary }], - isOpen: true, - }; - } - return null; - }); - - await spinUpStores(); - const viewedRoom = waitForRpsUpdate(); - dis.dispatch({ - action: Action.ViewRoom, - room_id: "r1", - }); - await viewedRoom; - - const { container } = render( - , - ); - expect(container.getElementsByClassName("mx_RoomSummaryCard")).toHaveLength(1); - - const switchedPhases = waitForRpsUpdate(); - userEvent.click(screen.getByText(/people/i)); - await switchedPhases; - - expect(container.getElementsByClassName("mx_MemberList")).toHaveLength(1); - }); - it("renders info from only one room during room changes", async () => { const r1 = mkRoom(cli, "r1"); const r2 = mkRoom(cli, "r2"); diff --git a/test/components/views/right_panel/RoomSummaryCard-test.tsx b/test/components/views/right_panel/RoomSummaryCard-test.tsx index 7f556000f6f..e6d43ef7ee3 100644 --- a/test/components/views/right_panel/RoomSummaryCard-test.tsx +++ b/test/components/views/right_panel/RoomSummaryCard-test.tsx @@ -33,6 +33,7 @@ import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } f import { PollHistoryDialog } from "../../../../src/components/views/dialogs/PollHistoryDialog"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; import { _t } from "../../../../src/languageHandler"; +import SettingsStore from "../../../../src/settings/SettingsStore"; describe("", () => { const userId = "@alice:domain.org"; @@ -108,17 +109,6 @@ describe("", () => { expect(onSearchClick).toHaveBeenCalled(); }); - it("opens room members list on button click", () => { - const { getByText } = getComponent(); - - fireEvent.click(getByText("People")); - - expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith( - { phase: RightPanelPhases.RoomMemberList }, - true, - ); - }); - it("opens room file panel on button click", () => { const { getByText } = getComponent(); @@ -130,7 +120,7 @@ describe("", () => { it("opens room export dialog on button click", () => { const { getByText } = getComponent(); - fireEvent.click(getByText("Export chat")); + fireEvent.click(getByText(_t("export_chat|title"))); expect(Modal.createDialog).toHaveBeenCalledWith(ExportDialog, { room }); }); @@ -138,7 +128,7 @@ describe("", () => { it("opens share room dialog on button click", () => { const { getByText } = getComponent(); - fireEvent.click(getByText("Share room")); + fireEvent.click(getByText(_t("action|copy_link"))); expect(Modal.createDialog).toHaveBeenCalledWith(ShareDialog, { target: room }); }); @@ -146,11 +136,23 @@ describe("", () => { it("opens room settings on button click", () => { const { getByText } = getComponent(); - fireEvent.click(getByText("Room settings")); + fireEvent.click(getByText(_t("common|settings"))); expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "open_room_settings" }); }); + it("renders room members options when new room UI is not enabled", () => { + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); + const { getByText } = getComponent(); + + fireEvent.click(getByText(_t("common|people"))); + + expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith( + { phase: RightPanelPhases.RoomMemberList }, + true, + ); + }); + describe("pinning", () => { it("renders pins options when pinning feature is enabled", () => { mocked(settingsHooks.useFeatureEnabled).mockImplementation((feature) => feature === "feature_pinning"); diff --git a/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap b/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap index 9e81528f1c4..399bdcf7690 100644 --- a/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap +++ b/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap @@ -46,13 +46,13 @@ exports[` renders the room summary 1`] = ` !

!room:domain.org

renders the room summary 1`] = ` style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x);" >
renders the room summary 1`] = `