Allow disabling legacy calls and make Voice & Video settings Legacy Voice & Video (#33692)
* Change `feature_group_calls` to default to true. Change state to Beta * Add `enableLegacyCallsVoip` (clarify: Audio settings relate to Legacy) Change `element_call.use_exclusively` to just be the default of `enableLegacyCallsVoip`. Allow disabling in Legacy in the settings. (overwrite `use_exclusivly` via `enableLegacyCallsVoip`) Rename `Voice & Video` to `Legacy Voice & Video` * improvements - settings title update to only show legacy when appropriate - hide menu all together if use_exclusivly is chosen * simplify by removing the feature_group_calls flag * smaller cleanup * fix tests and cleanup * clarify settings (disable) in config.md * add back EC relevant settings in room settings dialog * update screenshots (now default to EC enabled (not disabled) -> show voice and video tab in room settings * update screenshots * always show option to control voice messages input * lint * fix test * disable -> enabled And only have on mic dropdown * update config.md * Back to `disabled` (on by default/no config) * remove element call hint from voice-video settings * Update apps/web/src/i18n/strings/en_EN.json Co-authored-by: Robin <robin@robin.town> * Update apps/web/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx Co-authored-by: Robin <robin@robin.town> * review * cleanup element call url setting (its deprecated/not used anymore) * clarify, that also jisti will be enabled/disabled by legacy settings flag * remove double section * remove unn exassary element_call configurations * Update en_EN.json * Update VoiceUserSettingsTab.tsx * Make tests use new defaults (enable element call) * Default events power levels * format * review "Enable Legacy voice/video calls" label * more radical usage of defaults in element_call config.json's * Update config.json * Update en_EN.json * Update RoomSettingsDialog-test.tsx.snap * Update useRoomCall.tsx * fix imports * Update request permission promp to latest designs: https://www.figma.com/design/aOEkaJtaBmPy058V7uoqVr/Element-Call-Updates---2026?node-id=45-12591&t=7takL39icZwt9YjL-0 * Update config.json.d.ts * fix usage of `Element` in i18n * Update General-room-settings-tab-should-be-rendered-properly-1-linux.png --------- Co-authored-by: Robin <robin@robin.town>
This commit is contained in:
@@ -135,7 +135,7 @@ export function useMockedCalls() {
|
||||
* Enables the feature flags required for call tests.
|
||||
*/
|
||||
export function enableCalls(): { enabledSettings: Set<string> } {
|
||||
const enabledSettings = new Set(["feature_group_calls", "feature_video_rooms", "feature_element_call_video_rooms"]);
|
||||
const enabledSettings = new Set(["feature_video_rooms", "feature_element_call_video_rooms"]);
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName): any => {
|
||||
if (settingName.startsWith("feature_")) return enabledSettings.has(settingName);
|
||||
if (settingName === "activeCallRoomIds") return [];
|
||||
|
||||
@@ -68,7 +68,6 @@ describe("<LoggedInView />", () => {
|
||||
hideToSRUsers: false,
|
||||
config: {
|
||||
brand: "Test",
|
||||
element_call: {},
|
||||
},
|
||||
currentRoomId: "",
|
||||
currentUserId: "@bob:server",
|
||||
|
||||
@@ -242,7 +242,6 @@ describe("<MatrixChat />", () => {
|
||||
brand: "Test",
|
||||
help_url: "help_url",
|
||||
help_encryption_url: "help_encryption_url",
|
||||
element_call: {},
|
||||
feedback: {
|
||||
existing_issues_url: "https://feedback.org/existing",
|
||||
new_issue_url: "https://feedback.org/new",
|
||||
|
||||
@@ -146,10 +146,7 @@ describe("<RoomSettingsDialog />", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("renders voip settings tab when enabled", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
(settingName) => settingName === "feature_group_calls",
|
||||
);
|
||||
it("always renders voip settings tab when enabled", () => {
|
||||
getComponent();
|
||||
expect(screen.getByTestId("settings-tab-ROOM_VOIP_TAB")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
+29
@@ -32,6 +32,35 @@ NodeList [
|
||||
General
|
||||
</span>
|
||||
</li>,
|
||||
<li
|
||||
aria-controls="mx_tabpanel_ROOM_VOIP_TAB"
|
||||
aria-labelledby="mx_tabpanel_ROOM_VOIP_TAB_label"
|
||||
aria-selected="false"
|
||||
class="mx_AccessibleButton mx_TabbedView_tabLabel"
|
||||
data-testid="settings-tab-ROOM_VOIP_TAB"
|
||||
role="tab"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M8.929 15.1a13.6 13.6 0 0 0 4.654 3.066q2.62 1.036 5.492.923h.008l.003-.004.003-.002-.034-3.124-3.52-.483-1.791 1.792-.645-.322a13.5 13.5 0 0 1-3.496-2.52 13.4 13.4 0 0 1-2.52-3.496l-.322-.645 1.792-1.791-.483-3.52-3.123-.033-.003.002-.003.004v.002a13.65 13.65 0 0 0 .932 5.492A13.4 13.4 0 0 0 8.93 15.1m3.92 4.926a15.6 15.6 0 0 1-5.334-3.511 15.4 15.4 0 0 1-3.505-5.346 15.6 15.6 0 0 1-1.069-6.274 1.93 1.93 0 0 1 .589-1.366c.366-.366.84-.589 1.386-.589h.01l3.163.035a1.96 1.96 0 0 1 1.958 1.694v.005l.487 3.545v.003c.043.297.025.605-.076.907a2 2 0 0 1-.485.773l-.762.762a11.3 11.3 0 0 0 1.806 2.348 11.4 11.4 0 0 0 2.348 1.806l.762-.762a2 2 0 0 1 .774-.485c.302-.1.61-.118.907-.076l3.553.487a1.96 1.96 0 0 1 1.694 1.958l.034 3.174c0 .546-.223 1.02-.588 1.386-.36.36-.827.582-1.363.588a15.3 15.3 0 0 1-6.29-1.062"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="mx_TabbedView_tabLabel_text"
|
||||
id="mx_tabpanel_ROOM_VOIP_TAB_label"
|
||||
>
|
||||
Voice & Video
|
||||
</span>
|
||||
</li>,
|
||||
<li
|
||||
aria-controls="mx_tabpanel_ROOM_SECURITY_TAB"
|
||||
aria-labelledby="mx_tabpanel_ROOM_SECURITY_TAB_label"
|
||||
|
||||
@@ -409,11 +409,7 @@ describe("RoomHeader", () => {
|
||||
|
||||
describe("group call enabled", () => {
|
||||
beforeEach(async () => {
|
||||
SdkConfig.put({
|
||||
features: {
|
||||
feature_group_calls: true,
|
||||
},
|
||||
});
|
||||
SdkConfig.put({});
|
||||
// Enable Element Call
|
||||
client._unstable_getRTCTransports = jest
|
||||
.fn()
|
||||
@@ -478,12 +474,10 @@ describe("RoomHeader", () => {
|
||||
const user = userEvent.setup();
|
||||
mockRoomMembers(room, 3);
|
||||
SdkConfig.add({
|
||||
element_call: { disable: true }, // This test is about Jitsi widget re-pinning, not Element Call
|
||||
setting_defaults: {
|
||||
[UIFeature.Widgets]: true,
|
||||
},
|
||||
features: {
|
||||
feature_group_calls: false,
|
||||
},
|
||||
});
|
||||
// allow calls
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
|
||||
+39
-29
@@ -44,39 +44,17 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Video call"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
data-state="closed"
|
||||
id="radix-react-use-id-1"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="react-use-id-1"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Voice call"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
@@ -89,6 +67,38 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Voice call"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
data-state="closed"
|
||||
id="radix-react-use-id-3"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="react-use-id-4"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m20.958 16.374.039 3.527q0 .427-.33.756-.33.33-.756.33a16 16 0 0 1-6.57-1.105 16.2 16.2 0 0 1-5.563-3.663 16.1 16.1 0 0 1-3.653-5.573 16.3 16.3 0 0 1-1.115-6.56q0-.427.33-.757T4.095 3l3.528.039a1.07 1.07 0 0 1 1.085.93l.543 3.954q.039.271-.039.504a1.1 1.1 0 0 1-.271.426l-1.64 1.64q.505 1.008 1.154 1.909c.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444q.9.65 1.909 1.153l1.64-1.64q.193-.193.426-.27t.504-.04l3.954.543q.406.059.668.359t.262.727"
|
||||
@@ -98,7 +108,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby="react-use-id-3"
|
||||
aria-labelledby="react-use-id-5"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -125,7 +135,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby="react-use-id-4"
|
||||
aria-labelledby="react-use-id-6"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
|
||||
+6
-4
@@ -23,7 +23,7 @@ import userEvent from "@testing-library/user-event";
|
||||
import RolesRoomSettingsTab from "../../../../../../../src/components/views/settings/tabs/room/RolesRoomSettingsTab";
|
||||
import { mkStubRoom, withClientContextRenderOptions, stubClient } from "../../../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../../../../../../src/settings/SettingsStore";
|
||||
import SdkConfig from "../../../../../../../src/SdkConfig";
|
||||
import { ElementCallEventType, ElementCallMemberEventType } from "../../../../../../../src/call-types";
|
||||
|
||||
describe("RolesRoomSettingsTab", () => {
|
||||
@@ -75,11 +75,13 @@ describe("RolesRoomSettingsTab", () => {
|
||||
|
||||
describe("Element Call", () => {
|
||||
const setGroupCallsEnabled = (val: boolean): void => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string): any => {
|
||||
if (name === "feature_group_calls") return val;
|
||||
});
|
||||
SdkConfig.put({ element_call: { disable: !val } });
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
SdkConfig.reset();
|
||||
});
|
||||
|
||||
const getStartCallSelect = (tab: RenderResult): HTMLElement => {
|
||||
return tab.container.querySelector("select[label='Start Element Call calls']")!;
|
||||
};
|
||||
|
||||
@@ -36,10 +36,29 @@ import createRoom, {
|
||||
canEncryptToAllUsers,
|
||||
waitForRoomEncryption,
|
||||
} from "../../src/createRoom";
|
||||
import SettingsStore from "../../src/settings/SettingsStore";
|
||||
import { ElementCallMemberEventType } from "../../src/call-types";
|
||||
import DMRoomMap from "../../src/utils/DMRoomMap";
|
||||
import { PreferredRoomVersions } from "../../src/utils/PreferredRoomVersions";
|
||||
import SdkConfig from "../../src/SdkConfig";
|
||||
|
||||
/**
|
||||
* This should be the same as
|
||||
* { POWER_LEVEL_EVENTS_DEFAULT, [ElementCallMemberEventType.name]: 0 }
|
||||
* to get the most regression coverage we provide the string based snapshot here.
|
||||
*/
|
||||
const POWER_LEVELS_WITH_CALL_MEMBER = {
|
||||
// Default events power levels we always expect
|
||||
"m.room.avatar": 50,
|
||||
"m.room.canonical_alias": 50,
|
||||
"m.room.encryption": 100,
|
||||
"m.room.history_visibility": 100,
|
||||
"m.room.name": 50,
|
||||
"m.room.power_levels": 100,
|
||||
"m.room.server_acl": 100,
|
||||
"m.room.tombstone": 100,
|
||||
// Custom rtc.member event we expect to be 0
|
||||
[ElementCallMemberEventType.name]: 0,
|
||||
};
|
||||
|
||||
describe("createRoom", () => {
|
||||
mockPlatformPeg();
|
||||
@@ -51,7 +70,10 @@ describe("createRoom", () => {
|
||||
DMRoomMap.makeShared(client);
|
||||
});
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
SdkConfig.reset();
|
||||
});
|
||||
|
||||
it("creates a private room", async () => {
|
||||
await createRoom(client, { createOpts: { preset: Preset.PrivateChat } });
|
||||
@@ -63,6 +85,7 @@ describe("createRoom", () => {
|
||||
{ state_key: "", type: "m.room.guest_access", content: { guest_access: "can_join" } },
|
||||
{ type: "m.room.history_visibility", content: { history_visibility: "invited" } },
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,6 +106,7 @@ describe("createRoom", () => {
|
||||
},
|
||||
{ type: "m.room.history_visibility", content: { history_visibility: "invited" } },
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,6 +136,7 @@ describe("createRoom", () => {
|
||||
{ type: "m.room.history_visibility", content: { history_visibility: "invited" } },
|
||||
// Room name is NOT included, since it needs to be encrypted.
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
|
||||
// And the room name, topic and avatar are set later
|
||||
@@ -155,6 +180,7 @@ describe("createRoom", () => {
|
||||
{ type: "m.room.history_visibility", content: { history_visibility: "invited" } },
|
||||
// Room name is NOT included, since it needs to be encrypted.
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
|
||||
// And the avatar is set later
|
||||
@@ -188,6 +214,7 @@ describe("createRoom", () => {
|
||||
{ type: "m.room.history_visibility", content: { history_visibility: "invited" } },
|
||||
// Room name is NOT included, since it needs to be encrypted.
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
|
||||
// And the room name, topic and avatar were not set since we didn't
|
||||
@@ -228,6 +255,7 @@ describe("createRoom", () => {
|
||||
{ type: "m.space.parent", state_key: parentSpace.roomId, content: { canonical: true, via: [] } },
|
||||
{ type: "m.room.history_visibility", content: { history_visibility: "invited" } },
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -238,6 +266,7 @@ describe("createRoom", () => {
|
||||
preset: "public_chat",
|
||||
visibility: "private",
|
||||
initial_state: [{ state_key: "", type: "m.room.guest_access", content: { guest_access: "can_join" } }],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -249,6 +278,7 @@ describe("createRoom", () => {
|
||||
visibility: "private",
|
||||
topic: "My topic",
|
||||
initial_state: [{ state_key: "", type: "m.room.guest_access", content: { guest_access: "can_join" } }],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -266,6 +296,7 @@ describe("createRoom", () => {
|
||||
{ state_key: "", type: "m.room.guest_access", content: { guest_access: "can_join" } },
|
||||
{ type: "m.space.parent", state_key: parentSpace.roomId, content: { canonical: true, via: [] } },
|
||||
],
|
||||
power_level_content_override: { events: POWER_LEVELS_WITH_CALL_MEMBER },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -324,10 +355,6 @@ describe("createRoom", () => {
|
||||
});
|
||||
|
||||
it("correctly sets up MSC3401 power levels", async () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string): any => {
|
||||
if (name === "feature_group_calls") return true;
|
||||
});
|
||||
|
||||
await createRoom(client, {});
|
||||
|
||||
const callMemberPower =
|
||||
|
||||
@@ -22,9 +22,8 @@ import { ScopedRoomContextProvider } from "../../../src/contexts/ScopedRoomConte
|
||||
import RoomContext, { type RoomContextType } from "../../../src/contexts/RoomContext";
|
||||
import { MatrixClientContextProvider } from "../../../src/components/structures/MatrixClientContextProvider";
|
||||
import type LegacyCallHandler from "../../../src/LegacyCallHandler";
|
||||
import { SDKContextClass } from "../../../src/contexts/SDKContextClass";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { CallStore } from "../../../src/stores/CallStore";
|
||||
import { SDKContextClass } from "../../../src/contexts/SDKContextClass";
|
||||
|
||||
describe("useRoomCall", () => {
|
||||
const client = getMockClientWithEventEmitter({
|
||||
@@ -62,11 +61,6 @@ describe("useRoomCall", () => {
|
||||
jest.spyOn(SDKContextClass.instance, "legacyCallHandler", "get").mockReturnValue(
|
||||
callHandler as unknown as LegacyCallHandler,
|
||||
);
|
||||
const origGetValue = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name, ...params): any => {
|
||||
if (name === "feature_group_calls") return true;
|
||||
return origGetValue(name, ...params);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
Reference in New Issue
Block a user