mirror of
https://github.com/vector-im/element-call.git
synced 2026-09-22 22:29:30 +00:00
Make the settings blur control agree with the camera menu
- Both now read and write one setting, so they cannot disagree - An image background reads as blur off there; turning blur on replaces the image, which is what FR-022 and EC-011 ask for - The new setting defaults to whatever blur the user already had, so nobody loses the blur they turned on before this existed - Covers the derivation with tests: what the checkbox shows, what it writes, and the fallback when a stored background is one we no longer ship Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import {
|
||||
parseEffect,
|
||||
serializeEffect,
|
||||
shippedBackgrounds,
|
||||
imagePathFor,
|
||||
} from "./backgroundEffects";
|
||||
|
||||
describe("the chosen background effect", () => {
|
||||
test("round-trips through its stored form", () => {
|
||||
for (const effect of [
|
||||
{ kind: "none" } as const,
|
||||
{ kind: "blur" } as const,
|
||||
{ kind: "image", id: shippedBackgrounds[0].id } as const,
|
||||
])
|
||||
expect(parseEffect(serializeEffect(effect))).toEqual(effect);
|
||||
});
|
||||
|
||||
test("falls back to no effect when the stored form names nothing we ship", () => {
|
||||
// A background removed between releases, or a value from a future one.
|
||||
expect(parseEffect("image:a-background-we-no-longer-ship")).toEqual({
|
||||
kind: "none",
|
||||
});
|
||||
expect(parseEffect("")).toEqual({ kind: "none" });
|
||||
});
|
||||
|
||||
test("gives every shipped background an image to draw", () => {
|
||||
for (const background of shippedBackgrounds)
|
||||
expect(imagePathFor(background.id)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("the blur control in settings", () => {
|
||||
// The control is a checkbox over a setting that holds more than two states,
|
||||
// so what it shows and what it writes are both derived. This is that
|
||||
// derivation, which is what keeps it from disagreeing with the camera menu.
|
||||
const shows = (stored: string): boolean =>
|
||||
parseEffect(stored).kind === "blur";
|
||||
const writes = (on: boolean): string =>
|
||||
serializeEffect(on ? { kind: "blur" } : { kind: "none" });
|
||||
|
||||
test("reads as off while an image background is in force", () => {
|
||||
expect(shows(serializeEffect({ kind: "image", id: "indoor" }))).toBe(false);
|
||||
});
|
||||
|
||||
test("reads as on while blur is in force", () => {
|
||||
expect(shows("blur")).toBe(true);
|
||||
});
|
||||
|
||||
test("replaces an image background when it is turned on", () => {
|
||||
expect(writes(true)).toBe("blur");
|
||||
});
|
||||
|
||||
test("leaves no effect behind when it is turned off", () => {
|
||||
expect(writes(false)).toBe("none");
|
||||
});
|
||||
});
|
||||
@@ -22,13 +22,17 @@ import { useHostBridge } from "../HostBridge";
|
||||
import {
|
||||
useSetting,
|
||||
soundEffectVolume as soundEffectVolumeSetting,
|
||||
backgroundBlur as backgroundBlurSetting,
|
||||
backgroundEffect as backgroundEffectSetting,
|
||||
developerMode,
|
||||
} from "./settings";
|
||||
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
||||
import { Slider } from "../Slider";
|
||||
import { DeviceSelection } from "./DeviceSelection";
|
||||
import { useTrackProcessor } from "../livekit/TrackProcessorContext";
|
||||
import {
|
||||
parseEffect,
|
||||
serializeEffect,
|
||||
} from "../livekit/backgroundEffects";
|
||||
import {
|
||||
DeveloperSettingsTab,
|
||||
type DeveloperSettingsSnapshot,
|
||||
@@ -83,7 +87,13 @@ export const SettingsModal: FC<Props> = ({
|
||||
const BlurCheckbox: React.FC = (): ReactNode => {
|
||||
const { supported } = useTrackProcessor();
|
||||
|
||||
const [blurActive, setBlurActive] = useSetting(backgroundBlurSetting);
|
||||
// The same setting the camera menu's Background effects grid writes, so
|
||||
// the two controls cannot disagree. An image background reads here as blur
|
||||
// off, and turning blur on replaces it.
|
||||
const [effect, setEffect] = useSetting(backgroundEffectSetting);
|
||||
const blurActive = parseEffect(effect).kind === "blur";
|
||||
const setBlurActive = (on: boolean): void =>
|
||||
setEffect(serializeEffect(on ? { kind: "blur" } : { kind: "none" }));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -97,7 +107,7 @@ export const SettingsModal: FC<Props> = ({
|
||||
supported ? "" : t("settings.blur_not_supported_by_browser")
|
||||
}
|
||||
type="checkbox"
|
||||
checked={!!blurActive}
|
||||
checked={blurActive}
|
||||
onChange={(b): void => setBlurActive(b.target.checked)}
|
||||
disabled={!supported}
|
||||
/>
|
||||
|
||||
@@ -123,7 +123,10 @@ export const backgroundBlur = new Setting<boolean>("background-blur", false);
|
||||
*/
|
||||
export const backgroundEffect = new Setting<string>(
|
||||
"background-effect",
|
||||
"none",
|
||||
// Defaults to whatever blur the user had already chosen, so someone who
|
||||
// turned blur on before this existed still has it afterwards. Their own
|
||||
// choice replaces this the moment they make one.
|
||||
backgroundBlur.getValue() ? "blur" : "none",
|
||||
);
|
||||
|
||||
export const showHandRaisedTimer = new Setting<boolean>(
|
||||
|
||||
Reference in New Issue
Block a user