diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index c8fbfd9009..59e904ee0e 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -111,7 +111,7 @@ jobs: path: webapp - name: Run Cypress tests - uses: cypress-io/github-action@v4.2.2 + uses: cypress-io/github-action@v5.0.2 with: # The built-in Electron runner seems to grind to a halt trying # to run the tests, so use chrome. diff --git a/__mocks__/maplibre-gl.js b/__mocks__/maplibre-gl.js index 0398a1df79..77ee0e9a02 100644 --- a/__mocks__/maplibre-gl.js +++ b/__mocks__/maplibre-gl.js @@ -28,6 +28,7 @@ class MockMap extends EventEmitter { } const MockMapInstance = new MockMap(); +class MockAttributionControl {} class MockGeolocateControl extends EventEmitter { trigger = jest.fn(); } @@ -43,5 +44,5 @@ module.exports = { LngLat, LngLatBounds, NavigationControl, - AttributionControl, + AttributionControl: MockAttributionControl, }; diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index eb78828ff2..eb0e762829 100644 --- a/cypress/e2e/crypto/crypto.spec.ts +++ b/cypress/e2e/crypto/crypto.spec.ts @@ -158,8 +158,8 @@ describe("Cryptography", function () { cy.startSynapse("default") .as("synapse") .then((synapse: SynapseInstance) => { - cy.initTestUser(synapse, "Alice"); - cy.getBot(synapse, { displayName: "Bob", autoAcceptInvites: false }).as("bob"); + cy.initTestUser(synapse, "Alice", undefined, "alice_"); + cy.getBot(synapse, { displayName: "Bob", autoAcceptInvites: false, userIdPrefix: "bob_" }).as("bob"); }); }); diff --git a/cypress/e2e/settings/device-management.spec.ts b/cypress/e2e/settings/device-management.spec.ts index ca3385fcb2..32e99faf77 100644 --- a/cypress/e2e/settings/device-management.spec.ts +++ b/cypress/e2e/settings/device-management.spec.ts @@ -104,7 +104,7 @@ describe("Device manager", () => { cy.get(".mx_Spinner").should("not.exist"); // session name updated in details - cy.get(".mx_DeviceDetailHeading h3").should("have.text", sessionName); + cy.get(".mx_DeviceDetailHeading h4").should("have.text", sessionName); // and main list item cy.get(".mx_DeviceTile h4").should("have.text", sessionName); diff --git a/cypress/support/bot.ts b/cypress/support/bot.ts index 40e0986d16..750cd566bb 100644 --- a/cypress/support/bot.ts +++ b/cypress/support/bot.ts @@ -22,6 +22,10 @@ import { Credentials } from "./synapse"; import Chainable = Cypress.Chainable; interface CreateBotOpts { + /** + * A prefix to use for the userid. If unspecified, "bot_" will be used. + */ + userIdPrefix?: string; /** * Whether the bot should automatically accept all invites. */ @@ -41,6 +45,7 @@ interface CreateBotOpts { } const defaultCreateBotOptions = { + userIdPrefix: "bot_", autoAcceptInvites: true, startClient: true, bootstrapCrossSigning: true, @@ -157,7 +162,7 @@ function setupBotClient( Cypress.Commands.add("getBot", (synapse: SynapseInstance, opts: CreateBotOpts): Chainable => { opts = Object.assign({}, defaultCreateBotOptions, opts); - const username = Cypress._.uniqueId("userId_"); + const username = Cypress._.uniqueId(opts.userIdPrefix); const password = Cypress._.uniqueId("password_"); return cy .registerUser(synapse, username, password, opts.displayName) diff --git a/cypress/support/login.ts b/cypress/support/login.ts index 73300f7905..338da2f9db 100644 --- a/cypress/support/login.ts +++ b/cypress/support/login.ts @@ -37,11 +37,14 @@ declare global { * @param synapse the synapse returned by startSynapse * @param displayName the displayName to give the test user * @param prelaunchFn optional function to run before the app is visited + * @param userIdPrefix optional prefix to use for the generated user id. If unspecified, `user_` will be + * useed. */ initTestUser( synapse: SynapseInstance, displayName: string, prelaunchFn?: () => void, + userIdPrefix?: string, ): Chainable; /** * Logs into synapse with the given username/password @@ -91,7 +94,12 @@ Cypress.Commands.add( // eslint-disable-next-line max-len Cypress.Commands.add( "initTestUser", - (synapse: SynapseInstance, displayName: string, prelaunchFn?: () => void): Chainable => { + ( + synapse: SynapseInstance, + displayName: string, + prelaunchFn?: () => void, + userIdPrefix = "user_", + ): Chainable => { // XXX: work around Cypress not clearing IDB between tests cy.window({ log: false }).then((win) => { win.indexedDB.databases()?.then((databases) => { @@ -101,7 +109,7 @@ Cypress.Commands.add( }); }); - const username = Cypress._.uniqueId("userId_"); + const username = Cypress._.uniqueId(userIdPrefix); const password = Cypress._.uniqueId("password_"); return cy .registerUser(synapse, username, password, displayName) diff --git a/package.json b/package.json index 01bb51aa8c..c2b23773ad 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "linkify-string": "4.0.0-beta.4", "linkifyjs": "4.0.0-beta.4", "lodash": "^4.17.20", - "maplibre-gl": "^1.15.2", + "maplibre-gl": "^2.0.0", "matrix-encrypt-attachment": "^1.0.3", "matrix-events-sdk": "0.0.1", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", diff --git a/res/css/components/views/elements/_FilterDropdown.pcss b/res/css/components/views/elements/_FilterDropdown.pcss index 98808a8b1a..6a9fe3dc7c 100644 --- a/res/css/components/views/elements/_FilterDropdown.pcss +++ b/res/css/components/views/elements/_FilterDropdown.pcss @@ -20,24 +20,29 @@ limitations under the License. left: unset; right: -$spacing-12; width: 232px; + padding: $spacing-12; border: 1px solid $quinary-content; border-radius: 8px; box-shadow: 0px 1px 3px rgba(23, 25, 28, 0.05); + background-color: $system; + .mx_Dropdown_option_highlight { - background-color: $system; + background-color: transparent; } } .mx_Dropdown_input { height: 24px; - background-color: $quinary-content; - border-color: $quinary-content; + background-color: transparent; + border-color: transparent; color: $secondary-content; border-radius: 4px; - &:focus { + &:focus, + &:hover { + background-color: $quinary-content; border-color: $quinary-content; } } diff --git a/res/css/components/views/settings/devices/_DeviceDetails.pcss b/res/css/components/views/settings/devices/_DeviceDetails.pcss index 754ee43998..8c4aac6cbf 100644 --- a/res/css/components/views/settings/devices/_DeviceDetails.pcss +++ b/res/css/components/views/settings/devices/_DeviceDetails.pcss @@ -22,18 +22,18 @@ limitations under the License. width: 100%; margin-top: $spacing-16; - padding: $spacing-16; + padding: $spacing-24; border-radius: 8px; border: 1px solid $quinary-content; } .mx_DeviceDetails_section { - padding-bottom: $spacing-16; - margin-bottom: $spacing-16; + padding-bottom: $spacing-20; + margin-bottom: $spacing-20; border-bottom: 1px solid $quinary-content; display: grid; - grid-gap: $spacing-16; + grid-gap: $spacing-24; justify-content: left; grid-template-columns: 100%; @@ -52,6 +52,7 @@ limitations under the License. font-size: $font-12px; color: $secondary-content; line-height: $font-14px; + margin-top: $spacing-4; } } diff --git a/res/css/components/views/settings/devices/_DeviceExpandDetailsButton.pcss b/res/css/components/views/settings/devices/_DeviceExpandDetailsButton.pcss index 4c9d787fdb..b8972d6227 100644 --- a/res/css/components/views/settings/devices/_DeviceExpandDetailsButton.pcss +++ b/res/css/components/views/settings/devices/_DeviceExpandDetailsButton.pcss @@ -23,17 +23,25 @@ limitations under the License. color: $secondary-content; --icon-transform: rotate(-90deg); + + &:hover { + background: $quinary-content; + } } .mx_DeviceExpandDetailsButton.mx_DeviceExpandDetailsButton_expanded { --icon-transform: rotate(0deg); background: $system; + + &:hover { + background: $quinary-content; + } } .mx_DeviceExpandDetailsButton_icon { - height: 12px; - width: 12px; + height: 16px; + width: 16px; transition: all 0.3s; transform: var(--icon-transform); diff --git a/res/css/components/views/settings/devices/_DeviceTypeIcon.pcss b/res/css/components/views/settings/devices/_DeviceTypeIcon.pcss index a092112d8a..5a5937b151 100644 --- a/res/css/components/views/settings/devices/_DeviceTypeIcon.pcss +++ b/res/css/components/views/settings/devices/_DeviceTypeIcon.pcss @@ -55,7 +55,7 @@ limitations under the License. box-sizing: border-box; padding: $spacing-4; - border: 1px solid $system; + border: 1px solid $quinary-content; border-radius: 50%; background-color: $background; diff --git a/res/css/components/views/settings/shared/_SettingsSubsection.pcss b/res/css/components/views/settings/shared/_SettingsSubsection.pcss index 2ba909aac1..a2b3cd35bf 100644 --- a/res/css/components/views/settings/shared/_SettingsSubsection.pcss +++ b/res/css/components/views/settings/shared/_SettingsSubsection.pcss @@ -23,7 +23,7 @@ limitations under the License. width: 100%; box-sizing: inherit; line-height: $font-24px; - margin-bottom: $spacing-32; + margin-bottom: $spacing-24; color: $secondary-content; } diff --git a/res/css/structures/_ToastContainer.pcss b/res/css/structures/_ToastContainer.pcss index a5d4a629e9..9a1e3b5e19 100644 --- a/res/css/structures/_ToastContainer.pcss +++ b/res/css/structures/_ToastContainer.pcss @@ -77,7 +77,7 @@ limitations under the License. &::after { mask-image: url("$(res)/img/e2e/warning.svg"); - background-color: $alert; + background-color: $e2e-warning-color; } } diff --git a/res/css/views/messages/_common_CryptoEvent.pcss b/res/css/views/messages/_common_CryptoEvent.pcss index 681c117b22..41d9e378fc 100644 --- a/res/css/views/messages/_common_CryptoEvent.pcss +++ b/res/css/views/messages/_common_CryptoEvent.pcss @@ -38,7 +38,7 @@ limitations under the License. &.mx_cryptoEvent_icon_warning::after { mask-image: url("$(res)/img/e2e/warning.svg"); - background-color: $alert; + background-color: $e2e-warning-color; } .mx_cryptoEvent_state, diff --git a/res/css/views/right_panel/_RoomSummaryCard.pcss b/res/css/views/right_panel/_RoomSummaryCard.pcss index 69ccb98d70..9e5f2ce4e8 100644 --- a/res/css/views/right_panel/_RoomSummaryCard.pcss +++ b/res/css/views/right_panel/_RoomSummaryCard.pcss @@ -75,14 +75,14 @@ limitations under the License. } .mx_RoomSummaryCard_e2ee_verified { - background-color: #0dbd8b; + background-color: #$e2e-verified-color; &::before { mask-image: url("$(res)/img/e2e/verified.svg"); } } .mx_RoomSummaryCard_e2ee_warning { - background-color: #ff5b55; + background-color: $e2e-warning-color; &::before { mask-image: url("$(res)/img/e2e/warning.svg"); } diff --git a/res/css/views/rooms/_DecryptionFailureBar.pcss b/res/css/views/rooms/_DecryptionFailureBar.pcss index 06a51d2664..1369d599e3 100644 --- a/res/css/views/rooms/_DecryptionFailureBar.pcss +++ b/res/css/views/rooms/_DecryptionFailureBar.pcss @@ -29,7 +29,7 @@ limitations under the License. width: 24px; height: 24px; mask-image: url("$(res)/img/e2e/decryption-failure.svg"); - background-color: $alert; + background-color: $e2e-warning-color; mask-repeat: no-repeat; mask-position: center; mask-size: contain; diff --git a/res/css/views/rooms/_E2EIcon.pcss b/res/css/views/rooms/_E2EIcon.pcss index 5ae6de07f5..2d23585f54 100644 --- a/res/css/views/rooms/_E2EIcon.pcss +++ b/res/css/views/rooms/_E2EIcon.pcss @@ -66,7 +66,7 @@ limitations under the License. .mx_E2EIcon_warning::after { mask-image: url("$(res)/img/e2e/warning.svg"); - background-color: $alert; + background-color: $e2e-warning-color; } .mx_E2EIcon_normal::after { @@ -76,5 +76,5 @@ limitations under the License. .mx_E2EIcon_verified::after { mask-image: url("$(res)/img/e2e/verified.svg"); - background-color: $accent; + background-color: $e2e-verified-color; } diff --git a/res/css/views/rooms/_EventTile.pcss b/res/css/views/rooms/_EventTile.pcss index 932e1d7c23..9932fcfd9f 100644 --- a/res/css/views/rooms/_EventTile.pcss +++ b/res/css/views/rooms/_EventTile.pcss @@ -678,7 +678,7 @@ $left-gutter: 64px; &.mx_EventTile_e2eIcon_warning::after { mask-image: url("$(res)/img/e2e/warning.svg"); - background-color: $alert; + background-color: $e2e-warning-color; } &.mx_EventTile_e2eIcon_normal::after { diff --git a/res/themes/legacy-light/css/_legacy-light.pcss b/res/themes/legacy-light/css/_legacy-light.pcss index 32ad2a86a0..d5d7b4efc2 100644 --- a/res/themes/legacy-light/css/_legacy-light.pcss +++ b/res/themes/legacy-light/css/_legacy-light.pcss @@ -215,10 +215,10 @@ $event-timestamp-color: #acacac; $copy-button-url: "$(res)/img/element-icons/copy.svg"; /* e2e */ -$e2e-verified-color: #76cfa5; /* N.B. *NOT* the same as $accent */ +$e2e-verified-color: #0dbd8b; $e2e-unknown-color: #e8bf37; $e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; +$e2e-warning-color: #ff5b55; $e2e-verified-color-light: rgba($e2e-verified-color, 0.06); $e2e-warning-color-light: rgba($e2e-warning-color, 0.06); diff --git a/res/themes/light/css/_light.pcss b/res/themes/light/css/_light.pcss index e7bfa1e328..a88bacaac9 100644 --- a/res/themes/light/css/_light.pcss +++ b/res/themes/light/css/_light.pcss @@ -183,10 +183,10 @@ $roomtile-default-badge-bg-color: $muted-fg-color; /* e2e */ /* ******************** */ -$e2e-verified-color: #76cfa5; /* N.B. *NOT* the same as $accent */ +$e2e-verified-color: #0dbd8b; $e2e-unknown-color: #e8bf37; $e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; +$e2e-warning-color: #ff5b55; $e2e-verified-color-light: rgba($e2e-verified-color, 0.06); $e2e-warning-color-light: rgba($e2e-warning-color, 0.06); /* ******************** */ diff --git a/src/IConfigOptions.ts b/src/IConfigOptions.ts index b7b0761ebe..fffa3fbb9f 100644 --- a/src/IConfigOptions.ts +++ b/src/IConfigOptions.ts @@ -191,8 +191,6 @@ export interface IConfigOptions { description: string; show_once?: boolean; }; - - use_rust_crypto_sdk?: boolean; } export interface ISsoRedirectOptions { diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index 847cd38aef..d81bd73661 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -63,6 +63,8 @@ import { OpenInviteDialogPayload } from "./dispatcher/payloads/OpenInviteDialogP import { findDMForUser } from "./utils/dm/findDMForUser"; import { getJoinedNonFunctionalMembers } from "./utils/room/getJoinedNonFunctionalMembers"; import { localNotificationsAreSilenced } from "./utils/notifications"; +import { SdkContextClass } from "./contexts/SDKContext"; +import { showCantStartACallDialog } from "./voice-broadcast/utils/showCantStartACallDialog"; export const PROTOCOL_PSTN = "m.protocol.pstn"; export const PROTOCOL_PSTN_PREFIXED = "im.vector.protocol.pstn"; @@ -932,6 +934,15 @@ export default class LegacyCallHandler extends EventEmitter { } public async placeCall(roomId: string, type?: CallType, transferee?: MatrixCall): Promise { + // Pause current broadcast, if any + SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.pause(); + + if (SdkContextClass.instance.voiceBroadcastRecordingsStore.getCurrent()) { + // Do not start a call, if recording a broadcast + showCantStartACallDialog(); + return; + } + // We might be using managed hybrid widgets if (isManagedHybridWidgetEnabled()) { await addManagedHybridWidget(roomId); diff --git a/src/components/views/beacon/BeaconMarker.tsx b/src/components/views/beacon/BeaconMarker.tsx index e8c36bd6f0..3d712e5828 100644 --- a/src/components/views/beacon/BeaconMarker.tsx +++ b/src/components/views/beacon/BeaconMarker.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React, { ReactNode, useContext } from "react"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { Beacon, BeaconEvent } from "matrix-js-sdk/src/matrix"; import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; diff --git a/src/components/views/beacon/BeaconViewDialog.tsx b/src/components/views/beacon/BeaconViewDialog.tsx index c2a836a123..998c2f3668 100644 --- a/src/components/views/beacon/BeaconViewDialog.tsx +++ b/src/components/views/beacon/BeaconViewDialog.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { useState, useEffect } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Beacon, Room } from "matrix-js-sdk/src/matrix"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { Icon as LiveLocationIcon } from "../../../../res/img/location/live-location.svg"; import { useLiveBeacons } from "../../../utils/beacon/useLiveBeacons"; diff --git a/src/components/views/location/LocationPicker.tsx b/src/components/views/location/LocationPicker.tsx index 363bbe9767..b56df479ad 100644 --- a/src/components/views/location/LocationPicker.tsx +++ b/src/components/views/location/LocationPicker.tsx @@ -137,7 +137,7 @@ class LocationPicker extends React.Component { private addMarkerToMap = () => { this.marker = new maplibregl.Marker({ - element: document.getElementById(this.getMarkerId()), + element: document.getElementById(this.getMarkerId()) ?? undefined, anchor: "bottom", offset: [0, -1], }) diff --git a/src/components/views/location/Map.tsx b/src/components/views/location/Map.tsx index 2920cb1058..5217d97cdb 100644 --- a/src/components/views/location/Map.tsx +++ b/src/components/views/location/Map.tsx @@ -16,7 +16,7 @@ limitations under the License. import React, { ReactNode, useContext, useEffect } from "react"; import classNames from "classnames"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { ClientEvent, IClientWellKnown } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; diff --git a/src/components/views/location/SmartMarker.tsx b/src/components/views/location/SmartMarker.tsx index 13919f3f66..225cfbdcdc 100644 --- a/src/components/views/location/SmartMarker.tsx +++ b/src/components/views/location/SmartMarker.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React, { ReactNode, useCallback, useEffect, useState } from "react"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { RoomMember } from "matrix-js-sdk/src/matrix"; import { createMarker, parseGeoUri } from "../../../utils/location"; diff --git a/src/components/views/location/ZoomButtons.tsx b/src/components/views/location/ZoomButtons.tsx index 1a47d8984b..84931ff589 100644 --- a/src/components/views/location/ZoomButtons.tsx +++ b/src/components/views/location/ZoomButtons.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; diff --git a/src/components/views/settings/devices/CurrentDeviceSection.tsx b/src/components/views/settings/devices/CurrentDeviceSection.tsx index 461a92f2ac..9044f882ec 100644 --- a/src/components/views/settings/devices/CurrentDeviceSection.tsx +++ b/src/components/views/settings/devices/CurrentDeviceSection.tsx @@ -115,7 +115,7 @@ const CurrentDeviceSection: React.FC = ({ onClick={() => setIsExpanded(!isExpanded)} /> - {isExpanded && ( + {isExpanded ? ( = ({ onSignOutDevice={onSignOutCurrentDevice} saveDeviceName={saveDeviceName} /> + ) : ( + <> +
+ + )} -
- )} diff --git a/src/components/views/settings/devices/DeviceDetailHeading.tsx b/src/components/views/settings/devices/DeviceDetailHeading.tsx index d23ec871b5..a59a3edcf6 100644 --- a/src/components/views/settings/devices/DeviceDetailHeading.tsx +++ b/src/components/views/settings/devices/DeviceDetailHeading.tsx @@ -134,7 +134,7 @@ export const DeviceDetailHeading: React.FC = ({ device, saveDeviceName }) setIsEditing(false)} /> ) : (
- {device.display_name || device.device_id} + {device.display_name || device.device_id} setIsEditing(true)} diff --git a/src/components/views/settings/devices/FilteredDeviceListHeader.tsx b/src/components/views/settings/devices/FilteredDeviceListHeader.tsx index 2f7d420bb4..ffbe8a2b6c 100644 --- a/src/components/views/settings/devices/FilteredDeviceListHeader.tsx +++ b/src/components/views/settings/devices/FilteredDeviceListHeader.tsx @@ -50,7 +50,7 @@ const FilteredDeviceListHeader: React.FC = ({ {selectedDeviceCount > 0 - ? _t("%(selectedDeviceCount)s sessions selected", { selectedDeviceCount }) + ? _t("%(count)s sessions selected", { count: selectedDeviceCount }) : _t("Sessions")} {children} diff --git a/src/components/views/settings/devices/SecurityRecommendations.tsx b/src/components/views/settings/devices/SecurityRecommendations.tsx index a5e3a545d8..2805f0e7cb 100644 --- a/src/components/views/settings/devices/SecurityRecommendations.tsx +++ b/src/components/views/settings/devices/SecurityRecommendations.tsx @@ -53,7 +53,7 @@ const SecurityRecommendations: React.FC = ({ devices, currentDeviceId, go return ( {!!unverifiedDevicesCount && ( @@ -89,7 +89,7 @@ const SecurityRecommendations: React.FC = ({ devices, currentDeviceId, go <> {_t( `Consider signing out from old sessions ` + - `(%(inactiveAgeDays)s days or older) you don't use anymore`, + `(%(inactiveAgeDays)s days or older) you don't use anymore.`, { inactiveAgeDays }, )} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8321a925ef..306e048355 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -648,6 +648,8 @@ "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.": "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.", "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.": "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.", "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.": "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.", + "Can’t start a call": "Can’t start a call", + "You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.": "You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.", "You ended a voice broadcast": "You ended a voice broadcast", "%(senderName)s ended a voice broadcast": "%(senderName)s ended a voice broadcast", "You ended a voice broadcast": "You ended a voice broadcast", @@ -1005,8 +1007,6 @@ "Enable URL previews by default for participants in this room": "Enable URL previews by default for participants in this room", "Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets", "Prompt before sending invites to potentially invalid matrix IDs": "Prompt before sending invites to potentially invalid matrix IDs", - "Order rooms by name": "Order rooms by name", - "Show rooms with unread notifications first": "Show rooms with unread notifications first", "Show shortcuts to recently viewed rooms above the room list": "Show shortcuts to recently viewed rooms above the room list", "Show shortcut to welcome checklist above the room list": "Show shortcut to welcome checklist above the room list", "Show hidden events in timeline": "Show hidden events in timeline", @@ -1835,14 +1835,14 @@ "Inactive for %(inactiveAgeDays)s days or longer": "Inactive for %(inactiveAgeDays)s days or longer", "Filter devices": "Filter devices", "Show": "Show", - "%(selectedDeviceCount)s sessions selected": "%(selectedDeviceCount)s sessions selected", + "%(count)s sessions selected|other": "%(count)s sessions selected", + "%(count)s sessions selected|one": "%(count)s session selected", "Sign in with QR code": "Sign in with QR code", "You can use this device to sign in a new device with a QR code. You will need to scan the QR code shown on this device with your device that's signed out.": "You can use this device to sign in a new device with a QR code. You will need to scan the QR code shown on this device with your device that's signed out.", "Show QR code": "Show QR code", "Security recommendations": "Security recommendations", - "Improve your account security by following these recommendations": "Improve your account security by following these recommendations", + "Improve your account security by following these recommendations.": "Improve your account security by following these recommendations.", "View all": "View all", - "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore": "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore", "Failed to set pusher state": "Failed to set pusher state", "Unable to remove contact information": "Unable to remove contact information", "Remove %(email)s?": "Remove %(email)s?", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 00e62f733c..ae0cd5b30f 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -885,18 +885,6 @@ export const SETTINGS: { [setting: string]: ISetting } = { deny: [], }, }, - // TODO: Remove setting: https://github.com/vector-im/element-web/issues/14373 - "RoomList.orderAlphabetically": { - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td("Order rooms by name"), - default: false, - }, - // TODO: Remove setting: https://github.com/vector-im/element-web/issues/14373 - "RoomList.orderByImportance": { - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td("Show rooms with unread notifications first"), - default: true, - }, "breadcrumbs": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td("Show shortcuts to recently viewed rooms above the room list"), diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 84312fbf26..0d04bd1597 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -56,6 +56,7 @@ import { doMaybeSetCurrentVoiceBroadcastPlayback, } from "../voice-broadcast"; import { IRoomStateEventsActionPayload } from "../actions/MatrixActionCreators"; +import { showCantStartACallDialog } from "../voice-broadcast/utils/showCantStartACallDialog"; const NUM_JOIN_RETRY = 5; @@ -180,6 +181,16 @@ export class RoomViewStore extends EventEmitter { return; } + if (newState.viewingCall) { + // Pause current broadcast, if any + this.stores.voiceBroadcastPlaybacksStore.getCurrent()?.pause(); + + if (this.stores.voiceBroadcastRecordingsStore.getCurrent()) { + showCantStartACallDialog(); + newState.viewingCall = false; + } + } + const lastRoomId = this.state.roomId; this.state = Object.assign(this.state, newState); if (lastRoomId !== this.state.roomId) { diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index e045166e7c..78bbfb2e10 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -16,7 +16,6 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; -import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; import { logger } from "matrix-js-sdk/src/logger"; import { EventType } from "matrix-js-sdk/src/@types/event"; @@ -387,20 +386,15 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // logic must match calculateListOrder private calculateTagSorting(tagId: TagID): SortAlgorithm { - const isDefaultRecent = tagId === DefaultTagID.Invite || tagId === DefaultTagID.DM; - const defaultSort = isDefaultRecent ? SortAlgorithm.Recent : SortAlgorithm.Alphabetic; - const settingAlphabetical = SettingsStore.getValue("RoomList.orderAlphabetically", null, true); const definedSort = this.getTagSorting(tagId); const storedSort = this.getStoredTagSorting(tagId); // We use the following order to determine which of the 4 flags to use: // Stored > Settings > Defined > Default - let tagSort = defaultSort; + let tagSort = SortAlgorithm.Recent; if (storedSort) { tagSort = storedSort; - } else if (!isNullOrUndefined(settingAlphabetical)) { - tagSort = settingAlphabetical ? SortAlgorithm.Alphabetic : SortAlgorithm.Recent; } else if (definedSort) { tagSort = definedSort; } // else default (already set) @@ -431,8 +425,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // logic must match calculateTagSorting private calculateListOrder(tagId: TagID): ListAlgorithm { - const defaultOrder = ListAlgorithm.Natural; - const settingImportance = SettingsStore.getValue("RoomList.orderByImportance", null, true); + const defaultOrder = ListAlgorithm.Importance; const definedOrder = this.getListOrder(tagId); const storedOrder = this.getStoredListOrder(tagId); @@ -442,8 +435,6 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements let listOrder = defaultOrder; if (storedOrder) { listOrder = storedOrder; - } else if (!isNullOrUndefined(settingImportance)) { - listOrder = settingImportance ? ListAlgorithm.Importance : ListAlgorithm.Natural; } else if (definedOrder) { listOrder = definedOrder; } // else default (already set) diff --git a/src/utils/location/map.ts b/src/utils/location/map.ts index 861515eb77..a43b6d8034 100644 --- a/src/utils/location/map.ts +++ b/src/utils/location/map.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { M_LOCATION } from "matrix-js-sdk/src/@types/location"; import { logger } from "matrix-js-sdk/src/logger"; diff --git a/src/voice-broadcast/models/VoiceBroadcastPlayback.ts b/src/voice-broadcast/models/VoiceBroadcastPlayback.ts index 9ec50e094d..412deac3a4 100644 --- a/src/voice-broadcast/models/VoiceBroadcastPlayback.ts +++ b/src/voice-broadcast/models/VoiceBroadcastPlayback.ts @@ -388,6 +388,7 @@ export class VoiceBroadcastPlayback public stop(): void { this.setState(VoiceBroadcastPlaybackState.Stopped); + this.getCurrentPlayback()?.stop(); this.currentlyPlaying = null; this.setPosition(0); } @@ -397,7 +398,6 @@ export class VoiceBroadcastPlayback if (this.getState() === VoiceBroadcastPlaybackState.Stopped) return; this.setState(VoiceBroadcastPlaybackState.Paused); - if (!this.currentlyPlaying) return; this.getCurrentPlayback()?.pause(); } diff --git a/src/voice-broadcast/utils/showCantStartACallDialog.tsx b/src/voice-broadcast/utils/showCantStartACallDialog.tsx new file mode 100644 index 0000000000..d2c7865796 --- /dev/null +++ b/src/voice-broadcast/utils/showCantStartACallDialog.tsx @@ -0,0 +1,36 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; + +import InfoDialog from "../../components/views/dialogs/InfoDialog"; +import { _t } from "../../languageHandler"; +import Modal from "../../Modal"; + +export const showCantStartACallDialog = () => { + Modal.createDialog(InfoDialog, { + title: _t("Can’t start a call"), + description: ( +

+ {_t( + "You can’t start a call as you are currently recording a live broadcast. " + + "Please end your live broadcast in order to start a call.", + )} +

+ ), + hasCloseButton: true, + }); +}; diff --git a/test/LegacyCallHandler-test.ts b/test/LegacyCallHandler-test.ts index 474fb7d070..91d8a602a8 100644 --- a/test/LegacyCallHandler-test.ts +++ b/test/LegacyCallHandler-test.ts @@ -19,6 +19,7 @@ import { LOCAL_NOTIFICATION_SETTINGS_PREFIX, MatrixEvent, PushRuleKind, + Room, RuleId, TweakName, } from "matrix-js-sdk/src/matrix"; @@ -43,6 +44,28 @@ import { Action } from "../src/dispatcher/actions"; import { getFunctionalMembers } from "../src/utils/room/getFunctionalMembers"; import SettingsStore from "../src/settings/SettingsStore"; import { UIFeature } from "../src/settings/UIFeature"; +import { VoiceBroadcastInfoState, VoiceBroadcastPlayback, VoiceBroadcastRecording } from "../src/voice-broadcast"; +import { mkVoiceBroadcastInfoStateEvent } from "./voice-broadcast/utils/test-utils"; +import { SdkContextClass } from "../src/contexts/SDKContext"; +import Modal from "../src/Modal"; + +jest.mock("../src/Modal"); + +// mock VoiceRecording because it contains all the audio APIs +jest.mock("../src/audio/VoiceRecording", () => ({ + VoiceRecording: jest.fn().mockReturnValue({ + disableMaxLength: jest.fn(), + liveData: { + onUpdate: jest.fn(), + }, + off: jest.fn(), + on: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + destroy: jest.fn(), + contentType: "audio/ogg", + }), +})); jest.mock("../src/utils/room/getFunctionalMembers", () => ({ getFunctionalMembers: jest.fn(), @@ -71,7 +94,7 @@ const VIRTUAL_ROOM_BOB = "$virtual_bob_room:example.org"; // Bob's phone number const BOB_PHONE_NUMBER = "01818118181"; -function mkStubDM(roomId, userId) { +function mkStubDM(roomId: string, userId: string) { const room = mkStubRoom(roomId, "room", MatrixClientPeg.get()); room.getJoinedMembers = jest.fn().mockReturnValue([ { @@ -134,23 +157,24 @@ function untilCallHandlerEvent(callHandler: LegacyCallHandler, event: LegacyCall describe("LegacyCallHandler", () => { let dmRoomMap; - let callHandler; + let callHandler: LegacyCallHandler; let audioElement: HTMLAudioElement; - let fakeCall; + let fakeCall: MatrixCall | null; // what addresses the app has looked up via pstn and native lookup - let pstnLookup: string; - let nativeLookup: string; + let pstnLookup: string | null; + let nativeLookup: string | null; const deviceId = "my-device"; beforeEach(async () => { stubClient(); - MatrixClientPeg.get().createCall = (roomId) => { + fakeCall = null; + MatrixClientPeg.get().createCall = (roomId: string): MatrixCall | null => { if (fakeCall && fakeCall.roomId !== roomId) { throw new Error("Only one call is supported!"); } - fakeCall = new FakeCall(roomId); - return fakeCall; + fakeCall = new FakeCall(roomId) as unknown as MatrixCall; + return fakeCall as unknown as MatrixCall; }; MatrixClientPeg.get().deviceId = deviceId; @@ -172,7 +196,7 @@ describe("LegacyCallHandler", () => { const nativeRoomCharie = mkStubDM(NATIVE_ROOM_CHARLIE, NATIVE_CHARLIE); const virtualBobRoom = mkStubDM(VIRTUAL_ROOM_BOB, VIRTUAL_BOB); - MatrixClientPeg.get().getRoom = (roomId) => { + MatrixClientPeg.get().getRoom = (roomId: string): Room | null => { switch (roomId) { case NATIVE_ROOM_ALICE: return nativeRoomAlice; @@ -183,6 +207,8 @@ describe("LegacyCallHandler", () => { case VIRTUAL_ROOM_BOB: return virtualBobRoom; } + + return null; }; dmRoomMap = { @@ -212,13 +238,13 @@ describe("LegacyCallHandler", () => { return []; } }, - }; + } as DMRoomMap; DMRoomMap.setShared(dmRoomMap); pstnLookup = null; nativeLookup = null; - MatrixClientPeg.get().getThirdpartyUser = (proto, params) => { + MatrixClientPeg.get().getThirdpartyUser = (proto: string, params: any) => { if ([PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED].includes(proto)) { pstnLookup = params["m.id.phone"]; return Promise.resolve([ @@ -261,6 +287,8 @@ describe("LegacyCallHandler", () => { } return Promise.resolve([]); } + + return Promise.resolve([]); }; audioElement = document.createElement("audio"); @@ -270,10 +298,10 @@ describe("LegacyCallHandler", () => { afterEach(() => { callHandler.stop(); + // @ts-ignore DMRoomMap.setShared(null); // @ts-ignore window.mxLegacyCallHandler = null; - fakeCall = null; MatrixClientPeg.unset(); document.body.removeChild(audioElement); @@ -292,25 +320,27 @@ describe("LegacyCallHandler", () => { // Check that a call was started: its room on the protocol level // should be the virtual room - expect(fakeCall.roomId).toEqual(VIRTUAL_ROOM_BOB); + expect(fakeCall).not.toBeNull(); + expect(fakeCall?.roomId).toEqual(VIRTUAL_ROOM_BOB); // but it should appear to the user to be in thw native room for Bob - expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB); + expect(callHandler.roomIdForCall(fakeCall!)).toEqual(NATIVE_ROOM_BOB); }); it("should look up the correct user and start a call in the room when a call is transferred", async () => { // we can pass a very minimal object as as the call since we pass consultFirst=true: // we don't need to actually do any transferring - const mockTransferreeCall = { type: CallType.Voice }; + const mockTransferreeCall = { type: CallType.Voice } as unknown as MatrixCall; await callHandler.startTransferToPhoneNumber(mockTransferreeCall, BOB_PHONE_NUMBER, true); // same checks as above const viewRoomPayload = await untilDispatch(Action.ViewRoom); expect(viewRoomPayload.room_id).toEqual(NATIVE_ROOM_BOB); - expect(fakeCall.roomId).toEqual(VIRTUAL_ROOM_BOB); + expect(fakeCall).not.toBeNull(); + expect(fakeCall!.roomId).toEqual(VIRTUAL_ROOM_BOB); - expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB); + expect(callHandler.roomIdForCall(fakeCall!)).toEqual(NATIVE_ROOM_BOB); }); it("should move calls between rooms when remote asserted identity changes", async () => { @@ -331,10 +361,11 @@ describe("LegacyCallHandler", () => { // Now emit an asserted identity for Bob: this should be ignored // because we haven't set the config option to obey asserted identity - fakeCall.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ + expect(fakeCall).not.toBeNull(); + fakeCall!.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ id: NATIVE_BOB, }); - fakeCall.emit(CallEvent.AssertedIdentityChanged); + fakeCall!.emit(CallEvent.AssertedIdentityChanged); // Now set the config option SdkConfig.add({ @@ -344,10 +375,10 @@ describe("LegacyCallHandler", () => { }); // ...and send another asserted identity event for a different user - fakeCall.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ + fakeCall!.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ id: NATIVE_CHARLIE, }); - fakeCall.emit(CallEvent.AssertedIdentityChanged); + fakeCall!.emit(CallEvent.AssertedIdentityChanged); await roomChangePromise; callHandler.removeAllListeners(); @@ -362,21 +393,68 @@ describe("LegacyCallHandler", () => { expect(callHandler.getCallForRoom(NATIVE_ROOM_BOB)).toBeNull(); expect(callHandler.getCallForRoom(NATIVE_ROOM_CHARLIE)).toBe(fakeCall); }); + + describe("when listening to a voice broadcast", () => { + let voiceBroadcastPlayback: VoiceBroadcastPlayback; + + beforeEach(() => { + voiceBroadcastPlayback = new VoiceBroadcastPlayback( + mkVoiceBroadcastInfoStateEvent( + "!room:example.com", + VoiceBroadcastInfoState.Started, + MatrixClientPeg.get().getSafeUserId(), + "d42", + ), + MatrixClientPeg.get(), + ); + SdkContextClass.instance.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback); + jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation(); + }); + + it("and placing a call should pause the broadcast", async () => { + callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice); + await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState); + + expect(voiceBroadcastPlayback.pause).toHaveBeenCalled(); + }); + }); + + describe("when recording a voice broadcast", () => { + beforeEach(() => { + SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent( + new VoiceBroadcastRecording( + mkVoiceBroadcastInfoStateEvent( + "!room:example.com", + VoiceBroadcastInfoState.Started, + MatrixClientPeg.get().getSafeUserId(), + "d42", + ), + MatrixClientPeg.get(), + ), + ); + }); + + it("and placing a call should show the info dialog", async () => { + callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice); + expect(Modal.createDialog).toMatchSnapshot(); + }); + }); }); describe("LegacyCallHandler without third party protocols", () => { let dmRoomMap; let callHandler: LegacyCallHandler; let audioElement: HTMLAudioElement; - let fakeCall; + let fakeCall: MatrixCall | null; beforeEach(() => { stubClient(); + fakeCall = null; MatrixClientPeg.get().createCall = (roomId) => { if (fakeCall && fakeCall.roomId !== roomId) { throw new Error("Only one call is supported!"); } - fakeCall = new FakeCall(roomId); + fakeCall = new FakeCall(roomId) as unknown as MatrixCall; return fakeCall; }; @@ -389,11 +467,13 @@ describe("LegacyCallHandler without third party protocols", () => { const nativeRoomAlice = mkStubDM(NATIVE_ROOM_ALICE, NATIVE_ALICE); - MatrixClientPeg.get().getRoom = (roomId) => { + MatrixClientPeg.get().getRoom = (roomId: string): Room | null => { switch (roomId) { case NATIVE_ROOM_ALICE: return nativeRoomAlice; } + + return null; }; dmRoomMap = { @@ -411,7 +491,7 @@ describe("LegacyCallHandler without third party protocols", () => { return []; } }, - }; + } as DMRoomMap; DMRoomMap.setShared(dmRoomMap); MatrixClientPeg.get().getThirdpartyUser = (_proto, _params) => { @@ -421,14 +501,17 @@ describe("LegacyCallHandler without third party protocols", () => { audioElement = document.createElement("audio"); audioElement.id = "remoteAudio"; document.body.appendChild(audioElement); + + SdkContextClass.instance.voiceBroadcastPlaybacksStore.clearCurrent(); + SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent(); }); afterEach(() => { callHandler.stop(); + // @ts-ignore DMRoomMap.setShared(null); // @ts-ignore window.mxLegacyCallHandler = null; - fakeCall = null; MatrixClientPeg.unset(); document.body.removeChild(audioElement); @@ -442,10 +525,11 @@ describe("LegacyCallHandler without third party protocols", () => { // Check that a call was started: its room on the protocol level // should be the virtual room - expect(fakeCall.roomId).toEqual(NATIVE_ROOM_ALICE); + expect(fakeCall).not.toBeNull(); + expect(fakeCall!.roomId).toEqual(NATIVE_ROOM_ALICE); // but it should appear to the user to be in thw native room for Bob - expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_ALICE); + expect(callHandler.roomIdForCall(fakeCall!)).toEqual(NATIVE_ROOM_ALICE); }); describe("incoming calls", () => { diff --git a/test/__snapshots__/LegacyCallHandler-test.ts.snap b/test/__snapshots__/LegacyCallHandler-test.ts.snap new file mode 100644 index 0000000000..aaf4d78758 --- /dev/null +++ b/test/__snapshots__/LegacyCallHandler-test.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LegacyCallHandler when recording a voice broadcast and placing a call should show the info dialog 1`] = ` +[MockFunction] { + "calls": [ + [ + [Function], + { + "description":

+ You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call. +

, + "hasCloseButton": true, + "title": "Can’t start a call", + }, + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/test/components/views/beacon/BeaconMarker-test.tsx b/test/components/views/beacon/BeaconMarker-test.tsx index c30e99ed43..14b2032395 100644 --- a/test/components/views/beacon/BeaconMarker-test.tsx +++ b/test/components/views/beacon/BeaconMarker-test.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { act } from "react-dom/test-utils"; import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; @@ -41,7 +41,8 @@ describe("", () => { const aliceMember = new RoomMember(roomId, aliceId); - const mockMap = new maplibregl.Map(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); const mockClient = getMockClientWithEventEmitter({ getClientWellKnown: jest.fn().mockReturnValue({ diff --git a/test/components/views/beacon/BeaconViewDialog-test.tsx b/test/components/views/beacon/BeaconViewDialog-test.tsx index f05f17a5ba..9a17d60431 100644 --- a/test/components/views/beacon/BeaconViewDialog-test.tsx +++ b/test/components/views/beacon/BeaconViewDialog-test.tsx @@ -19,7 +19,7 @@ import React from "react"; import { mount, ReactWrapper } from "enzyme"; import { act } from "react-dom/test-utils"; import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { mocked } from "jest-mock"; import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog"; @@ -58,7 +58,8 @@ describe("", () => { getVisibleRooms: jest.fn().mockReturnValue([]), }); - const mockMap = new maplibregl.Map(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); // make fresh rooms every time // as we update room state @@ -91,10 +92,6 @@ describe("", () => { component.setProps({}); }); - beforeAll(() => { - maplibregl.AttributionControl = jest.fn(); - }); - beforeEach(() => { jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockRestore(); jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockRestore(); diff --git a/test/components/views/location/LocationPicker-test.tsx b/test/components/views/location/LocationPicker-test.tsx index 29b044630b..a35b5fa02e 100644 --- a/test/components/views/location/LocationPicker-test.tsx +++ b/test/components/views/location/LocationPicker-test.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; import { act } from "react-dom/test-utils"; @@ -62,8 +62,9 @@ describe("LocationPicker", () => { wrappingComponentProps: { value: mockClient }, }); - const mockMap = new maplibregl.Map(); - const mockGeolocate = new maplibregl.GeolocateControl(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); + const mockGeolocate = new maplibregl.GeolocateControl({}); const mockMarker = new maplibregl.Marker(); const mockGeolocationPosition = { diff --git a/test/components/views/location/LocationViewDialog-test.tsx b/test/components/views/location/LocationViewDialog-test.tsx index 31e40f94db..576e5f3217 100644 --- a/test/components/views/location/LocationViewDialog-test.tsx +++ b/test/components/views/location/LocationViewDialog-test.tsx @@ -19,7 +19,6 @@ import React from "react"; import { mount } from "enzyme"; import { RoomMember } from "matrix-js-sdk/src/matrix"; import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; -import maplibregl from "maplibre-gl"; import LocationViewDialog from "../../../../src/components/views/location/LocationViewDialog"; import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils"; @@ -42,10 +41,6 @@ describe("", () => { }; const getComponent = (props = {}) => mount(); - beforeAll(() => { - maplibregl.AttributionControl = jest.fn(); - }); - it("renders map correctly", () => { const component = getComponent(); expect(component.find("Map")).toMatchSnapshot(); diff --git a/test/components/views/location/Map-test.tsx b/test/components/views/location/Map-test.tsx index 8cb3109a7e..0d96a4a578 100644 --- a/test/components/views/location/Map-test.tsx +++ b/test/components/views/location/Map-test.tsx @@ -18,7 +18,7 @@ import React from "react"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; import { act } from "react-dom/test-utils"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { ClientEvent } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; @@ -45,10 +45,6 @@ describe("", () => { wrappingComponentProps: { value: matrixClient }, }); - beforeAll(() => { - maplibregl.AttributionControl = jest.fn(); - }); - beforeEach(() => { jest.clearAllMocks(); matrixClient.getClientWellKnown.mockReturnValue({ @@ -58,7 +54,8 @@ describe("", () => { jest.spyOn(logger, "error").mockRestore(); }); - const mockMap = new maplibregl.Map(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); it("renders", () => { const component = getComponent(); diff --git a/test/components/views/location/SmartMarker-test.tsx b/test/components/views/location/SmartMarker-test.tsx index 569c80638a..8ddcafb00f 100644 --- a/test/components/views/location/SmartMarker-test.tsx +++ b/test/components/views/location/SmartMarker-test.tsx @@ -18,7 +18,7 @@ import React from "react"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; import { mocked } from "jest-mock"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import SmartMarker from "../../../../src/components/views/location/SmartMarker"; @@ -27,7 +27,8 @@ jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({ })); describe("", () => { - const mockMap = new maplibregl.Map(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); const mockMarker = new maplibregl.Marker(); const defaultProps = { diff --git a/test/components/views/location/ZoomButtons-test.tsx b/test/components/views/location/ZoomButtons-test.tsx index 5c5b63b299..831860bcd5 100644 --- a/test/components/views/location/ZoomButtons-test.tsx +++ b/test/components/views/location/ZoomButtons-test.tsx @@ -17,14 +17,15 @@ limitations under the License. import React from "react"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { act } from "react-dom/test-utils"; import ZoomButtons from "../../../../src/components/views/location/ZoomButtons"; import { findByTestId } from "../../../test-utils"; describe("", () => { - const mockMap = new maplibregl.Map(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); const defaultProps = { map: mockMap, }; diff --git a/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap b/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap index c78636dc66..a2284ceee8 100644 --- a/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap +++ b/test/components/views/location/__snapshots__/LocationViewDialog-test.tsx.snap @@ -26,7 +26,7 @@ exports[` renders map correctly 1`] = ` "addControl": [MockFunction] { "calls": [ [ - mockConstructor {}, + MockAttributionControl {}, "top-right", ], ], @@ -94,7 +94,7 @@ exports[` renders map correctly 1`] = ` "addControl": [MockFunction] { "calls": [ [ - mockConstructor {}, + MockAttributionControl {}, "top-right", ], ], diff --git a/test/components/views/messages/MBeaconBody-test.tsx b/test/components/views/messages/MBeaconBody-test.tsx index b779c35a9c..a466f9be9e 100644 --- a/test/components/views/messages/MBeaconBody-test.tsx +++ b/test/components/views/messages/MBeaconBody-test.tsx @@ -18,7 +18,7 @@ import React from "react"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; import { act } from "react-dom/test-utils"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { BeaconEvent, getBeaconInfoIdentifier, RelationType, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; import { Relations } from "matrix-js-sdk/src/models/relations"; import { M_BEACON } from "matrix-js-sdk/src/@types/beacon"; @@ -48,7 +48,8 @@ describe("", () => { const roomId = "!room:server"; const aliceId = "@alice:server"; - const mockMap = new maplibregl.Map(); + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; + const mockMap = new maplibregl.Map(mapOptions); const mockMarker = new maplibregl.Marker(); const mockClient = getMockClientWithEventEmitter({ @@ -81,10 +82,6 @@ describe("", () => { const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue(undefined); - beforeAll(() => { - maplibregl.AttributionControl = jest.fn(); - }); - beforeEach(() => { jest.clearAllMocks(); }); diff --git a/test/components/views/messages/MLocationBody-test.tsx b/test/components/views/messages/MLocationBody-test.tsx index cae3727874..e36b785dea 100644 --- a/test/components/views/messages/MLocationBody-test.tsx +++ b/test/components/views/messages/MLocationBody-test.tsx @@ -19,7 +19,7 @@ import React from "react"; import { mount } from "enzyme"; import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; import { ClientEvent, RoomMember } from "matrix-js-sdk/src/matrix"; -import maplibregl from "maplibre-gl"; +import * as maplibregl from "maplibre-gl"; import { logger } from "matrix-js-sdk/src/logger"; import { act } from "react-dom/test-utils"; import { SyncState } from "matrix-js-sdk/src/sync"; @@ -35,6 +35,7 @@ import { makeLocationEvent } from "../../../test-utils/location"; import { getMockClientWithEventEmitter } from "../../../test-utils"; describe("MLocationBody", () => { + const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; describe("", () => { const roomId = "!room:server"; const userId = "@user:server"; @@ -60,7 +61,7 @@ describe("MLocationBody", () => { wrappingComponentProps: { value: mockClient }, }); const getMapErrorComponent = () => { - const mockMap = new maplibregl.Map(); + const mockMap = new maplibregl.Map(mapOptions); mockClient.getClientWellKnown.mockReturnValue({ [TILE_SERVER_WK_KEY.name]: { map_style_url: "bad-tile-server.com" }, }); @@ -73,10 +74,6 @@ describe("MLocationBody", () => { return component; }; - beforeAll(() => { - maplibregl.AttributionControl = jest.fn(); - }); - beforeEach(() => { jest.clearAllMocks(); }); @@ -131,7 +128,7 @@ describe("MLocationBody", () => { }); it("renders map correctly", () => { - const mockMap = new maplibregl.Map(); + const mockMap = new maplibregl.Map(mapOptions); const component = getComponent(); expect(component).toMatchSnapshot(); @@ -154,7 +151,7 @@ describe("MLocationBody", () => { }); it("renders marker correctly for a non-self share", () => { - const mockMap = new maplibregl.Map(); + const mockMap = new maplibregl.Map(mapOptions); const component = getComponent(); expect(component.find("SmartMarker").at(0).props()).toEqual( diff --git a/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap b/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap index cead368a69..160a2b1aa2 100644 --- a/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap +++ b/test/components/views/messages/__snapshots__/MLocationBody-test.tsx.snap @@ -131,11 +131,11 @@ exports[`MLocationBody without error renders map correctly 1`] = "addControl": [MockFunction] { "calls": [ [ - mockConstructor {}, + MockAttributionControl {}, "top-right", ], [ - mockConstructor {}, + MockAttributionControl {}, "top-right", ], ], diff --git a/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap index accfd10806..739f559ad4 100644 --- a/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap +++ b/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap @@ -13,11 +13,11 @@ HTMLCollection [ class="mx_DeviceDetailHeading" data-testid="device-detail-heading" > -

alices_device -

+