mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 12:16:53 +02:00
Update URL Preview settings (#32992)
* Remove ability for url previews to be set per-room * Add ability to enable E2EE URL Previews globally * Remove old migration * Cleanup * Remove room account handler * update snap * screenshot updated * Add a test
This commit is contained in:
parent
253dcb44dd
commit
b4d0c21abf
Binary file not shown.
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 268 KiB |
@ -1380,12 +1380,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
|
||||
|
||||
if (ev.getType() === "org.matrix.room.preview_urls") {
|
||||
this.updatePreviewUrlVisibility(room);
|
||||
this.updatePreviewUrlVisibility();
|
||||
}
|
||||
|
||||
if (ev.getType() === "m.room.encryption") {
|
||||
this.updateE2EStatus(room);
|
||||
this.updatePreviewUrlVisibility(room);
|
||||
this.updatePreviewUrlVisibility();
|
||||
}
|
||||
|
||||
// ignore anything but real-time updates at the end of the room:
|
||||
@ -1541,15 +1541,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
});
|
||||
}
|
||||
|
||||
private updatePreviewUrlVisibility(room: Room): void {
|
||||
private updatePreviewUrlVisibility(): void {
|
||||
this.setState(({ isRoomEncrypted }) => ({
|
||||
showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted),
|
||||
showUrlPreview: this.getPreviewUrlVisibility(isRoomEncrypted),
|
||||
}));
|
||||
}
|
||||
|
||||
private getPreviewUrlVisibility({ roomId }: Room, isRoomEncrypted: boolean | null): boolean {
|
||||
const key = isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
||||
return SettingsStore.getValue(key, roomId);
|
||||
private getPreviewUrlVisibility(isRoomEncrypted: boolean | null): boolean {
|
||||
return SettingsStore.getValue(isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled");
|
||||
}
|
||||
|
||||
private onRoom = (room: Room): void => {
|
||||
@ -1608,9 +1607,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
}
|
||||
|
||||
private onUrlPreviewsEnabledChange = (): void => {
|
||||
if (this.state.room) {
|
||||
this.updatePreviewUrlVisibility(this.state.room);
|
||||
}
|
||||
this.updatePreviewUrlVisibility();
|
||||
};
|
||||
|
||||
private onRoomStateEvents = async (ev: MatrixEvent, state: RoomState): Promise<void> => {
|
||||
@ -1638,7 +1635,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
|
||||
this.setState({
|
||||
isRoomEncrypted,
|
||||
showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted),
|
||||
showUrlPreview: this.getPreviewUrlVisibility(isRoomEncrypted),
|
||||
...(newE2EStatus && { e2eStatus: newE2EStatus }),
|
||||
});
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ interface IProps {
|
||||
label?: string;
|
||||
isExplicit?: boolean;
|
||||
hideIfCannotSet?: boolean;
|
||||
requires?: BooleanSettingKey[];
|
||||
onChange?(checked: boolean): void;
|
||||
}
|
||||
|
||||
@ -45,6 +46,12 @@ export default class SettingsFlag extends React.Component<IProps, IState> {
|
||||
|
||||
public componentDidMount(): void {
|
||||
defaultWatchManager.watchSetting(this.props.name, this.props.roomId ?? null, this.onSettingChange);
|
||||
if (this.props.requires) {
|
||||
// If we have any dependencies for this feature, also watch those features to ensure we catch the disabled state.
|
||||
for (const flag of this.props.requires) {
|
||||
defaultWatchManager.watchSetting(flag, this.props.roomId ?? null, this.onSettingChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
|
||||
@ -1,152 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2018, 2019 New Vector Ltd
|
||||
Copyright 2017 Travis Ralston
|
||||
Copyright 2016 OpenMarket 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 React, { type ReactNode, type JSX } from "react";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
import { InlineSpinner } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import SettingsFlag from "../elements/SettingsFlag";
|
||||
import SettingsFieldset from "../settings/SettingsFieldset";
|
||||
import AccessibleButton, { type ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { useIsEncrypted } from "../../../hooks/useIsEncrypted.ts";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext.tsx";
|
||||
import { useSettingValueAt } from "../../../hooks/useSettings.ts";
|
||||
|
||||
/**
|
||||
* The URL preview settings for a room
|
||||
*/
|
||||
interface UrlPreviewSettingsProps {
|
||||
/**
|
||||
* The room.
|
||||
*/
|
||||
room: Room;
|
||||
}
|
||||
|
||||
export function UrlPreviewSettings({ room }: UrlPreviewSettingsProps): JSX.Element {
|
||||
const { roomId } = room;
|
||||
const matrixClient = useMatrixClientContext();
|
||||
const isEncrypted = useIsEncrypted(matrixClient, room);
|
||||
const isLoading = isEncrypted === null;
|
||||
|
||||
return (
|
||||
<SettingsFieldset
|
||||
legend={_t("room_settings|general|url_previews_section")}
|
||||
description={!isLoading && <Description isEncrypted={isEncrypted} />}
|
||||
>
|
||||
{isLoading ? (
|
||||
<InlineSpinner />
|
||||
) : (
|
||||
<>
|
||||
<PreviewsForRoom isEncrypted={isEncrypted} roomId={roomId} />
|
||||
<SettingsFlag
|
||||
name={isEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled"}
|
||||
level={SettingLevel.ROOM_DEVICE}
|
||||
roomId={roomId}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</SettingsFieldset>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for the user settings link
|
||||
* @param e
|
||||
*/
|
||||
function onClickUserSettings(e: ButtonEvent): void {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dis.fire(Action.ViewUserSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* The description for the URL preview settings
|
||||
*/
|
||||
interface DescriptionProps {
|
||||
/**
|
||||
* Whether the room is encrypted
|
||||
*/
|
||||
isEncrypted: boolean;
|
||||
}
|
||||
|
||||
function Description({ isEncrypted }: DescriptionProps): JSX.Element {
|
||||
const urlPreviewsEnabled = useSettingValueAt(SettingLevel.ACCOUNT, "urlPreviewsEnabled");
|
||||
|
||||
let previewsForAccount: ReactNode | undefined;
|
||||
if (isEncrypted) {
|
||||
previewsForAccount = _t("room_settings|general|url_preview_encryption_warning");
|
||||
} else {
|
||||
const button = {
|
||||
a: (sub: string) => (
|
||||
<AccessibleButton kind="link_inline" onClick={onClickUserSettings}>
|
||||
{sub}
|
||||
</AccessibleButton>
|
||||
),
|
||||
};
|
||||
|
||||
previewsForAccount = urlPreviewsEnabled
|
||||
? _t("room_settings|general|user_url_previews_default_on", {}, button)
|
||||
: _t("room_settings|general|user_url_previews_default_off", {}, button);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{_t("room_settings|general|url_preview_explainer")}</p>
|
||||
<p>{previewsForAccount}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The description for the URL preview settings
|
||||
*/
|
||||
interface PreviewsForRoomProps {
|
||||
/**
|
||||
* Whether the room is encrypted
|
||||
*/
|
||||
isEncrypted: boolean;
|
||||
/**
|
||||
* The room ID
|
||||
*/
|
||||
roomId: string;
|
||||
}
|
||||
|
||||
function PreviewsForRoom({ isEncrypted, roomId }: PreviewsForRoomProps): JSX.Element | null {
|
||||
const urlPreviewsEnabled = useSettingValueAt(
|
||||
SettingLevel.ACCOUNT,
|
||||
"urlPreviewsEnabled",
|
||||
roomId,
|
||||
/*explicit=*/ true,
|
||||
);
|
||||
if (isEncrypted) return null;
|
||||
|
||||
let previewsForRoom: ReactNode;
|
||||
if (SettingsStore.canSetValue("urlPreviewsEnabled", roomId, SettingLevel.ROOM)) {
|
||||
previewsForRoom = (
|
||||
<SettingsFlag name="urlPreviewsEnabled" level={SettingLevel.ROOM} roomId={roomId} isExplicit={true} />
|
||||
);
|
||||
} else {
|
||||
previewsForRoom = (
|
||||
<div>
|
||||
{urlPreviewsEnabled
|
||||
? _t("room_settings|general|default_url_previews_on")
|
||||
: _t("room_settings|general|default_url_previews_off")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return previewsForRoom;
|
||||
}
|
||||
@ -15,14 +15,11 @@ import RoomProfileSettings from "../../../room_settings/RoomProfileSettings";
|
||||
import AccessibleButton, { type ButtonEvent } from "../../../elements/AccessibleButton";
|
||||
import dis from "../../../../../dispatcher/dispatcher";
|
||||
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { UIFeature } from "../../../../../settings/UIFeature";
|
||||
import AliasSettings from "../../../room_settings/AliasSettings";
|
||||
import PosthogTrackers from "../../../../../PosthogTrackers";
|
||||
import { SettingsSubsection } from "../../shared/SettingsSubsection";
|
||||
import SettingsTab from "../SettingsTab";
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import { UrlPreviewSettings } from "../../../room_settings/UrlPreviewSettings";
|
||||
import { MediaPreviewAccountSettings } from "../user/MediaPreviewAccountSettings";
|
||||
|
||||
interface IProps {
|
||||
@ -62,10 +59,6 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
|
||||
const canSetCanonical = room.currentState.mayClientSendStateEvent("m.room.canonical_alias", client);
|
||||
const canonicalAliasEv = room.currentState.getStateEvents("m.room.canonical_alias", "") ?? undefined;
|
||||
|
||||
const urlPreviewSettings = SettingsStore.getValue(UIFeature.URLPreviews) ? (
|
||||
<UrlPreviewSettings room={room} />
|
||||
) : null;
|
||||
|
||||
let leaveSection;
|
||||
if (room.getMyMembership() === KnownMembership.Join) {
|
||||
leaveSection = (
|
||||
@ -99,7 +92,6 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection heading={_t("room_settings|general|other_section")}>
|
||||
{urlPreviewSettings}
|
||||
<SettingsSubsection heading={_t("common|moderation_and_safety")} legacy={false}>
|
||||
<MediaPreviewAccountSettings roomId={room.roomId} />
|
||||
</SettingsSubsection>
|
||||
|
||||
@ -147,11 +147,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||
"showCodeLineNumbers",
|
||||
];
|
||||
|
||||
private static IMAGES_AND_VIDEOS_SETTINGS: BooleanSettingKey[] = [
|
||||
"urlPreviewsEnabled",
|
||||
"autoplayGifs",
|
||||
"autoplayVideo",
|
||||
];
|
||||
private static IMAGES_AND_VIDEOS_SETTINGS: BooleanSettingKey[] = ["autoplayGifs", "autoplayVideo"];
|
||||
|
||||
private static TIMELINE_SETTINGS: BooleanSettingKey[] = [
|
||||
"showTypingNotifications",
|
||||
@ -350,6 +346,19 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||
{this.renderGroup(PreferencesUserSettingsTab.CODE_BLOCKS_SETTINGS)}
|
||||
</SettingsSubsection>
|
||||
|
||||
<SettingsSubsection
|
||||
heading={_t("settings|preferences|link_previews_heading")}
|
||||
description={_t("settings|preferences|link_previews_description")}
|
||||
formWrap
|
||||
>
|
||||
<SettingsFlag name="urlPreviewsEnabled" level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag
|
||||
name="urlPreviewsEnabled_e2ee"
|
||||
level={SettingLevel.DEVICE}
|
||||
requires={["urlPreviewsEnabled"]}
|
||||
/>
|
||||
</SettingsSubsection>
|
||||
|
||||
<SettingsSubsection heading={_t("settings|preferences|media_heading")} formWrap>
|
||||
{this.renderGroup(PreferencesUserSettingsTab.IMAGES_AND_VIDEOS_SETTINGS)}
|
||||
</SettingsSubsection>
|
||||
|
||||
@ -2238,8 +2238,6 @@
|
||||
"aliases_section": "Room Addresses",
|
||||
"avatar_field_label": "Room avatar",
|
||||
"canonical_alias_field_label": "Main address",
|
||||
"default_url_previews_off": "URL previews are disabled by default for participants in this room.",
|
||||
"default_url_previews_on": "URL previews are enabled by default for participants in this room.",
|
||||
"description_space": "Edit settings relating to your space.",
|
||||
"error_creating_alias_description": "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.",
|
||||
"error_creating_alias_title": "Error creating address",
|
||||
@ -2270,12 +2268,7 @@
|
||||
"published_aliases_explainer_space": "Published addresses can be used by anyone on any server to join your space.",
|
||||
"published_aliases_section": "Published Addresses",
|
||||
"save": "Save Changes",
|
||||
"topic_field_label": "Room Topic",
|
||||
"url_preview_encryption_warning": "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.",
|
||||
"url_preview_explainer": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.",
|
||||
"url_previews_section": "URL Previews",
|
||||
"user_url_previews_default_off": "You have <a>disabled</a> URL previews by default.",
|
||||
"user_url_previews_default_on": "You have <a>enabled</a> URL previews by default."
|
||||
"topic_field_label": "Room Topic"
|
||||
},
|
||||
"notifications": {
|
||||
"browse_button": "Browse",
|
||||
@ -2697,9 +2690,8 @@
|
||||
"unable_to_load_msisdns": "Unable to load phone numbers",
|
||||
"username": "Username"
|
||||
},
|
||||
"inline_url_previews_default": "Enable inline URL previews by default",
|
||||
"inline_url_previews_room": "Enable URL previews by default for participants in this room",
|
||||
"inline_url_previews_room_account": "Enable URL previews for this room (only affects you)",
|
||||
"inline_url_previews_default": "Enable previews",
|
||||
"inline_url_previews_encrypted": "Enable previews in encrypted rooms",
|
||||
"insert_trailing_colon_mentions": "Insert a trailing colon after user mentions at the start of a message",
|
||||
"invite_controls": {
|
||||
"default_label": "Allow users to invite you to rooms"
|
||||
@ -2837,6 +2829,8 @@
|
||||
"enable_tray_icon": "Show tray icon and minimise window to it on close",
|
||||
"keyboard_heading": "Keyboard shortcuts",
|
||||
"keyboard_view_shortcuts_button": "To view all keyboard shortcuts, <a>click here</a>.",
|
||||
"link_previews_description": "Shows information about links underneath messages",
|
||||
"link_previews_heading": "Link previews",
|
||||
"media_heading": "Images, GIFs and videos",
|
||||
"presence_description": "Share your activity and status with others.",
|
||||
"publish_timezone": "Publish timezone on public profile",
|
||||
|
||||
@ -51,6 +51,7 @@ import MediaPreviewConfigController from "./controllers/MediaPreviewConfigContro
|
||||
import InviteRulesConfigController from "./controllers/InviteRulesConfigController.ts";
|
||||
import { type ComputedInviteConfig } from "../@types/invite-rules.ts";
|
||||
import BlockInvitesConfigController from "./controllers/BlockInvitesConfigController.ts";
|
||||
import RequiresSettingsController from "./controllers/RequiresSettingsController.ts";
|
||||
|
||||
export const defaultWatchManager = new WatchManager();
|
||||
|
||||
@ -1140,22 +1141,22 @@ export const SETTINGS: Settings = {
|
||||
controller: new UIFeatureController(UIFeature.AdvancedEncryption),
|
||||
},
|
||||
"urlPreviewsEnabled": {
|
||||
supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
|
||||
displayName: {
|
||||
"default": _td("settings|inline_url_previews_default"),
|
||||
"room-account": _td("settings|inline_url_previews_room_account"),
|
||||
"room": _td("settings|inline_url_previews_room"),
|
||||
},
|
||||
// Enabled by default and client configurable as this setting only allows unencrypted
|
||||
// messages to be previewed.
|
||||
supportedLevels: [SettingLevel.DEVICE, SettingLevel.ACCOUNT, SettingLevel.CONFIG],
|
||||
supportedLevelsAreOrdered: true,
|
||||
displayName: _td("settings|inline_url_previews_default"),
|
||||
default: true,
|
||||
controller: new UIFeatureController(UIFeature.URLPreviews),
|
||||
},
|
||||
"urlPreviewsEnabled_e2ee": {
|
||||
supportedLevels: [SettingLevel.ROOM_DEVICE],
|
||||
displayName: {
|
||||
"room-device": _td("settings|inline_url_previews_room_account"),
|
||||
},
|
||||
// Can only be enabled per-device to ensure neither the homeserver nor client config
|
||||
// can impact the user's choices.
|
||||
supportedLevels: [SettingLevel.DEVICE],
|
||||
supportedLevelsAreOrdered: true,
|
||||
displayName: _td("settings|inline_url_previews_encrypted"),
|
||||
default: false,
|
||||
controller: new UIFeatureController(UIFeature.URLPreviews),
|
||||
controller: new RequiresSettingsController([UIFeature.URLPreviews, "urlPreviewsEnabled"]),
|
||||
},
|
||||
"notificationsEnabled": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
|
||||
@ -654,40 +654,6 @@ export default class SettingsStore {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the setting for URL previews in e2e rooms from room account
|
||||
* data to the room device level.
|
||||
*
|
||||
* @param isFreshLogin True if the user has just logged in, false if a previous session is being restored.
|
||||
*/
|
||||
private static async migrateURLPreviewsE2EE(isFreshLogin: boolean): Promise<void> {
|
||||
const MIGRATION_DONE_FLAG = "url_previews_e2ee_migration_done";
|
||||
if (localStorage.getItem(MIGRATION_DONE_FLAG)) return;
|
||||
if (isFreshLogin) return;
|
||||
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
|
||||
while (!client.isInitialSyncComplete()) {
|
||||
await new Promise((r) => client.once(ClientEvent.Sync, r));
|
||||
}
|
||||
|
||||
logger.info("Performing one-time settings migration of URL previews in E2EE rooms");
|
||||
|
||||
const roomAccounthandler = LEVEL_HANDLERS[SettingLevel.ROOM_ACCOUNT];
|
||||
|
||||
for (const room of client.getRooms()) {
|
||||
// We need to use the handler directly because this setting is no longer supported
|
||||
// at this level at all
|
||||
const val = roomAccounthandler.getValue("urlPreviewsEnabled_e2ee", room.roomId);
|
||||
|
||||
if (val !== undefined) {
|
||||
await SettingsStore.setValue("urlPreviewsEnabled_e2ee", room.roomId, SettingLevel.ROOM_DEVICE, val);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(MIGRATION_DONE_FLAG, "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the setting for visible images to a setting.
|
||||
*/
|
||||
@ -739,15 +705,6 @@ export default class SettingsStore {
|
||||
* Runs or queues any setting migrations needed.
|
||||
*/
|
||||
public static runMigrations(isFreshLogin: boolean): void {
|
||||
// This can be removed once enough users have run a version of Element with
|
||||
// this migration. A couple of months after its release should be sufficient
|
||||
// (so around October 2024).
|
||||
// The consequences of missing the migration are only that URL previews will
|
||||
// be disabled in E2EE rooms.
|
||||
SettingsStore.migrateURLPreviewsE2EE(isFreshLogin).catch((e) => {
|
||||
logger.error("Failed to migrate URL previews in E2EE rooms:", e);
|
||||
});
|
||||
|
||||
// This can be removed once enough users have run a version of Element with
|
||||
// this migration.
|
||||
// The consequences of missing the migration are that previously shown images
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
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 SettingController from "./SettingController";
|
||||
import SettingsStore from "../SettingsStore";
|
||||
import type { BooleanSettingKey } from "../Settings.tsx";
|
||||
|
||||
/**
|
||||
* Disables a setting & forces it's value if one or more settings are not enabled
|
||||
*/
|
||||
export default class RequiresSettingsController extends SettingController {
|
||||
public constructor(
|
||||
public readonly settingNames: BooleanSettingKey[],
|
||||
private forcedValue = false,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public getValueOverride(): any {
|
||||
if (this.settingDisabled) {
|
||||
// per the docs: we force a disabled state when the feature isn't active
|
||||
return this.forcedValue;
|
||||
}
|
||||
return null; // no override
|
||||
}
|
||||
|
||||
public get settingDisabled(): boolean {
|
||||
return this.settingNames.some((s) => !SettingsStore.getValue(s));
|
||||
}
|
||||
}
|
||||
@ -76,15 +76,6 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin
|
||||
};
|
||||
|
||||
public getValue(settingName: string, roomId: string): any {
|
||||
// Special case URL previews
|
||||
if (settingName === "urlPreviewsEnabled") {
|
||||
const content = this.getSettings(roomId, "org.matrix.room.preview_urls") || {};
|
||||
|
||||
// Check to make sure that we actually got a boolean
|
||||
if (typeof content["disable"] !== "boolean") return null;
|
||||
return !content["disable"];
|
||||
}
|
||||
|
||||
// Special case allowed widgets
|
||||
if (settingName === "allowedWidgets") {
|
||||
return this.getSettings(roomId, ALLOWED_WIDGETS_EVENT_TYPE);
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector 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 React from "react";
|
||||
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import { waitFor } from "@testing-library/dom";
|
||||
import { Form } from "@vector-im/compound-web";
|
||||
|
||||
import { createTestClient, mkStubRoom, withClientContextRenderOptions } from "../../../../test-utils";
|
||||
import { UrlPreviewSettings } from "../../../../../src/components/views/room_settings/UrlPreviewSettings.tsx";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore.ts";
|
||||
import dis from "../../../../../src/dispatcher/dispatcher.ts";
|
||||
import { Action } from "../../../../../src/dispatcher/actions.ts";
|
||||
|
||||
describe("UrlPreviewSettings", () => {
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
client = createTestClient();
|
||||
room = mkStubRoom("roomId", "room", client);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
function renderComponent() {
|
||||
return render(
|
||||
<Form.Root>
|
||||
<UrlPreviewSettings room={room} />
|
||||
</Form.Root>,
|
||||
withClientContextRenderOptions(client),
|
||||
);
|
||||
}
|
||||
|
||||
it("should display the correct preview when the setting is in a loading state", () => {
|
||||
jest.spyOn(client, "getCrypto").mockReturnValue(undefined);
|
||||
const { asFragment } = renderComponent();
|
||||
expect(screen.getByText("URL Previews")).toBeInTheDocument();
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display the correct preview when the room is encrypted and the url preview is enabled", async () => {
|
||||
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(true);
|
||||
|
||||
const { asFragment } = renderComponent();
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display the correct preview when the room is unencrypted and the url preview is enabled", async () => {
|
||||
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(false);
|
||||
jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(true);
|
||||
jest.spyOn(dis, "fire").mockReturnValue(undefined);
|
||||
|
||||
const { asFragment } = renderComponent();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "enabled" })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("URL previews are enabled by default for participants in this room."),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
screen.getByRole("button", { name: "enabled" }).click();
|
||||
expect(dis.fire).toHaveBeenCalledWith(Action.ViewUserSettings);
|
||||
});
|
||||
|
||||
it("should display the correct preview when the room is unencrypted and the url preview is disabled", async () => {
|
||||
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(false);
|
||||
jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(false);
|
||||
|
||||
const { asFragment } = renderComponent();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "disabled" })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("URL previews are disabled by default for participants in this room."),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@ -1,270 +0,0 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`UrlPreviewSettings should display the correct preview when the room is encrypted and the url preview is enabled 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
URL Previews
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
<p>
|
||||
When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.
|
||||
</p>
|
||||
<p>
|
||||
In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
id="mx_SettingsFlag_vY7Q4uEh9K38"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_vY7Q4uEh9K38"
|
||||
>
|
||||
Enable URL previews for this room (only affects you)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`UrlPreviewSettings should display the correct preview when the room is unencrypted and the url preview is disabled 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
URL Previews
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
<p>
|
||||
When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
You have
|
||||
</span>
|
||||
</p>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
disabled
|
||||
</div>
|
||||
URL previews by default.
|
||||
<p />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<div>
|
||||
URL previews are disabled by default for participants in this room.
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_vY7Q4uEh9K38"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_vY7Q4uEh9K38"
|
||||
>
|
||||
Enable inline URL previews by default
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`UrlPreviewSettings should display the correct preview when the room is unencrypted and the url preview is enabled 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
URL Previews
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
<p>
|
||||
When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
You have
|
||||
</span>
|
||||
</p>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
enabled
|
||||
</div>
|
||||
URL previews by default.
|
||||
<p />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<div>
|
||||
URL previews are enabled by default for participants in this room.
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
id="mx_SettingsFlag_vY7Q4uEh9K38"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_vY7Q4uEh9K38"
|
||||
>
|
||||
Enable inline URL previews by default
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`UrlPreviewSettings should display the correct preview when the setting is in a loading state 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
URL Previews
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<svg
|
||||
class="_icon_11k6c_18"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
style="width: 20px; height: 20px;"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M12 4.031a8 8 0 1 0 8 8 1 1 0 0 1 2 0c0 5.523-4.477 10-10 10s-10-4.477-10-10 4.477-10 10-10a1 1 0 1 1 0 2"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@ -905,9 +905,18 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<h2
|
||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Images, GIFs and videos
|
||||
Link previews
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
Shows information about links underneath messages
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
@ -923,7 +932,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_vfGFMldL2r2v"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
@ -940,7 +948,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_vfGFMldL2r2v"
|
||||
>
|
||||
Enable inline URL previews by default
|
||||
Enable previews
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -955,7 +963,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<input
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_bsSwicmKUiOB"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
@ -972,10 +979,31 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_bsSwicmKUiOB"
|
||||
>
|
||||
Autoplay GIFs
|
||||
Enable previews in encrypted rooms
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Images, GIFs and videos
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
@ -1003,6 +1031,38 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_dvqsxEaZtl3A"
|
||||
>
|
||||
Autoplay GIFs
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
>
|
||||
Autoplay videos
|
||||
</label>
|
||||
@ -1029,39 +1089,6 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_NIiWzqsApP1c"
|
||||
>
|
||||
Show typing notifications
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
@ -1091,7 +1118,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_q1SIAPqLMVXh"
|
||||
>
|
||||
Show a placeholder for removed messages
|
||||
Show typing notifications
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1124,7 +1151,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_dXFDGgBsKXay"
|
||||
>
|
||||
Show read receipts sent by other users
|
||||
Show a placeholder for removed messages
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1157,7 +1184,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_7Az0xw4Bs4Tt"
|
||||
>
|
||||
Show join/leave messages (invites/removes/bans unaffected)
|
||||
Show read receipts sent by other users
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1190,7 +1217,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_8jmzPIlPoBCv"
|
||||
>
|
||||
Show display name changes
|
||||
Show join/leave messages (invites/removes/bans unaffected)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1223,7 +1250,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_enFRaTjdsFou"
|
||||
>
|
||||
Show chat effects (animations when receiving e.g. confetti)
|
||||
Show display name changes
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1256,7 +1283,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_bfwnd5rz4XNX"
|
||||
>
|
||||
Show profile picture changes
|
||||
Show chat effects (animations when receiving e.g. confetti)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1289,7 +1316,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_gs5uWEzYzZrS"
|
||||
>
|
||||
Show avatars in user, room and event mentions
|
||||
Show profile picture changes
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1322,7 +1349,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_qWg7OgID1yRR"
|
||||
>
|
||||
Enable big emoji in chat
|
||||
Show avatars in user, room and event mentions
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1355,7 +1382,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_pOPewl7rtMbV"
|
||||
>
|
||||
Jump to the bottom of the timeline when you send a message
|
||||
Enable big emoji in chat
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -1387,6 +1414,39 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_cmt3PZSyNp3v"
|
||||
>
|
||||
Jump to the bottom of the timeline when you send a message
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
>
|
||||
Show current profile picture and name for users in message history
|
||||
</label>
|
||||
@ -1424,7 +1484,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<input
|
||||
class="_input_udcm8_24"
|
||||
id="_r_24_"
|
||||
id="_r_26_"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
@ -1438,7 +1498,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="_r_24_"
|
||||
for="_r_26_"
|
||||
>
|
||||
Hide avatars of room and inviter
|
||||
</label>
|
||||
@ -1452,13 +1512,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="radix-_r_26_"
|
||||
for="radix-_r_28_"
|
||||
>
|
||||
Show media in timeline
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91 mx_MediaPreviewAccountSetting_RadioHelp"
|
||||
id="radix-_r_27_"
|
||||
id="radix-_r_29_"
|
||||
>
|
||||
A hidden media can always be shown by tapping on it
|
||||
</span>
|
||||
@ -1571,7 +1631,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="_r_2b_"
|
||||
id="_r_2d_"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
@ -1585,13 +1645,13 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="_r_2b_"
|
||||
for="_r_2d_"
|
||||
>
|
||||
Allow users to invite you to rooms
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id="radix-_r_2d_"
|
||||
id="radix-_r_2f_"
|
||||
>
|
||||
Your server does not implement this feature.
|
||||
</span>
|
||||
@ -1636,7 +1696,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
<input
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
id="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
@ -1650,7 +1710,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_dJJz3lHUv9XX"
|
||||
for="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
>
|
||||
Show NSFW content
|
||||
</label>
|
||||
@ -1690,7 +1750,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
id="mx_SettingsFlag_FLEpLCb0jpp6"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
@ -1704,7 +1764,7 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_SettingsFlag_SBSSOZDRlzlA"
|
||||
for="mx_SettingsFlag_FLEpLCb0jpp6"
|
||||
>
|
||||
Prompt before sending invites to potentially invalid matrix IDs
|
||||
</label>
|
||||
|
||||
@ -96,63 +96,18 @@ describe("SettingsStore", () => {
|
||||
describe("runMigrations", () => {
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
let localStorageSetItemSpy: jest.SpyInstance;
|
||||
let localStorageSetPromise: Promise<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
client = stubClient();
|
||||
room = mkStubRoom("!room:example.org", "Room", client);
|
||||
room.getAccountData = jest.fn().mockReturnValue({
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
urlPreviewsEnabled_e2ee: true,
|
||||
}),
|
||||
});
|
||||
client.getRooms = jest.fn().mockReturnValue([room]);
|
||||
client.getRoom = jest.fn().mockReturnValue(room);
|
||||
|
||||
localStorageSetPromise = new Promise((resolve) => {
|
||||
localStorageSetItemSpy = jest
|
||||
.spyOn(localStorage.__proto__, "setItem")
|
||||
.mockImplementation(() => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("migrates URL previews setting for e2ee rooms", async () => {
|
||||
SettingsStore.runMigrations(false);
|
||||
client.emit(ClientEvent.Sync, SyncState.Prepared, null);
|
||||
|
||||
expect(room.getAccountData).toHaveBeenCalled();
|
||||
|
||||
await localStorageSetPromise;
|
||||
|
||||
expect(localStorageSetItemSpy!).toHaveBeenCalledWith(
|
||||
`mx_setting_urlPreviewsEnabled_e2ee_${room.roomId}`,
|
||||
JSON.stringify({ value: true }),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not migrate e2ee URL previews on a fresh login", async () => {
|
||||
SettingsStore.runMigrations(true);
|
||||
client.emit(ClientEvent.Sync, SyncState.Prepared, null);
|
||||
|
||||
expect(room.getAccountData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not migrate if the device is flagged as migrated", async () => {
|
||||
jest.spyOn(localStorage.__proto__, "getItem").mockImplementation((key: unknown): string | undefined => {
|
||||
if (key === "url_previews_e2ee_migration_done") return JSON.stringify({ value: true });
|
||||
return undefined;
|
||||
});
|
||||
SettingsStore.runMigrations(false);
|
||||
client.emit(ClientEvent.Sync, SyncState.Prepared, null);
|
||||
|
||||
expect(room.getAccountData).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("Migrate media preview configuration", () => {
|
||||
beforeEach(() => {
|
||||
MatrixClientBackedController.matrixClient = client;
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
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 RequiresSettingsController from "../../../../src/settings/controllers/RequiresSettingsController";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
|
||||
describe("RequiresSettingsController", () => {
|
||||
afterEach(() => {
|
||||
SettingsStore.reset();
|
||||
});
|
||||
|
||||
it("forces a value if a setting is false", async () => {
|
||||
const forcedValue = true;
|
||||
await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
|
||||
await SettingsStore.setValue("useCustomFontSize", null, SettingLevel.DEVICE, false);
|
||||
const controller = new RequiresSettingsController(["useCompactLayout", "useCustomFontSize"], forcedValue);
|
||||
expect(controller.settingDisabled).toEqual(true);
|
||||
expect(controller.getValueOverride()).toEqual(forcedValue);
|
||||
});
|
||||
|
||||
it("does not force a value if all settings are true", async () => {
|
||||
const controller = new RequiresSettingsController(["useCompactLayout", "useCustomFontSize"]);
|
||||
await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
|
||||
await SettingsStore.setValue("useCustomFontSize", null, SettingLevel.DEVICE, true);
|
||||
expect(controller.settingDisabled).toEqual(false);
|
||||
expect(controller.getValueOverride()).toEqual(null);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user