From af3715821be8a74d58ff04fa59125304318e1b92 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 18 Dec 2022 23:17:15 +0000 Subject: [PATCH 01/10] Update dependency maplibre-gl to v2 (#9699) * Update dependency maplibre-gl to v2 * update types and mocks for maplibre 2.0 * prettier + strict fix Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kerry Archibald --- __mocks__/maplibre-gl.js | 3 +- package.json | 2 +- src/components/views/beacon/BeaconMarker.tsx | 2 +- .../views/beacon/BeaconViewDialog.tsx | 2 +- .../views/location/LocationPicker.tsx | 2 +- src/components/views/location/Map.tsx | 2 +- src/components/views/location/SmartMarker.tsx | 2 +- src/components/views/location/ZoomButtons.tsx | 2 +- src/utils/location/map.ts | 2 +- .../views/beacon/BeaconMarker-test.tsx | 5 +- .../views/beacon/BeaconViewDialog-test.tsx | 9 +- .../views/location/LocationPicker-test.tsx | 7 +- .../location/LocationViewDialog-test.tsx | 5 - test/components/views/location/Map-test.tsx | 9 +- .../views/location/SmartMarker-test.tsx | 5 +- .../views/location/ZoomButtons-test.tsx | 5 +- .../LocationViewDialog-test.tsx.snap | 4 +- .../views/messages/MBeaconBody-test.tsx | 9 +- .../views/messages/MLocationBody-test.tsx | 13 +-- .../__snapshots__/MLocationBody-test.tsx.snap | 4 +- yarn.lock | 107 +++++++++--------- 21 files changed, 97 insertions(+), 104 deletions(-) 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/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/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/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/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/yarn.lock b/yarn.lock index d377e23ac9..9afcf99efd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1470,7 +1470,7 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@mapbox/geojson-rewind@^0.5.0": +"@mapbox/geojson-rewind@^0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a" integrity sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA== @@ -1478,35 +1478,30 @@ get-stream "^6.0.1" minimist "^1.2.6" -"@mapbox/geojson-types@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz#9aecf642cb00eab1080a57c4f949a65b4a5846d6" - integrity sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw== - "@mapbox/jsonlint-lines-primitives@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ== -"@mapbox/mapbox-gl-supported@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz#f60b6a55a5d8e5ee908347d2ce4250b15103dc8e" - integrity sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg== +"@mapbox/mapbox-gl-supported@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz#c15367178d8bfe4765e6b47b542fe821ce259c7b" + integrity sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ== "@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ== -"@mapbox/tiny-sdf@^1.1.1": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz#424c620a96442b20402552be70a7f62a8407cc59" - integrity sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw== +"@mapbox/tiny-sdf@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz#cdba698d3d65087643130f9af43a2b622ce0b372" + integrity sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw== -"@mapbox/unitbezier@^0.0.0": - version "0.0.0" - resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e" - integrity sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA== +"@mapbox/unitbezier@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01" + integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw== "@mapbox/vector-tile@^1.3.1": version "1.3.1" @@ -2070,7 +2065,7 @@ dependencies: "@types/node" "*" -"@types/geojson@^7946.0.8": +"@types/geojson@*", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.8": version "7946.0.10" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== @@ -2146,6 +2141,20 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== +"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz#488a9b76e8457d6792ea2504cdd4ecdd9860a27e" + integrity sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA== + +"@types/mapbox__vector-tile@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz#8fa1379dbaead1e1b639b8d96cfd174404c379d6" + integrity sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g== + dependencies: + "@types/geojson" "*" + "@types/mapbox__point-geometry" "*" + "@types/pbf" "*" + "@types/minimist@^1.2.0": version "1.2.2" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" @@ -2191,6 +2200,11 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== +"@types/pbf@*", "@types/pbf@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.2.tgz#8d291ad68b4b8c533e96c174a2e3e6399a59ed61" + integrity sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ== + "@types/prettier@^2.1.5": version "2.7.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" @@ -3783,7 +3797,7 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.1" -earcut@^2.2.2: +earcut@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a" integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ== @@ -4787,7 +4801,7 @@ gfm.css@^1.1.2: resolved "https://registry.yarnpkg.com/gfm.css/-/gfm.css-1.1.2.tgz#94acfa600672663b9dd0fd4b6ee5d11c8dbc161e" integrity sha512-KhK3rqxMj+UTLRxWnfUA5n8XZYMWfHrrcCxtWResYR2B3hWIqBM6v9FPGZSlVuX+ScLewizOvNkjYXuPs95ThQ== -gl-matrix@^3.2.1: +gl-matrix@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9" integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA== @@ -4903,11 +4917,6 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -grid-index@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/grid-index/-/grid-index-1.1.0.tgz#97f8221edec1026c8377b86446a7c71e79522ea7" - integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA== - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -6341,34 +6350,35 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -maplibre-gl@^1.15.2: - version "1.15.3" - resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-1.15.3.tgz#eebbdd6b4cba46c61a660d6fa1808fced126e24d" - integrity sha512-ZuOhLCNgp7Yl1L9uyKgZeuo7kKdewP0iWtmEXsZ/snp0JiVkR1Kl+m1rsfKT/wpm/O4zZ7mUGxF16cYbMIFDRA== +maplibre-gl@^2.0.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-2.4.0.tgz#2b53dbf526626bf4ee92ad4f33f13ef09e5af182" + integrity sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA== dependencies: - "@mapbox/geojson-rewind" "^0.5.0" - "@mapbox/geojson-types" "^1.0.2" + "@mapbox/geojson-rewind" "^0.5.2" "@mapbox/jsonlint-lines-primitives" "^2.0.2" - "@mapbox/mapbox-gl-supported" "^1.5.0" + "@mapbox/mapbox-gl-supported" "^2.0.1" "@mapbox/point-geometry" "^0.1.0" - "@mapbox/tiny-sdf" "^1.1.1" - "@mapbox/unitbezier" "^0.0.0" + "@mapbox/tiny-sdf" "^2.0.5" + "@mapbox/unitbezier" "^0.0.1" "@mapbox/vector-tile" "^1.3.1" "@mapbox/whoots-js" "^3.1.0" + "@types/geojson" "^7946.0.10" + "@types/mapbox__point-geometry" "^0.1.2" + "@types/mapbox__vector-tile" "^1.3.0" + "@types/pbf" "^3.0.2" csscolorparser "~1.0.3" - earcut "^2.2.2" + earcut "^2.2.4" geojson-vt "^3.2.1" - gl-matrix "^3.2.1" - grid-index "^1.1.0" - minimist "^1.2.6" + gl-matrix "^3.4.3" + global-prefix "^3.0.0" murmurhash-js "^1.0.0" pbf "^3.2.1" - potpack "^1.0.1" + potpack "^1.0.2" quickselect "^2.0.0" - rw "^1.3.3" - supercluster "^7.1.0" + supercluster "^7.1.5" tinyqueue "^2.0.3" - vt-pbf "^3.1.1" + vt-pbf "^3.1.3" mathml-tag-names@^2.1.3: version "2.1.3" @@ -7077,7 +7087,7 @@ posthog-js@1.36.0: fflate "^0.4.1" rrweb-snapshot "^1.1.14" -potpack@^1.0.1: +potpack@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14" integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ== @@ -7672,11 +7682,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rw@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" - integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== - rxjs@^7.5.1: version "7.5.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" @@ -8121,7 +8126,7 @@ stylelint@^14.9.1: v8-compile-cache "^2.3.0" write-file-atomic "^4.0.2" -supercluster@^7.1.0: +supercluster@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.5.tgz#65a6ce4a037a972767740614c19051b64b8be5a3" integrity sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg== @@ -8576,7 +8581,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vt-pbf@^3.1.1: +vt-pbf@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA== From 35a187a23196353b354a8462ebd416655ade6361 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Mon, 19 Dec 2022 09:44:19 +0100 Subject: [PATCH 02/10] Handle starting a call while listen to a broadcast (#9764) --- src/LegacyCallHandler.tsx | 11 ++ src/i18n/strings/en_EN.json | 2 + src/stores/RoomViewStore.tsx | 11 ++ .../utils/showCantStartACallDialog.tsx | 36 +++++ test/LegacyCallHandler-test.ts | 140 ++++++++++++++---- .../LegacyCallHandler-test.ts.snap | 24 +++ test/stores/RoomViewStore-test.ts | 93 +++++++++++- .../__snapshots__/RoomViewStore-test.ts.snap | 24 +++ 8 files changed, 312 insertions(+), 29 deletions(-) create mode 100644 src/voice-broadcast/utils/showCantStartACallDialog.tsx create mode 100644 test/__snapshots__/LegacyCallHandler-test.ts.snap create mode 100644 test/stores/__snapshots__/RoomViewStore-test.ts.snap 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/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8321a925ef..7aacb9f976 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", 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/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/stores/RoomViewStore-test.ts b/test/stores/RoomViewStore-test.ts index 6103c6bb46..04f595a914 100644 --- a/test/stores/RoomViewStore-test.ts +++ b/test/stores/RoomViewStore-test.ts @@ -28,6 +28,17 @@ import { UPDATE_EVENT } from "../../src/stores/AsyncStore"; import { ActiveRoomChangedPayload } from "../../src/dispatcher/payloads/ActiveRoomChangedPayload"; import { SpaceStoreClass } from "../../src/stores/spaces/SpaceStore"; import { TestSdkContext } from "../TestSdkContext"; +import { ViewRoomPayload } from "../../src/dispatcher/payloads/ViewRoomPayload"; +import { + VoiceBroadcastInfoState, + VoiceBroadcastPlayback, + VoiceBroadcastPlaybacksStore, + VoiceBroadcastRecording, +} from "../../src/voice-broadcast"; +import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils"; +import Modal from "../../src/Modal"; + +jest.mock("../../src/Modal"); // mock out the injected classes jest.mock("../../src/PosthogAnalytics"); @@ -37,6 +48,22 @@ const MockSlidingSyncManager = >(SlidingS jest.mock("../../src/stores/spaces/SpaceStore"); const MockSpaceStore = >(SpaceStoreClass); +// 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/DMRoomMap", () => { const mock = { getUserIdForRoomId: jest.fn(), @@ -60,12 +87,24 @@ describe("RoomViewStore", function () { getRoom: jest.fn(), getRoomIdForAlias: jest.fn(), isGuest: jest.fn(), + getSafeUserId: jest.fn(), }); const room = new Room(roomId, mockClient, userId); + const viewCall = async (): Promise => { + dis.dispatch({ + action: Action.ViewRoom, + room_id: roomId, + view_call: true, + metricsTrigger: undefined, + }); + await untilDispatch(Action.ViewRoom, dis); + }; + let roomViewStore: RoomViewStore; let slidingSyncManager: SlidingSyncManager; let dis: MatrixDispatcher; + let stores: TestSdkContext; beforeEach(function () { jest.clearAllMocks(); @@ -73,14 +112,16 @@ describe("RoomViewStore", function () { mockClient.joinRoom.mockResolvedValue(room); mockClient.getRoom.mockReturnValue(room); mockClient.isGuest.mockReturnValue(false); + mockClient.getSafeUserId.mockReturnValue(userId); // Make the RVS to test dis = new MatrixDispatcher(); slidingSyncManager = new MockSlidingSyncManager(); - const stores = new TestSdkContext(); + stores = new TestSdkContext(); stores._SlidingSyncManager = slidingSyncManager; stores._PosthogAnalytics = new MockPosthogAnalytics(); stores._SpaceStore = new MockSpaceStore(); + stores._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(); roomViewStore = new RoomViewStore(dis, stores); stores._RoomViewStore = roomViewStore; }); @@ -206,6 +247,56 @@ describe("RoomViewStore", function () { expect(roomViewStore.getRoomId()).toBeNull(); }); + it("when viewing a call without a broadcast, it should not raise an error", async () => { + await viewCall(); + }); + + describe("when listening to a voice broadcast", () => { + let voiceBroadcastPlayback: VoiceBroadcastPlayback; + + beforeEach(() => { + voiceBroadcastPlayback = new VoiceBroadcastPlayback( + mkVoiceBroadcastInfoStateEvent( + roomId, + VoiceBroadcastInfoState.Started, + mockClient.getSafeUserId(), + "d42", + ), + mockClient, + ); + stores.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback); + jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation(); + }); + + it("and viewing a call it should pause the current broadcast", async () => { + await viewCall(); + expect(voiceBroadcastPlayback.pause).toHaveBeenCalled(); + expect(roomViewStore.isViewingCall()).toBe(true); + }); + }); + + describe("when recording a voice broadcast", () => { + beforeEach(() => { + stores.voiceBroadcastRecordingsStore.setCurrent( + new VoiceBroadcastRecording( + mkVoiceBroadcastInfoStateEvent( + roomId, + VoiceBroadcastInfoState.Started, + mockClient.getSafeUserId(), + "d42", + ), + mockClient, + ), + ); + }); + + it("and trying to view a call, it should not actually view it and show the info dialog", async () => { + await viewCall(); + expect(Modal.createDialog).toMatchSnapshot(); + expect(roomViewStore.isViewingCall()).toBe(false); + }); + }); + describe("Sliding Sync", function () { beforeEach(() => { jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName, roomId, value) => { diff --git a/test/stores/__snapshots__/RoomViewStore-test.ts.snap b/test/stores/__snapshots__/RoomViewStore-test.ts.snap new file mode 100644 index 0000000000..64221efac4 --- /dev/null +++ b/test/stores/__snapshots__/RoomViewStore-test.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RoomViewStore when recording a voice broadcast and trying to view a call, it should not actually view it and 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, + }, + ], +} +`; From 8541e1c4b5fe62142a0e8cf8a2c292cfd0927575 Mon Sep 17 00:00:00 2001 From: Germain Date: Mon, 19 Dec 2022 10:42:58 +0000 Subject: [PATCH 03/10] Change room list sorting to activity and unread first by default (#9773) --- src/stores/room-list/RoomListStore.ts | 6 ++-- test/stores/room-list/RoomListStore-test.ts | 35 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 test/stores/room-list/RoomListStore-test.ts diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index e045166e7c..470c2b685f 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -387,8 +387,6 @@ 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); @@ -396,7 +394,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // 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)) { @@ -431,7 +429,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // logic must match calculateTagSorting private calculateListOrder(tagId: TagID): ListAlgorithm { - const defaultOrder = ListAlgorithm.Natural; + const defaultOrder = ListAlgorithm.Importance; const settingImportance = SettingsStore.getValue("RoomList.orderByImportance", null, true); const definedOrder = this.getListOrder(tagId); const storedOrder = this.getStoredListOrder(tagId); diff --git a/test/stores/room-list/RoomListStore-test.ts b/test/stores/room-list/RoomListStore-test.ts new file mode 100644 index 0000000000..04b99a8035 --- /dev/null +++ b/test/stores/room-list/RoomListStore-test.ts @@ -0,0 +1,35 @@ +/* +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 { ListAlgorithm, SortAlgorithm } from "../../../src/stores/room-list/algorithms/models"; +import { OrderedDefaultTagIDs } from "../../../src/stores/room-list/models"; +import RoomListStore, { RoomListStoreClass } from "../../../src/stores/room-list/RoomListStore"; +import { stubClient } from "../../test-utils"; + +describe("RoomListStore", () => { + beforeAll(async () => { + const client = stubClient(); + await (RoomListStore.instance as RoomListStoreClass).makeReady(client); + }); + + it.each(OrderedDefaultTagIDs)("defaults to importance ordering for %s=", (tagId) => { + expect(RoomListStore.instance.getTagSorting(tagId)).toBe(SortAlgorithm.Recent); + }); + + it.each(OrderedDefaultTagIDs)("defaults to activity ordering for %s=", (tagId) => { + expect(RoomListStore.instance.getListOrder(tagId)).toBe(ListAlgorithm.Importance); + }); +}); From 3ec75fdd3c614dd96929980fe9c6273c3f4cf1c3 Mon Sep 17 00:00:00 2001 From: Germain Date: Mon, 19 Dec 2022 11:57:57 +0000 Subject: [PATCH 04/10] Remove RoomList sorting settings (#9781) --- src/i18n/strings/en_EN.json | 2 -- src/settings/Settings.tsx | 12 ------------ src/stores/room-list/RoomListStore.ts | 7 ------- 3 files changed, 21 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7aacb9f976..b43e7e2891 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1007,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", 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/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 470c2b685f..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,7 +386,6 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // logic must match calculateListOrder private calculateTagSorting(tagId: TagID): SortAlgorithm { - const settingAlphabetical = SettingsStore.getValue("RoomList.orderAlphabetically", null, true); const definedSort = this.getTagSorting(tagId); const storedSort = this.getStoredTagSorting(tagId); @@ -397,8 +395,6 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements 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) @@ -430,7 +426,6 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // logic must match calculateTagSorting private calculateListOrder(tagId: TagID): ListAlgorithm { const defaultOrder = ListAlgorithm.Importance; - const settingImportance = SettingsStore.getValue("RoomList.orderByImportance", null, true); const definedOrder = this.getListOrder(tagId); const storedOrder = this.getStoredListOrder(tagId); @@ -440,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) From ddfa627ce64eb28c3d98cf0a02996609a70207a5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Mon, 19 Dec 2022 13:12:01 +0000 Subject: [PATCH 05/10] Cypress: add a way to specify the prefix for userIDs (#9787) This helps a lot with debugging tests where there are multiple users. --- cypress/e2e/crypto/crypto.spec.ts | 4 ++-- cypress/support/bot.ts | 7 ++++++- cypress/support/login.ts | 12 ++++++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index 2a55c21b05..b5db3871b8 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/support/bot.ts b/cypress/support/bot.ts index f4476af12f..78c83c5c8e 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, @@ -153,7 +158,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).then((credentials) => { cy.log(`Registered bot user ${username} with displayname ${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) From c0e0078b6e7239f7b6b8c2a047765946950ca709 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:55:13 +0000 Subject: [PATCH 06/10] Remove unused property from `IConfigOptions` (#9792) accidentally added in #9759 --- src/IConfigOptions.ts | 2 -- 1 file changed, 2 deletions(-) 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 { From 2152f2ab8b7198932b6fcfc8b7cf63064fb494c4 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Mon, 19 Dec 2022 17:05:34 +0100 Subject: [PATCH 07/10] Stop broadcast playback when stopping the broadcast (#9795) --- src/voice-broadcast/models/VoiceBroadcastPlayback.ts | 2 +- test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) 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/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts index 759adf1128..616875816b 100644 --- a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts +++ b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts @@ -411,6 +411,10 @@ describe("VoiceBroadcastPlayback", () => { stopPlayback(); itShouldSetTheStateTo(VoiceBroadcastPlaybackState.Stopped); + it("should stop the playback", () => { + expect(chunk1Playback.stop).toHaveBeenCalled(); + }); + describe("and skipping to somewhere in the middle of the first chunk", () => { beforeEach(async () => { mocked(chunk1Playback.play).mockClear(); From 0485b74acbc1d2a2b1d031a3d7d4ea7afb50a8d6 Mon Sep 17 00:00:00 2001 From: Kerry Date: Tue, 20 Dec 2022 14:17:05 +1300 Subject: [PATCH 08/10] Device manager - design tweaks (#9768) * hover style on dropdown * hover states * device details spacing * device details h3 -> h4 * missing fullstops * update snapshots for new copy * device details expand icon bigger * hide current session security card when details expanded * filter dropdown padding and hover state * update cypress selector for device detail heading --- cypress/e2e/settings/device-management.spec.ts | 2 +- .../views/elements/_FilterDropdown.pcss | 13 +++++++++---- .../views/settings/devices/_DeviceDetails.pcss | 9 +++++---- .../devices/_DeviceExpandDetailsButton.pcss | 12 ++++++++++-- .../settings/devices/_DeviceTypeIcon.pcss | 2 +- .../settings/shared/_SettingsSubsection.pcss | 2 +- .../settings/devices/CurrentDeviceSection.tsx | 9 ++++++--- .../settings/devices/DeviceDetailHeading.tsx | 2 +- .../devices/FilteredDeviceListHeader.tsx | 2 +- .../devices/SecurityRecommendations.tsx | 4 ++-- src/i18n/strings/en_EN.json | 6 +++--- .../CurrentDeviceSection-test.tsx.snap | 6 +++--- .../DeviceDetailHeading-test.tsx.snap | 6 +++--- .../__snapshots__/DeviceDetails-test.tsx.snap | 18 +++++++++--------- .../SecurityRecommendations-test.tsx.snap | 12 ++++++------ .../tabs/user/SessionManagerTab-test.tsx | 15 +++++++++++++++ 16 files changed, 76 insertions(+), 44 deletions(-) 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/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/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 b43e7e2891..306e048355 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -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/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 -

+