diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 16574bad79..b436b4e901 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,3 +2,4 @@ /.github/workflows/** @matrix-org/element-web-app-team /package.json @matrix-org/element-web-app-team /yarn.lock @matrix-org/element-web-app-team +/src/i18n/strings diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000000..e9d334f61b --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1 @@ +_extends: matrix-org/matrix-js-sdk diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index 2892b62ae0..424d3699b6 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -78,16 +78,19 @@ jobs: core.setOutput("author", response.data.author.name); core.setOutput("email", response.data.author.email); - # Only run Percy when it is demanded or we are running the daily build - - name: Enable Percy - id: percy - if: | - github.event.workflow_run.event == 'schedule' || - ( - github.event.workflow_run.event == 'merge_group' && - contains(fromJSON(steps.prdetails.outputs.data).labels.*.name, 'X-Needs-Percy') - ) - run: echo "value=1" >> $GITHUB_OUTPUT + # Percy is disabled while we're figuring out https://github.com/vector-im/wat-internal/issues/36 + # and https://github.com/vector-im/wat-internal/issues/56. We're hoping to turn it back on or switch + # to an alternative in the future. + # # Only run Percy when it is demanded or we are running the daily build + # - name: Enable Percy + # id: percy + # if: | + # github.event.workflow_run.event == 'schedule' || + # ( + # github.event.workflow_run.event == 'merge_group' && + # contains(fromJSON(steps.prdetails.outputs.data).labels.*.name, 'X-Needs-Percy') + # ) + # run: echo "value=1" >> $GITHUB_OUTPUT - name: Generate unique ID 💎 id: uuid diff --git a/.github/workflows/element-web.yaml b/.github/workflows/element-web.yaml index 74a570d78b..7b085d892d 100644 --- a/.github/workflows/element-web.yaml +++ b/.github/workflows/element-web.yaml @@ -3,8 +3,12 @@ # as an artifact and run integration tests. name: Element Web - Build on: - schedule: - - cron: "17 4 * * 1-5" # every weekday at 04:17 UTC + # We only need the nightly run for Percy which is disabled while we're + # figuring out https://github.com/vector-im/wat-internal/issues/36 and + # https://github.com/vector-im/wat-internal/issues/56. We're hoping to + # turn it back on or switch to an alternative in the future. + # schedule: + # - cron: "17 4 * * 1-5" # every weekday at 04:17 UTC pull_request: {} merge_group: types: [checks_requested] diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000000..3ce2b5a2af --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,14 @@ +name: Release Drafter +on: + push: + branches: [staging] +concurrency: ${{ github.workflow }} +jobs: + draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@dabcf3767562210392d862070ed2ef6434b9bc6f # v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + disable-autolabeler: true diff --git a/.github/workflows/release-gitflow.yml b/.github/workflows/release-gitflow.yml new file mode 100644 index 0000000000..b515bb4cc1 --- /dev/null +++ b/.github/workflows/release-gitflow.yml @@ -0,0 +1,13 @@ +# Gitflow merge-back master->develop +name: Merge master -> develop +on: + push: + branches: [master] +concurrency: ${{ github.repository }}-${{ github.workflow }} +jobs: + merge: + uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop + secrets: inherit + with: + dependencies: | + matrix-js-sdk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8b44ef47d..1b08aa0734 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,32 @@ name: Release Process on: - release: - types: [published] -concurrency: ${{ github.workflow }}-${{ github.ref }} + workflow_dispatch: + inputs: + mode: + description: What type of release + required: true + default: rc + type: choice + options: + - rc + - final + matrix-js-sdk: + description: JS SDK version to use (current|X.Y.Z) + required: false + default: current + type: string + npm: + description: Publish to npm + required: true + type: boolean + default: true +concurrency: ${{ github.workflow }} jobs: - npm: - name: Publish - uses: matrix-org/matrix-js-sdk/.github/workflows/release-npm.yml@develop - secrets: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + release: + uses: matrix-org/matrix-js-sdk/.github/workflows/release-action.yml@develop + secrets: inherit + with: + final: ${{ inputs.mode == 'final' }} + npm: ${{ inputs.npm }} + dependencies: | + matrix-js-sdk=${{ inputs.matrix-js-sdk }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e792b05d8..b00376bb84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.84.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.84.1) (2023-11-13) +===================================================================================================== + +## 🐛 Bug Fixes + * Ensure `setUserCreator` is called when a store is assigned ([\#3867](https://github.com/matrix-org/matrix-js-sdk/pull/3867)). Fixes vector-im/element-web#26520. Contributed by @MidhunSureshR. + Changes in [3.84.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.84.0) (2023-11-07) ===================================================================================================== diff --git a/README.md b/README.md index f17350a43d..39d1f600e2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,14 @@ ![Tests](https://github.com/matrix-org/matrix-react-sdk/actions/workflows/tests.yml/badge.svg) ![Static Analysis](https://github.com/matrix-org/matrix-react-sdk/actions/workflows/static_analysis.yaml/badge.svg) [![matrix-react-sdk](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/ppvnzg/develop&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/ppvnzg/runs) + + + [![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement-web%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element-web) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=matrix-react-sdk&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=matrix-react-sdk) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=matrix-react-sdk&metric=coverage)](https://sonarcloud.io/summary/new_code?id=matrix-react-sdk) diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index 9795b9da2d..f9cd5ba316 100644 --- a/cypress/e2e/crypto/crypto.spec.ts +++ b/cypress/e2e/crypto/crypto.spec.ts @@ -269,7 +269,7 @@ describe("Cryptography", function () { // Assert that verified icon is rendered cy.findByRole("button", { name: "Room members" }).click(); cy.findByRole("button", { name: "Room information" }).click(); - cy.get(".mx_RoomSummaryCard_e2ee_verified").should("exist"); + cy.get('.mx_RoomSummaryCard_badges [data-kind="success"]').should("contain.text", "Encrypted"); // Take a snapshot of RoomSummaryCard with a verified E2EE icon cy.get(".mx_RightPanel").percySnapshotElement("RoomSummaryCard - with a verified E2EE icon", { diff --git a/cypress/e2e/read-receipts/editing-messages.spec.ts b/cypress/e2e/read-receipts/editing-messages.spec.ts index c5ccb77750..04b21acd1a 100644 --- a/cypress/e2e/read-receipts/editing-messages.spec.ts +++ b/cypress/e2e/read-receipts/editing-messages.spec.ts @@ -134,8 +134,7 @@ describe("Read receipts", () => { goTo(room1); assertStillRead(room2); }); - // XXX: fails because we see a dot instead of an unread number - probably the server and client disagree - it.skip("Editing a message after marking as read makes the room unread", () => { + it("Editing a message after marking as read makes the room unread", () => { // Given the room is marked as read goTo(room1); receiveMessages(room2, ["Msg1"]); @@ -146,8 +145,8 @@ describe("Read receipts", () => { // When a message is edited receiveMessages(room2, [editOf("Msg1", "Msg1 Edit1")]); - // Then the room becomes unread - assertUnread(room2, 1); + // Then the room remains unread + assertStillRead(room2); }); it("Editing a reply after reading it makes the room unread", () => { // Given the room is all read @@ -178,7 +177,8 @@ describe("Read receipts", () => { // Then the room remains read assertStillRead(room2); }); - it("A room with an edit is still read after restart", () => { + // XXX: fails because flaky: https://github.com/vector-im/element-web/issues/26341 + it.skip("A room with an edit is still read after restart", () => { // Given a message is marked as read goTo(room2); receiveMessages(room2, ["Msg1"]); diff --git a/cypress/e2e/read-receipts/high-level.spec.ts b/cypress/e2e/read-receipts/high-level.spec.ts index 36f358f00d..2c49e1cfc9 100644 --- a/cypress/e2e/read-receipts/high-level.spec.ts +++ b/cypress/e2e/read-receipts/high-level.spec.ts @@ -303,7 +303,8 @@ describe("Read receipts", () => { assertUnreadThread("Root2"); assertUnreadThread("Root3"); }); - it("Looking in thread view to find old threads that were never read makes the room unread", () => { + // XXX: fails because flaky: https://github.com/vector-im/element-web/issues/26331 + it.skip("Looking in thread view to find old threads that were never read makes the room unread", () => { // Given lots of messages in threads that are unread goTo(room1); receiveMessages(room2, [ diff --git a/docs/cypress.md b/docs/cypress.md index 91ec314bac..d786e1b5c0 100644 --- a/docs/cypress.md +++ b/docs/cypress.md @@ -238,6 +238,10 @@ should generally try to adhere to them. ## Screenshot testing with Percy +**⚠️ Percy is disabled while we're figuring out https://github.com/vector-im/wat-internal/issues/36** +**and https://github.com/vector-im/wat-internal/issues/56. We're hoping to turn it back on or switch** +**to an alternative in the future.** + We also support visual testing via [Percy](https://percy.io). Within many of our Cypress tests you can see lines calling `cy.percySnapshot()`. This creates a screenshot and uses Percy to check whether it has changed from the last time diff --git a/package.json b/package.json index 1246cf210d..f7120476ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.84.0", + "version": "3.84.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { diff --git a/res/css/views/right_panel/_RoomSummaryCard.pcss b/res/css/views/right_panel/_RoomSummaryCard.pcss index e2b71e1a15..d6da39365e 100644 --- a/res/css/views/right_panel/_RoomSummaryCard.pcss +++ b/res/css/views/right_panel/_RoomSummaryCard.pcss @@ -31,68 +31,12 @@ limitations under the License. .mx_RoomSummaryCard_roomName { margin: $spacing-12 0 $spacing-4; - font-weight: var(--cpd-font-weight-semibold); - font-size: $font-17px; } .mx_RoomSummaryCard_alias { - font: var(--cpd-font-body-md-regular); - color: $secondary-content; text-overflow: ellipsis; } - .mx_RoomSummaryCard_avatar { - display: flex; - justify-content: center; - align-items: center; - - .mx_RoomSummaryCard_e2ee { - display: inline-block; - position: relative; - width: 54px; - height: 54px; - border-radius: 50%; - background-color: #737d8c; - margin-left: -10px; /* overlap */ - border: 3px solid $dark-panel-bg-color; - - &::before { - content: ""; - position: absolute; - top: 13px; - left: 13px; - height: 28px; - width: 28px; - mask-size: cover; - mask-repeat: no-repeat; - mask-position: center; - mask-image: url("$(res)/img/e2e/disabled.svg"); - background-color: #ffffff; - } - } - - .mx_RoomSummaryCard_e2ee_normal { - background-color: #424446; - &::before { - mask-image: url("$(res)/img/e2e/normal.svg"); - } - } - - .mx_RoomSummaryCard_e2ee_verified { - background-color: $e2e-verified-color; - &::before { - mask-image: url("$(res)/img/e2e/verified.svg"); - } - } - - .mx_RoomSummaryCard_e2ee_warning { - background-color: $e2e-warning-color; - &::before { - mask-image: url("$(res)/img/e2e/warning.svg"); - } - } - } - .mx_RoomSummaryCard_aboutGroup { .mx_RoomSummaryCard_Button { padding-left: 44px; @@ -244,6 +188,10 @@ limitations under the License. } } +.mx_RoomSummaryCard_badges { + margin: var(--cpd-space-4x) 0; +} + .mx_RoomSummaryCard_header { padding: 15px 12px; diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index 98876fce93..ef6e3ea3ab 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -16,9 +16,13 @@ limitations under the License. import React, { useCallback, useContext, useEffect, useMemo, useState } from "react"; import classNames from "classnames"; -import { Room } from "matrix-js-sdk/src/matrix"; -import { Tooltip } from "@vector-im/compound-web"; +import { EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix"; +import { Badge, Heading, Text, Tooltip } from "@vector-im/compound-web"; import { Icon as SearchIcon } from "@vector-im/compound-design-tokens/icons/search.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 MatrixClientContext from "../../../contexts/MatrixClientContext"; import { useIsEncrypted } from "../../../hooks/useIsEncrypted"; @@ -34,7 +38,6 @@ import { useEventEmitter } from "../../../hooks/useEventEmitter"; import WidgetUtils from "../../../utils/WidgetUtils"; import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; import SettingsStore from "../../../settings/SettingsStore"; -import TextWithTooltip from "../elements/TextWithTooltip"; import WidgetAvatar from "../avatars/WidgetAvatar"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import WidgetStore, { IApp } from "../../../stores/WidgetStore"; @@ -56,6 +59,8 @@ import PosthogTrackers from "../../../PosthogTrackers"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { PollHistoryDialog } from "../dialogs/PollHistoryDialog"; import { Flex } from "../../utils/Flex"; +import { useAccountData } from "../../../hooks/useAccountData"; +import { useRoomState } from "../../../hooks/useRoomState"; interface IProps { room: Room; @@ -307,31 +312,74 @@ const RoomSummaryCard: React.FC = ({ room, permalinkCreator, onClose, on const isVideoRoom = videoRoomsEnabled && (room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom())); + const roomState = useRoomState(room); + const directRoomsList = useAccountData>(room.client, EventType.Direct); + const [isDirectMessage, setDirectMessage] = useState(false); + useEffect(() => { + for (const [, dmRoomList] of Object.entries(directRoomsList)) { + if (dmRoomList.includes(room?.roomId ?? "")) { + setDirectMessage(true); + break; + } + } + }, [room, directRoomsList]); + const alias = room.getCanonicalAlias() || room.getAltAliases()[0] || ""; const header = (
-
- - -
- + {(name) => ( -

+ {name} -

+ )}
-
+ {alias} -
+ + + + {!isDirectMessage && roomState.getJoinRule() === JoinRule.Public && ( + + + {_t("common|public_room")} + + )} + + {isRoomEncrypted && e2eStatus !== E2EStatus.Warning && ( + + + {_t("common|encrypted")} + + )} + + {!e2eStatus && ( + + + {_t("common|unencrypted")} + + )} + + {e2eStatus === E2EStatus.Warning && ( + + + {_t("common|not_trusted")} + + )} +
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7ae2f7a70f..d5b76383b4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1398,6 +1398,7 @@ "element_call_video_rooms": "Element Call video rooms", "experimental_description": "Feeling experimental? Try out our latest ideas in development. These features are not finalised; they may be unstable, may change, or may be dropped altogether. Learn more.", "experimental_section": "Early previews", + "feature_disable_call_per_sender_encryption": "Disable per-sender encryption for Element Call", "feature_wysiwyg_composer_description": "Use rich text instead of Markdown in the message composer.", "group_calls": "New group call experience", "group_developer": "Developer", diff --git a/src/models/Call.ts b/src/models/Call.ts index ecf1cbab66..9a841eb9d7 100644 --- a/src/models/Call.ts +++ b/src/models/Call.ts @@ -661,9 +661,11 @@ export class ElementCall extends Call { analyticsID, }); - if (client.isRoomEncrypted(roomId)) params.append("perParticipantE2EE", ""); - if (SettingsStore.getValue("fallbackICEServerAllowed")) params.append("allowIceFallback", ""); - if (SettingsStore.getValue("feature_allow_screen_share_only_mode")) params.append("allowVoipWithNoMedia", ""); + if (client.isRoomEncrypted(roomId) && !SettingsStore.getValue("feature_disable_call_per_sender_encryption")) + params.append("perParticipantE2EE", "true"); + if (SettingsStore.getValue("fallbackICEServerAllowed")) params.append("allowIceFallback", "true"); + if (SettingsStore.getValue("feature_allow_screen_share_only_mode")) + params.append("allowVoipWithNoMedia", "true"); // Set custom fonts if (SettingsStore.getValue("useSystemFont")) { diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 7f92f0413f..4f000f300c 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -404,6 +404,13 @@ export const SETTINGS: { [setting: string]: ISetting } = { controller: new ReloadOnChangeController(), default: false, }, + "feature_disable_call_per_sender_encryption": { + isFeature: true, + supportedLevels: LEVELS_FEATURE, + labsGroup: LabGroup.VoiceAndVideo, + displayName: _td("labs|feature_disable_call_per_sender_encryption"), + default: false, + }, "feature_allow_screen_share_only_mode": { isFeature: true, supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, diff --git a/test/components/views/right_panel/RoomSummaryCard-test.tsx b/test/components/views/right_panel/RoomSummaryCard-test.tsx index eef7d06ba9..7f556000f6 100644 --- a/test/components/views/right_panel/RoomSummaryCard-test.tsx +++ b/test/components/views/right_panel/RoomSummaryCard-test.tsx @@ -15,8 +15,9 @@ limitations under the License. */ import React from "react"; -import { render, fireEvent } from "@testing-library/react"; -import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; +import { render, fireEvent, screen } from "@testing-library/react"; +import { EventType, MatrixEvent, Room, MatrixClient, JoinRule } from "matrix-js-sdk/src/matrix"; +import { mocked, MockedObject } from "jest-mock"; import DMRoomMap from "../../../../src/utils/DMRoomMap"; import RoomSummaryCard from "../../../../src/components/views/right_panel/RoomSummaryCard"; @@ -28,56 +29,67 @@ import * as settingsHooks from "../../../../src/hooks/useSettings"; import Modal from "../../../../src/Modal"; import RightPanelStore from "../../../../src/stores/right-panel/RightPanelStore"; import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases"; -import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils"; +import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils"; import { PollHistoryDialog } from "../../../../src/components/views/dialogs/PollHistoryDialog"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; import { _t } from "../../../../src/languageHandler"; describe("", () => { const userId = "@alice:domain.org"; - const mockClient = getMockClientWithEventEmitter({ - ...mockClientMethodsUser(userId), - isRoomEncrypted: jest.fn(), - getRoom: jest.fn(), - }); + const roomId = "!room:domain.org"; - const room = new Room(roomId, mockClient, userId); - const roomCreateEvent = new MatrixEvent({ - type: "m.room.create", - room_id: roomId, - sender: userId, - content: { - creator: userId, - room_version: "5", - }, - state_key: "", - }); - room.currentState.setStateEvents([roomCreateEvent]); - const defaultProps = { - room, - onClose: jest.fn(), - permalinkCreator: new RoomPermalinkCreator(room), - }; - const getComponent = (props = {}) => - render(, { + let mockClient!: MockedObject; + let room!: Room; + + const getComponent = (props = {}) => { + const defaultProps = { + room, + onClose: jest.fn(), + permalinkCreator: new RoomPermalinkCreator(room), + }; + + return render(, { wrapper: ({ children }) => ( {children} ), }); - - const modalSpy = jest.spyOn(Modal, "createDialog"); - const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch"); - const rightPanelCardSpy = jest.spyOn(RightPanelStore.instance, "pushCard"); - const featureEnabledSpy = jest.spyOn(settingsHooks, "useFeatureEnabled"); + }; beforeEach(() => { + mockClient = getMockClientWithEventEmitter({ + ...mockClientMethodsUser(userId), + getAccountData: jest.fn(), + isRoomEncrypted: jest.fn(), + getOrCreateFilter: jest.fn().mockResolvedValue({ filterId: 1 }), + getRoom: jest.fn(), + }); + room = new Room(roomId, mockClient, userId); + const roomCreateEvent = new MatrixEvent({ + type: "m.room.create", + room_id: roomId, + sender: userId, + content: { + creator: userId, + room_version: "5", + }, + state_key: "", + }); + room.currentState.setStateEvents([roomCreateEvent]); + + jest.spyOn(Modal, "createDialog"); + jest.spyOn(RightPanelStore.instance, "pushCard"); + jest.spyOn(settingsHooks, "useFeatureEnabled").mockReturnValue(false); + jest.spyOn(defaultDispatcher, "dispatch"); jest.clearAllMocks(); DMRoomMap.makeShared(mockClient); mockClient.getRoom.mockReturnValue(room); jest.spyOn(room, "isElementVideoRoom").mockRestore(); jest.spyOn(room, "isCallRoom").mockRestore(); - featureEnabledSpy.mockReset().mockReturnValue(false); + }); + + afterEach(() => { + jest.restoreAllMocks(); }); it("renders the room summary", () => { @@ -101,7 +113,10 @@ describe("", () => { fireEvent.click(getByText("People")); - expect(rightPanelCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.RoomMemberList }, true); + expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith( + { phase: RightPanelPhases.RoomMemberList }, + true, + ); }); it("opens room file panel on button click", () => { @@ -109,7 +124,7 @@ describe("", () => { fireEvent.click(getByText("Files")); - expect(rightPanelCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.FilePanel }, true); + expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.FilePanel }, true); }); it("opens room export dialog on button click", () => { @@ -117,7 +132,7 @@ describe("", () => { fireEvent.click(getByText("Export chat")); - expect(modalSpy).toHaveBeenCalledWith(ExportDialog, { room }); + expect(Modal.createDialog).toHaveBeenCalledWith(ExportDialog, { room }); }); it("opens share room dialog on button click", () => { @@ -125,7 +140,7 @@ describe("", () => { fireEvent.click(getByText("Share room")); - expect(modalSpy).toHaveBeenCalledWith(ShareDialog, { target: room }); + expect(Modal.createDialog).toHaveBeenCalledWith(ShareDialog, { target: room }); }); it("opens room settings on button click", () => { @@ -133,12 +148,12 @@ describe("", () => { fireEvent.click(getByText("Room settings")); - expect(dispatchSpy).toHaveBeenCalledWith({ action: "open_room_settings" }); + expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "open_room_settings" }); }); describe("pinning", () => { it("renders pins options when pinning feature is enabled", () => { - featureEnabledSpy.mockImplementation((feature) => feature === "feature_pinning"); + mocked(settingsHooks.useFeatureEnabled).mockImplementation((feature) => feature === "feature_pinning"); const { getByText } = getComponent(); expect(getByText("Pinned")).toBeInTheDocument(); @@ -153,14 +168,15 @@ describe("", () => { }); it("opens poll history dialog on button click", () => { - const { getByText } = getComponent(); + const permalinkCreator = new RoomPermalinkCreator(room); + const { getByText } = getComponent({ permalinkCreator }); fireEvent.click(getByText("Poll history")); - expect(modalSpy).toHaveBeenCalledWith(PollHistoryDialog, { + expect(Modal.createDialog).toHaveBeenCalledWith(PollHistoryDialog, { room, matrixClient: mockClient, - permalinkCreator: defaultProps.permalinkCreator, + permalinkCreator: permalinkCreator, }); }); }); @@ -168,7 +184,7 @@ describe("", () => { describe("video rooms", () => { it("does not render irrelevant options for element video room", () => { jest.spyOn(room, "isElementVideoRoom").mockReturnValue(true); - featureEnabledSpy.mockImplementation( + mocked(settingsHooks.useFeatureEnabled).mockImplementation( (feature) => feature === "feature_video_rooms" || feature === "feature_pinning", ); const { queryByText } = getComponent(); @@ -181,7 +197,7 @@ describe("", () => { it("does not render irrelevant options for element call room", () => { jest.spyOn(room, "isCallRoom").mockReturnValue(true); - featureEnabledSpy.mockImplementation( + mocked(settingsHooks.useFeatureEnabled).mockImplementation( (feature) => feature === "feature_element_call_video_rooms" || feature === "feature_video_rooms" || @@ -195,4 +211,49 @@ describe("", () => { expect(queryByText("Export chat")).not.toBeInTheDocument(); }); }); + + describe("public room label", () => { + beforeEach(() => { + jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Public); + }); + + it("does not show public room label for a DM", async () => { + mockClient.getAccountData.mockImplementation( + (eventType) => + ({ + [EventType.Direct]: new MatrixEvent({ + type: EventType.Direct, + content: { + "@bob:sesame.st": ["some-room-id"], + // this room is a DM with ernie + "@ernie:sesame.st": ["some-other-room-id", room.roomId], + }, + }), + }[eventType]), + ); + getComponent(); + + await flushPromises(); + + expect(screen.queryByText("Public room")).not.toBeInTheDocument(); + }); + + it("does not show public room label for non public room", async () => { + jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Invite); + getComponent(); + + await flushPromises(); + + expect(screen.queryByText("Public room")).not.toBeInTheDocument(); + }); + + it("shows a public room label for a public room", async () => { + jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Public); + getComponent(); + + await flushPromises(); + + expect(screen.queryByText("Public room")).toBeInTheDocument(); + }); + }); }); 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 8c5891b576..9e81528f1c 100644 --- a/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap +++ b/test/components/views/right_panel/__snapshots__/RoomSummaryCard-test.tsx.snap @@ -35,36 +35,40 @@ exports[` renders the room summary 1`] = `
-
{ call.destroy(); expect(destroyPersistentWidgetSpy).toHaveBeenCalled(); }); + + it("the perParticipantE2EE url flag is used in encrypted rooms while respecting the feature_disable_call_per_sender_encryption flag", async () => { + // We destroy the call created in beforeEach because we test the call creation process. + call.destroy(); + const addWidgetSpy = jest.spyOn(WidgetStore.instance, "addVirtualWidget"); + // If a room is not encrypted we will never add the perParticipantE2EE flag. + client.isRoomEncrypted.mockReturnValue(true); + + // should create call with perParticipantE2EE flag + ElementCall.create(room); + + expect(addWidgetSpy.mock.calls[0][0].url).toContain("perParticipantE2EE=true"); + ElementCall.get(room)?.destroy(); + + // should create call without perParticipantE2EE flag + enabledSettings.add("feature_disable_call_per_sender_encryption"); + await ElementCall.create(room); + enabledSettings.delete("feature_disable_call_per_sender_encryption"); + + expect(addWidgetSpy.mock.calls[1][0].url).not.toContain("perParticipantE2EE=true"); + + client.isRoomEncrypted.mockClear(); + addWidgetSpy.mockRestore(); + }); }); describe("instance in a video room", () => { diff --git a/yarn.lock b/yarn.lock index 32b191e167..743c2b68de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1799,10 +1799,10 @@ emojibase "^15.0.0" emojibase-data "^15.0.0" -"@matrix-org/matrix-sdk-crypto-wasm@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-2.2.0.tgz#7c60afe01915281a6b71502821bc8e01afbfa70d" - integrity sha512-txmvaTiZpVV0/kWCRcE7tZvRESCEc1ynLJDVh9OUsFlaXfl13c7qdD3E6IJEJ8YiPMIn+PHogdfBZsO84reaMg== +"@matrix-org/matrix-sdk-crypto-wasm@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-3.0.1.tgz#56a0376f8a389264bcf4d5325b378a71f18b7664" + integrity sha512-r0PBfUKlLHm67+fpIV21netX5+DujbY2XjJy7JUGJ55oW4XWBNbSf9vElfaQkrdt/iDscL/8I5PoD5lCuVW6zA== "@matrix-org/matrix-wysiwyg@2.4.1": version "2.4.1" @@ -7565,11 +7565,11 @@ matrix-events-sdk@0.0.1: integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA== "matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "30.0.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/107e28e1145c8b2667701e1f75b9f09b5d2ac3d6" + version "30.0.1" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/76f993e7ff72768ba1769f6893712ec9a4f4a0c2" dependencies: "@babel/runtime" "^7.12.5" - "@matrix-org/matrix-sdk-crypto-wasm" "^2.2.0" + "@matrix-org/matrix-sdk-crypto-wasm" "^3.0.1" another-json "^0.2.0" bs58 "^5.0.0" content-type "^1.0.4"