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 {
|
import {
|
||||||
useSetting,
|
useSetting,
|
||||||
soundEffectVolume as soundEffectVolumeSetting,
|
soundEffectVolume as soundEffectVolumeSetting,
|
||||||
backgroundBlur as backgroundBlurSetting,
|
backgroundEffect as backgroundEffectSetting,
|
||||||
developerMode,
|
developerMode,
|
||||||
} from "./settings";
|
} from "./settings";
|
||||||
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
||||||
import { Slider } from "../Slider";
|
import { Slider } from "../Slider";
|
||||||
import { DeviceSelection } from "./DeviceSelection";
|
import { DeviceSelection } from "./DeviceSelection";
|
||||||
import { useTrackProcessor } from "../livekit/TrackProcessorContext";
|
import { useTrackProcessor } from "../livekit/TrackProcessorContext";
|
||||||
|
import {
|
||||||
|
parseEffect,
|
||||||
|
serializeEffect,
|
||||||
|
} from "../livekit/backgroundEffects";
|
||||||
import {
|
import {
|
||||||
DeveloperSettingsTab,
|
DeveloperSettingsTab,
|
||||||
type DeveloperSettingsSnapshot,
|
type DeveloperSettingsSnapshot,
|
||||||
@@ -83,7 +87,13 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
const BlurCheckbox: React.FC = (): ReactNode => {
|
const BlurCheckbox: React.FC = (): ReactNode => {
|
||||||
const { supported } = useTrackProcessor();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -97,7 +107,7 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
supported ? "" : t("settings.blur_not_supported_by_browser")
|
supported ? "" : t("settings.blur_not_supported_by_browser")
|
||||||
}
|
}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={!!blurActive}
|
checked={blurActive}
|
||||||
onChange={(b): void => setBlurActive(b.target.checked)}
|
onChange={(b): void => setBlurActive(b.target.checked)}
|
||||||
disabled={!supported}
|
disabled={!supported}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -123,7 +123,10 @@ export const backgroundBlur = new Setting<boolean>("background-blur", false);
|
|||||||
*/
|
*/
|
||||||
export const backgroundEffect = new Setting<string>(
|
export const backgroundEffect = new Setting<string>(
|
||||||
"background-effect",
|
"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>(
|
export const showHandRaisedTimer = new Setting<boolean>(
|
||||||
|
|||||||
Reference in New Issue
Block a user