mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
Offer background effects as a grid in the camera menu
- The camera menu's blur toggle becomes a Background effects section under the cameras: no effect, blur and the two shipped backgrounds as one radio choice of tiles, three across. It scrolls with the device list, so it stays reachable in a short call, and choosing keeps the menu open. The lobby gets it too, as both render the same footer. - The footer's view model gives the options, the notice and the actions; the footer only names them. - Each tile is Compound's MenuItem as a menuitemradio, styled as a tile. Compound makes it an item of the dropdown, so the arrow keys walk the tiles with the device rows, and takes its tap in the phone drawer. A tile that can't be chosen has no select handler, so the keyboard passes over it. - Where the browser can't run effects the tiles are shown, disabled and explained by a notice the group is described by. No effect stays choosable and shows as in force. The notice is in the scrolling list, so a short call doesn't push the menu out of it. - Section headings are raised, or the positioned tiles scroll over the one they sit under; raised within the list only, so the menu's frame stays over them. Tiles clip with overflow: clip; with hidden they are scroll containers, which focus scrolls into view without their border. - End to end on Chromium and Firefox: a shipped background chosen in the lobby shows in the preview, and None after Blur draws nothing, counted in WebGL draws (Blur: about 1400 a second on Chromium, 2300 on Firefox). - The Quick Audio Menu's test of the camera menu keeps its name and now finds blur among the effects rather than as a toggle. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { testScope, mockMuteStates, mockMediaDevices } from "../utils/test";
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
createLobbyFooterViewModel,
|
||||
} from "./CallFooterViewModel";
|
||||
import { HeaderStyle } from "../UrlParams";
|
||||
import { backgroundEffect as backgroundEffectSetting } from "../settings/settings";
|
||||
import { shippedBackgrounds } from "../livekit/backgroundEffects";
|
||||
|
||||
const platformMock = vi.hoisted(() => vi.fn(() => "desktop"));
|
||||
vi.mock("../Platform", () => ({
|
||||
@@ -227,6 +229,8 @@ describe("createCallFooterViewModel", () => {
|
||||
});
|
||||
|
||||
describe("background effects", () => {
|
||||
afterEach(() => backgroundEffectSetting.setValue("none"));
|
||||
|
||||
// The lobby, because in a call the whole device switcher is already
|
||||
// withheld on a phone, whatever the verdict says.
|
||||
function lobbyOn(
|
||||
@@ -245,17 +249,55 @@ describe("createCallFooterViewModel", () => {
|
||||
|
||||
it("offers nothing on a phone, where the pipeline would refuse them", () => {
|
||||
sdkSupportMock.mockReturnValue(true);
|
||||
expect(lobbyOn("ios").toggleBlur$.value).toBeUndefined();
|
||||
expect(lobbyOn("ios").selectBackgroundEffect$.value).toBeUndefined();
|
||||
});
|
||||
|
||||
it("offers them where the pipeline will honour them", () => {
|
||||
sdkSupportMock.mockReturnValue(true);
|
||||
expect(lobbyOn("desktop").toggleBlur$.value).toBeDefined();
|
||||
expect(lobbyOn("desktop").selectBackgroundEffect$.value).toBeDefined();
|
||||
});
|
||||
|
||||
it("offers nothing where the browser itself cannot run them", () => {
|
||||
sdkSupportMock.mockReturnValue(false);
|
||||
expect(lobbyOn("desktop").toggleBlur$.value).toBeUndefined();
|
||||
expect(lobbyOn("desktop").selectBackgroundEffect$.value).toBeUndefined();
|
||||
});
|
||||
|
||||
it("puts no effect in force where the browser cannot run them", () => {
|
||||
backgroundEffectSetting.setValue("blur");
|
||||
sdkSupportMock.mockReturnValue(false);
|
||||
expect(lobbyOn("desktop").backgroundEffect$.value).toBe("none");
|
||||
sdkSupportMock.mockReturnValue(true);
|
||||
expect(lobbyOn("desktop").backgroundEffect$.value).toBe("blur");
|
||||
});
|
||||
|
||||
it("stores the effect chosen", () => {
|
||||
sdkSupportMock.mockReturnValue(true);
|
||||
const vm = lobbyOn("desktop");
|
||||
vm.selectBackgroundEffect$.value?.("image:arc");
|
||||
expect(vm.backgroundEffect$.value).toBe("image:arc");
|
||||
// What isn't an effect on offer is stored as none.
|
||||
vm.selectBackgroundEffect$.value?.("image:gone");
|
||||
expect(backgroundEffectSetting.getValue()).toBe("none");
|
||||
});
|
||||
|
||||
it("offers every effect in order", () => {
|
||||
sdkSupportMock.mockReturnValue(true);
|
||||
expect(lobbyOn("desktop").backgroundEffects$.value).toEqual([
|
||||
{ id: "none", kind: "none" },
|
||||
{ id: "blur", kind: "blur" },
|
||||
...shippedBackgrounds.map((background) => ({
|
||||
id: `image:${background.id}`,
|
||||
kind: "image",
|
||||
imageUrl: background.imagePath,
|
||||
})),
|
||||
]);
|
||||
});
|
||||
|
||||
it("says they are unavailable where they cannot be chosen", () => {
|
||||
sdkSupportMock.mockReturnValue(false);
|
||||
expect(lobbyOn("desktop").backgroundEffectNotice$.value).toBe(
|
||||
"unavailable",
|
||||
);
|
||||
});
|
||||
|
||||
it("availability is the same before and during a call", () => {
|
||||
@@ -270,8 +312,11 @@ describe("createCallFooterViewModel", () => {
|
||||
/* reactionIdentifier */ undefined,
|
||||
{ showControls: true, header: HeaderStyle.Standard },
|
||||
);
|
||||
const offeredInLobby = lobby.toggleBlur$.value !== undefined;
|
||||
expect(inCall.toggleBlur$.value !== undefined).toBe(offeredInLobby);
|
||||
const offeredInLobby =
|
||||
lobby.selectBackgroundEffect$.value !== undefined;
|
||||
expect(inCall.selectBackgroundEffect$.value !== undefined).toBe(
|
||||
offeredInLobby,
|
||||
);
|
||||
expect(offeredInLobby).toBe(supported);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user