Rewrite tons of tests
This commit is contained in:
@@ -22,6 +22,7 @@ import { SettingsSubsection } from "../../shared/SettingsSubsection";
|
||||
import SettingsTab from "../SettingsTab";
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import { UrlPreviewSettings } from "../../../room_settings/UrlPreviewSettings";
|
||||
import { MediaPreviewAccountSettings } from "../user/MediaPreviewSetting";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
@@ -92,6 +93,7 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
|
||||
|
||||
<SettingsSection heading={_t("room_settings|general|other_section")}>
|
||||
{urlPreviewSettings}
|
||||
<MediaPreviewAccountSettings roomId={room.roomId} />
|
||||
{leaveSection}
|
||||
</SettingsSection>
|
||||
</SettingsTab>
|
||||
|
||||
@@ -15,19 +15,30 @@ import { useSettingValue } from "../../../../../hooks/useSettings";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
|
||||
export const MediaPreviewAccountSettings: React.FC = () => {
|
||||
const currentMediaPreview = useSettingValue("mediaPreviewConfig");
|
||||
export const MediaPreviewAccountSettings: React.FC<{ roomId?: string }> = ({ roomId }) => {
|
||||
const currentMediaPreview = useSettingValue("mediaPreviewConfig", roomId);
|
||||
|
||||
const changeSetting = useCallback(
|
||||
(newValue: MediaPreviewConfig) => {
|
||||
SettingsStore.setValue(
|
||||
"mediaPreviewConfig",
|
||||
roomId ?? null,
|
||||
roomId ? SettingLevel.ROOM_ACCOUNT : SettingLevel.ACCOUNT,
|
||||
newValue,
|
||||
);
|
||||
},
|
||||
[roomId],
|
||||
);
|
||||
|
||||
const avatarOnChange = useCallback(
|
||||
(c: boolean) => {
|
||||
const newValue = {
|
||||
changeSetting({
|
||||
...currentMediaPreview,
|
||||
// Switch is inverted. "Hide avatars..."
|
||||
invite_avatars: c ? MediaPreviewValue.Off : MediaPreviewValue.On,
|
||||
} satisfies MediaPreviewConfig;
|
||||
SettingsStore.setValue("mediaPreviewConfig", null, SettingLevel.ACCOUNT, newValue);
|
||||
});
|
||||
},
|
||||
[currentMediaPreview],
|
||||
[changeSetting, currentMediaPreview],
|
||||
);
|
||||
|
||||
const mediaPreviewOnChangeOff = useCallback<ChangeEventHandler<HTMLInputElement>>(
|
||||
@@ -35,12 +46,12 @@ export const MediaPreviewAccountSettings: React.FC = () => {
|
||||
if (!event.target.checked) {
|
||||
return;
|
||||
}
|
||||
SettingsStore.setValue("mediaPreviewConfig", null, SettingLevel.ACCOUNT, {
|
||||
changeSetting({
|
||||
...currentMediaPreview,
|
||||
media_previews: MediaPreviewValue.Off,
|
||||
} satisfies MediaPreviewConfig);
|
||||
});
|
||||
},
|
||||
[currentMediaPreview],
|
||||
[changeSetting, currentMediaPreview],
|
||||
);
|
||||
|
||||
const mediaPreviewOnChangePrivate = useCallback<ChangeEventHandler<HTMLInputElement>>(
|
||||
@@ -48,12 +59,12 @@ export const MediaPreviewAccountSettings: React.FC = () => {
|
||||
if (!event.target.checked) {
|
||||
return;
|
||||
}
|
||||
SettingsStore.setValue("mediaPreviewConfig", null, SettingLevel.ACCOUNT, {
|
||||
changeSetting({
|
||||
...currentMediaPreview,
|
||||
media_previews: MediaPreviewValue.Private,
|
||||
} satisfies MediaPreviewConfig);
|
||||
});
|
||||
},
|
||||
[currentMediaPreview],
|
||||
[changeSetting, currentMediaPreview],
|
||||
);
|
||||
|
||||
const mediaPreviewOnChangeOn = useCallback<ChangeEventHandler<HTMLInputElement>>(
|
||||
@@ -61,56 +72,71 @@ export const MediaPreviewAccountSettings: React.FC = () => {
|
||||
if (!event.target.checked) {
|
||||
return;
|
||||
}
|
||||
SettingsStore.setValue("mediaPreviewConfig", null, SettingLevel.ACCOUNT, {
|
||||
changeSetting({
|
||||
...currentMediaPreview,
|
||||
media_previews: MediaPreviewValue.On,
|
||||
} satisfies MediaPreviewConfig);
|
||||
});
|
||||
},
|
||||
[currentMediaPreview],
|
||||
[changeSetting, currentMediaPreview],
|
||||
);
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<LabelledToggleSwitch
|
||||
label={_t("settings|media_preview|hide_avatars")}
|
||||
value={currentMediaPreview.invite_avatars === MediaPreviewValue.Off}
|
||||
onChange={avatarOnChange}
|
||||
/>
|
||||
<Field role="radiogroup" name="media_previews">
|
||||
{!roomId && (
|
||||
<LabelledToggleSwitch
|
||||
label={_t("settings|media_preview|hide_avatars")}
|
||||
value={currentMediaPreview.invite_avatars === MediaPreviewValue.Off}
|
||||
onChange={avatarOnChange}
|
||||
/>
|
||||
)}
|
||||
{/* Explict label here because htmlFor is not supported for linking to radiogroups */}
|
||||
<Field
|
||||
id="mx_media_previews"
|
||||
role="radiogroup"
|
||||
name="media_previews"
|
||||
aria-label={_t("settings|media_preview|media_preview_label")}
|
||||
>
|
||||
<Label>{_t("settings|media_preview|media_preview_label")}</Label>
|
||||
<HelpMessage>{_t("settings|media_preview|media_preview_description")}</HelpMessage>
|
||||
<InlineField
|
||||
name="media_preview_off"
|
||||
control={
|
||||
<RadioInput
|
||||
id="mx_media_previews_off"
|
||||
checked={currentMediaPreview.media_previews === MediaPreviewValue.Off}
|
||||
onChange={mediaPreviewOnChangeOff}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label>{_t("settings|media_preview|hide_media")}</Label>
|
||||
</InlineField>
|
||||
<InlineField
|
||||
name="media_preview_private"
|
||||
control={
|
||||
<RadioInput
|
||||
checked={currentMediaPreview.media_previews === MediaPreviewValue.Private}
|
||||
onChange={mediaPreviewOnChangePrivate}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label>{_t("settings|media_preview|show_in_private")}</Label>
|
||||
<Label htmlFor="mx_media_previews_off">{_t("settings|media_preview|hide_media")}</Label>
|
||||
</InlineField>
|
||||
{!roomId && (
|
||||
<InlineField
|
||||
name="mx_media_previews_private"
|
||||
control={
|
||||
<RadioInput
|
||||
id="mx_media_previews_private"
|
||||
checked={currentMediaPreview.media_previews === MediaPreviewValue.Private}
|
||||
onChange={mediaPreviewOnChangePrivate}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label htmlFor="mx_media_previews_private">
|
||||
{_t("settings|media_preview|show_in_private")}
|
||||
</Label>
|
||||
</InlineField>
|
||||
)}
|
||||
<InlineField
|
||||
name="media_preview_on"
|
||||
control={
|
||||
<RadioInput
|
||||
id="mx_media_previews_on"
|
||||
checked={currentMediaPreview.media_previews === MediaPreviewValue.On}
|
||||
onChange={mediaPreviewOnChangeOn}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label>{_t("settings|media_preview|show_media")}</Label>
|
||||
<Label htmlFor="mx_media_previews_on">{_t("settings|media_preview|show_media")}</Label>
|
||||
</InlineField>
|
||||
</Field>
|
||||
</Root>
|
||||
|
||||
30
src/hooks/room/useJoinRule.ts
Normal file
30
src/hooks/room/useJoinRule.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2025 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 { useEffect, useState } from "react";
|
||||
import { EventType, type MatrixEvent, type Room, RoomStateEvent, type JoinRule } from "matrix-js-sdk/src/matrix";
|
||||
import { type Optional } from "matrix-events-sdk";
|
||||
|
||||
import { useTypedEventEmitter } from "../useEventEmitter";
|
||||
|
||||
/**
|
||||
* Helper to retrieve the join rules for given room
|
||||
* @param room
|
||||
* @returns the current join rule
|
||||
*/
|
||||
export function useJoinRule(room?: Room): Optional<JoinRule> {
|
||||
const [topic, setJoinRule] = useState(room?.getJoinRule());
|
||||
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, (ev: MatrixEvent) => {
|
||||
if (ev.getType() !== EventType.RoomJoinRules) return;
|
||||
setJoinRule(room?.getJoinRule());
|
||||
});
|
||||
useEffect(() => {
|
||||
setJoinRule(room?.getJoinRule());
|
||||
}, [room]);
|
||||
|
||||
return topic;
|
||||
}
|
||||
30
src/hooks/room/useRoomAvatar.ts
Normal file
30
src/hooks/room/useRoomAvatar.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2025 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 { useEffect, useState } from "react";
|
||||
import { EventType, type MatrixEvent, type Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { type Optional } from "matrix-events-sdk";
|
||||
|
||||
import { useTypedEventEmitter } from "../useEventEmitter";
|
||||
|
||||
/**
|
||||
* Helper to retrieve the avatar for given room
|
||||
* @param room
|
||||
* @returns the current avatar
|
||||
*/
|
||||
export function useRoomAvatar(room?: Room): Optional<string> {
|
||||
const [topic, setAvatar] = useState(room?.getMxcAvatarUrl());
|
||||
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, (ev: MatrixEvent) => {
|
||||
if (ev.getType() !== EventType.RoomAvatar) return;
|
||||
setAvatar(room?.getMxcAvatarUrl());
|
||||
});
|
||||
useEffect(() => {
|
||||
setAvatar(room?.getMxcAvatarUrl());
|
||||
}, [room]);
|
||||
|
||||
return topic;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { JoinRule } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
@@ -13,6 +13,7 @@ import { useSettingValue } from "./useSettings";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { useMatrixClientContext } from "../contexts/MatrixClientContext";
|
||||
import { MediaPreviewValue } from "../@types/media_preview";
|
||||
import { useJoinRule } from "./room/useJoinRule";
|
||||
|
||||
const PRIVATE_JOIN_RULES: JoinRule[] = [JoinRule.Invite, JoinRule.Knock, JoinRule.Restricted];
|
||||
|
||||
@@ -25,6 +26,7 @@ export function useMediaVisible(eventId: string, roomId: string): [boolean, (vis
|
||||
const mediaPreviewSetting = useSettingValue("mediaPreviewConfig", roomId);
|
||||
const client = useMatrixClientContext();
|
||||
const eventVisibility = useSettingValue("showMediaEventIds");
|
||||
const joinRule = useJoinRule(client.getRoom(roomId) ?? undefined);
|
||||
const setMediaVisible = useCallback(
|
||||
(visible: boolean) => {
|
||||
SettingsStore.setValue("showMediaEventIds", null, SettingLevel.DEVICE, {
|
||||
@@ -35,15 +37,7 @@ export function useMediaVisible(eventId: string, roomId: string): [boolean, (vis
|
||||
[eventId, eventVisibility],
|
||||
);
|
||||
|
||||
const roomIsPrivate = useMemo(() => {
|
||||
const joinRule = client?.getRoom(roomId)?.getJoinRule();
|
||||
if (PRIVATE_JOIN_RULES.includes(joinRule as JoinRule)) {
|
||||
return true;
|
||||
} else {
|
||||
// All other join rules, and unknown will default to hiding.
|
||||
return false;
|
||||
}
|
||||
}, [client, roomId]);
|
||||
const roomIsPrivate = joinRule ? PRIVATE_JOIN_RULES.includes(joinRule) : false;
|
||||
|
||||
const explicitEventVisiblity = eventVisibility[eventId];
|
||||
// Always prefer the explicit per-event user preference here.
|
||||
|
||||
@@ -374,10 +374,6 @@ export default class SettingsStore {
|
||||
roomId: string | null = null,
|
||||
excludeDefault = false,
|
||||
): Settings[S]["default"] | undefined {
|
||||
if (settingName === "mediaPreviewConfig") {
|
||||
console.log("GET VALUE", SETTINGS[settingName]);
|
||||
}
|
||||
|
||||
// Verify that the setting is actually a setting
|
||||
if (!SETTINGS[settingName]) {
|
||||
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
||||
|
||||
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { ClientEvent, type MatrixEvent, type MatrixClient, IContent } from "matrix-js-sdk/src/matrix";
|
||||
import { type IContent } from "matrix-js-sdk/src/matrix";
|
||||
import { type AccountDataEvents } from "matrix-js-sdk/src/types";
|
||||
|
||||
import {
|
||||
@@ -31,17 +31,23 @@ export default class MediaPreviewConfigController extends MatrixClientBackedCont
|
||||
const inviteAvatars: MediaPreviewValue = content.invite_avatars;
|
||||
const validValues = Object.values(MediaPreviewValue);
|
||||
return {
|
||||
invite_avatars: validValues.includes(inviteAvatars) ? inviteAvatars : MediaPreviewConfigController.default.invite_avatars,
|
||||
media_previews: validValues.includes(mediaPreviews) ? mediaPreviews : MediaPreviewConfigController.default.media_previews,
|
||||
invite_avatars: validValues.includes(inviteAvatars)
|
||||
? inviteAvatars
|
||||
: MediaPreviewConfigController.default.invite_avatars,
|
||||
media_previews: validValues.includes(mediaPreviews)
|
||||
? mediaPreviews
|
||||
: MediaPreviewConfigController.default.media_previews,
|
||||
};
|
||||
}
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
private getValue = (roomId?: string): MediaPreviewConfig | null => {
|
||||
const source = roomId ? this.client?.getRoom(roomId) : this.client;
|
||||
const value = source
|
||||
?.getAccountData(MEDIA_PREVIEW_ACCOUNT_DATA_TYPE)
|
||||
?.getContent<MediaPreviewConfig>();
|
||||
|
||||
const value = source?.getAccountData(MEDIA_PREVIEW_ACCOUNT_DATA_TYPE)?.getContent<MediaPreviewConfig>();
|
||||
|
||||
if (!value) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -49,20 +55,17 @@ export default class MediaPreviewConfigController extends MatrixClientBackedCont
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
protected async initMatrixClient(newClient: MatrixClient, oldClient?: MatrixClient): Promise<void> {
|
||||
|
||||
protected async initMatrixClient(): Promise<void> {
|
||||
// Unused
|
||||
}
|
||||
|
||||
public getValueOverride(_level: SettingLevel, roomId: string | null): MediaPreviewConfig {
|
||||
if (roomId) {
|
||||
// Use globals for any undefined setting
|
||||
return {
|
||||
...this.getRoomValue(roomId),
|
||||
...this.globalSetting,
|
||||
};
|
||||
const roomConfig = roomId && this.getValue(roomId);
|
||||
if (roomConfig) {
|
||||
return roomConfig;
|
||||
}
|
||||
return this.globalSetting;
|
||||
// If no room config, or global settings request then return global.
|
||||
return this.getValue() ?? MediaPreviewConfigController.default;
|
||||
}
|
||||
|
||||
public get settingDisabled(): false {
|
||||
@@ -79,9 +82,7 @@ export default class MediaPreviewConfigController extends MatrixClientBackedCont
|
||||
return false;
|
||||
}
|
||||
if (roomId) {
|
||||
await this.client.setRoomAccountData(roomId, MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, {
|
||||
value: newValue,
|
||||
});
|
||||
await this.client.setRoomAccountData(roomId, MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, newValue);
|
||||
return true;
|
||||
}
|
||||
await this.client.setAccountData(MEDIA_PREVIEW_ACCOUNT_DATA_TYPE, newValue);
|
||||
|
||||
Reference in New Issue
Block a user