diff --git a/apps/web/playwright/e2e/devtools/lowbandwidth.spec.ts b/apps/web/playwright/e2e/devtools/lowbandwidth.spec.ts new file mode 100644 index 0000000000..d24ac69b94 --- /dev/null +++ b/apps/web/playwright/e2e/devtools/lowbandwidth.spec.ts @@ -0,0 +1,61 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { test, expect } from "../../element-web-test"; +import { getSampleFilePath } from "../../sample-files"; + +test.describe("Devtools", () => { + test.use({ + displayName: "Alice", + }); + + test("should allow enabling low bandwidth mode", async ({ page, homeserver, user, app }) => { + // Upload a picture + const userSettings = await app.settings.openUserSettings("Account"); + const profileSettings = userSettings.locator(".mx_UserProfileSettings"); + await profileSettings.getByAltText("Upload").setInputFiles(getSampleFilePath("riot.png")); + await app.closeDialog(); + + // Create an initial room. + const createRoomDialog = await app.openCreateRoomDialog(); + await createRoomDialog.getByRole("textbox", { name: "Name" }).fill("Test Room"); + await createRoomDialog.getByRole("button", { name: "Create room" }).click(); + + const composer = app.getComposer().locator("[contenteditable]"); + await composer.fill("/devtools"); + await composer.press("Enter"); + const dialog = page.locator(".mx_Dialog"); + await dialog.getByLabel("Developer mode").check(); + await dialog.getByLabel("Disable bandwidth-heavy features").click(); + // Wait for refresh. + await page.waitForEvent("domcontentloaded"); + await app.viewRoomByName("Test Room"); + + // This only appears when encryption has been disabled in the client. + await expect(page.getByText("The encryption used by this room isn't supported.")).toBeVisible(); + + // None of these should be requested. + let hasSentTyping = false; + let hasRequestedThumbnail = false; + await page.route("**/_matrix/client/v3/rooms/*/typing/*", async (route) => { + hasSentTyping = true; + await route.fulfill({ json: {} }); + }); + await page.route("**/_matrix/media/v3/thumbnail/**", async (route) => { + hasRequestedThumbnail = true; + await route.fulfill({ json: {} }); + }); + await page.route("**/_matrix/client/v1/media/thumbnail/**", async (route) => { + hasRequestedThumbnail = true; + await route.fulfill({ json: {} }); + }); + + await composer.pressSequentially("Provoke typing request", { delay: 5 }); + expect(hasSentTyping).toEqual(false); + expect(hasRequestedThumbnail).toEqual(false); + }); +}); diff --git a/apps/web/src/components/views/dialogs/DevtoolsDialog.tsx b/apps/web/src/components/views/dialogs/DevtoolsDialog.tsx index fd006ec264..4c5d5cc431 100644 --- a/apps/web/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/apps/web/src/components/views/dialogs/DevtoolsDialog.tsx @@ -115,6 +115,7 @@ const DevtoolsDialog: React.FC = ({ roomId, threadRootId, onFinished }) + {/* The settings field needs to be outside `Form.Root` because `SettingsField` will have a inner Form, Otherwise we end up with a nester `Form` and that prohibits `preventDefault` so setting the value diff --git a/apps/web/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx b/apps/web/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx index ab0bfd6314..19c2fb7d40 100644 --- a/apps/web/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx +++ b/apps/web/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx @@ -16,7 +16,7 @@ import { SettingLevel } from "../../../../../settings/SettingLevel"; import SdkConfig from "../../../../../SdkConfig"; import BetaCard from "../../../beta/BetaCard"; import SettingsFlag from "../../../elements/SettingsFlag"; -import { type FeatureSettingKey, LabGroup, labGroupNames } from "../../../../../settings/Settings"; +import { type FeatureSettingKey, type LabGroup, labGroupNames } from "../../../../../settings/Settings"; import { EnhancedMap } from "../../../../../utils/maps"; import { SettingsSection } from "../../shared/SettingsSection"; import { SettingsSubsection, SettingsSubsectionText } from "../../shared/SettingsSubsection"; @@ -71,10 +71,6 @@ export default class LabsUserSettingsTab extends React.Component { .push(); }); - groups - .getOrCreate(LabGroup.Experimental, []) - .push(); - labsSections = ( <> {sortBy(Array.from(groups.entries()), "0").map(([group, flags]) => ( diff --git a/apps/web/src/i18n/strings/en_EN.json b/apps/web/src/i18n/strings/en_EN.json index 32d0d255a9..3fad87c427 100644 --- a/apps/web/src/i18n/strings/en_EN.json +++ b/apps/web/src/i18n/strings/en_EN.json @@ -828,8 +828,8 @@ "invalid_device_key_id": "Invalid device key ID", "invalid_json": "Doesn't look like valid JSON.", "level": "Level", - "low_bandwidth_mode": "Low bandwidth mode", - "low_bandwidth_mode_description": "Requires compatible homeserver.", + "low_bandwidth_mode": "Disable bandwidth-heavy features", + "low_bandwidth_mode_description": "Disables encryption, presence, avatars, read receipts, and typing notifications", "main_timeline": "Main timeline", "manual_device_verification": "Manual device verification", "no_receipt_found": "No receipt found", diff --git a/apps/web/src/settings/Settings.tsx b/apps/web/src/settings/Settings.tsx index a3c1565bc8..cb7fccc304 100644 --- a/apps/web/src/settings/Settings.tsx +++ b/apps/web/src/settings/Settings.tsx @@ -327,6 +327,10 @@ export interface Settings { }>; "breadcrumbs": IBaseSetting; "showHiddenEventsInTimeline": IBaseSetting; + /** + * This is the 2019-era low bandwidth that deals with disabling features of the + * client. It does NOT make any API or spec changes. + */ "lowBandwidth": IBaseSetting; "fallbackICEServerAllowed": IBaseSetting; "RoomList.preferredSorting": IBaseSetting; diff --git a/apps/web/test/unit-tests/components/views/dialogs/__snapshots__/DevtoolsDialog-test.tsx.snap b/apps/web/test/unit-tests/components/views/dialogs/__snapshots__/DevtoolsDialog-test.tsx.snap index c756084986..b165dc24b6 100644 --- a/apps/web/test/unit-tests/components/views/dialogs/__snapshots__/DevtoolsDialog-test.tsx.snap +++ b/apps/web/test/unit-tests/components/views/dialogs/__snapshots__/DevtoolsDialog-test.tsx.snap @@ -237,6 +237,50 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = ` +
+
+
+ +
+
+
+
+ + + + + WARNING: + + Disables encryption, presence, avatars, read receipts, and typing notifications + + +
+
@@ -255,7 +299,7 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = ` > ", () => { // non-beta labs section expect(screen.getByText("Early previews")).toBeInTheDocument(); const labsSections = container.getElementsByClassName("mx_SettingsSubsection"); - expect(labsSections).toHaveLength(9); + expect(labsSections).toHaveLength(8); }); });